Skip to content

Commit 15ab115

Browse files
committed
refactor: make currentLang honestly nullable (Language | null) instead of undefined cast
_currentLang signal now uses null as initial value instead of undefined-cast-to-Language. getCurrentLang() and currentLang signal return Language | null. Internal callers get null guards where needed.
1 parent 0320f80 commit 15ab115

3 files changed

Lines changed: 52 additions & 14 deletions

File tree

projects/ngx-translate/src/lib/translate.service.interface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export abstract class ITranslateService {
121121
* The current language as a reactive Signal.
122122
* Use `getCurrentLang()` for a non-reactive snapshot.
123123
*/
124-
public abstract readonly currentLang: Signal<Language>;
124+
public abstract readonly currentLang: Signal<Language | null>;
125125

126126
/**
127127
* The fallback language as a reactive Signal.
@@ -133,7 +133,7 @@ export abstract class ITranslateService {
133133
* Returns the current language as a plain value (non-reactive).
134134
* Use `currentLang` signal for reactive usage.
135135
*/
136-
public abstract getCurrentLang(): Language;
136+
public abstract getCurrentLang(): Language | null;
137137

138138
/**
139139
* Returns the loaded translations for the given language.

projects/ngx-translate/src/lib/translate.service.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class TranslateService implements ITranslateService {
7979

8080
protected _onLangChange = new Subject<LangChangeEvent>();
8181
protected _onFallbackLangChange = new Subject<FallbackLangChangeEvent>();
82-
protected _currentLang: WritableSignal<Language> = signal(undefined as unknown as Language);
82+
protected _currentLang: WritableSignal<Language | null> = signal(null);
8383
protected _fallbackLang: WritableSignal<Language | null> = signal(null);
8484
private _onTranslationRefresh: Observable<void> | null = null;
8585

@@ -311,8 +311,8 @@ export class TranslateService implements ITranslateService {
311311
this._onLangChange.next({ lang: lang, translations: this.store.getTranslations(lang) });
312312
}
313313

314-
public getCurrentLang(): Language {
315-
return this.isRoot ? this._currentLang() : (this.parent?.getCurrentLang() ?? (undefined as unknown as Language));
314+
public getCurrentLang(): Language | null {
315+
return this.isRoot ? this._currentLang() : (this.parent?.getCurrentLang() ?? null);
316316
}
317317

318318
protected loadAndCompileTranslations(
@@ -407,7 +407,10 @@ export class TranslateService implements ITranslateService {
407407
const fallbackLang = this.getFallbackLang();
408408

409409
// 1. Try own store (currentLang)
410-
let res = this.store.getTranslationValue(currentLang, key);
410+
let res: InterpolatableTranslation | undefined;
411+
if (currentLang) {
412+
res = this.store.getTranslationValue(currentLang, key);
413+
}
411414

412415
// 2. Try own store (fallbackLang) - null values also trigger fallback
413416
if (!isDefinedAndNotNull(res) && fallbackLang && fallbackLang !== currentLang) {
@@ -650,7 +653,7 @@ export class TranslateService implements ITranslateService {
650653
public set(
651654
key: string,
652655
translation: string | TranslationObject,
653-
lang: Language = this.getCurrentLang(),
656+
lang: Language = this.getCurrentLang()!,
654657
): void {
655658
this.store.setTranslations(
656659
lang,
@@ -721,7 +724,7 @@ export class TranslateService implements ITranslateService {
721724
* The current language as a reactive Signal.
722725
* Use `getCurrentLang()` for a non-reactive snapshot.
723726
*/
724-
get currentLang(): Signal<Language> {
727+
get currentLang(): Signal<Language | null> {
725728
return this.isRoot ? this._currentLang.asReadonly() : this.parent!.currentLang;
726729
}
727730

projects/ngx-translate/src/tests/translate.service.spec.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe("TranslateService (Delayed loading)", () => {
8585
it("currentLang should be the language, on which use() was called last - reverse order", fakeAsync(() => {
8686
const completionOrder: string[] = [];
8787

88-
expect(translate.getCurrentLang()).toBeUndefined();
88+
expect(translate.getCurrentLang()).toBeNull();
8989

9090
translate.use("delay-20").subscribe(() => completionOrder.push("delay-20"));
9191
expect(translate.getCurrentLang()).toEqual("delay-20");
@@ -1145,11 +1145,14 @@ describe("TranslateService (Error Conditions and Recovery)", () => {
11451145
});
11461146
});
11471147

1148-
it("should handle array with invalid elements", () => {
1149-
expect(() => {
1150-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1151-
translate.get([null, undefined, ""] as any);
1152-
}).toThrow();
1148+
it("should handle array with invalid elements gracefully", (done) => {
1149+
// With honest nullable currentLang, invalid keys no longer crash —
1150+
// they fall through to the missing translation handler which returns the key
1151+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1152+
translate.get([null, undefined, ""] as any).subscribe((res: Translation) => {
1153+
expect(res).toEqual({ null: null, undefined: undefined, "": "" });
1154+
done();
1155+
});
11531156
});
11541157

11551158
it("should handle deeply nested invalid parameters", () => {
@@ -1816,3 +1819,35 @@ describe("error logging", () => {
18161819
);
18171820
});
18181821
});
1822+
1823+
describe("TranslateService pre-initialization (no use() called)", () => {
1824+
let translate: TranslateService;
1825+
1826+
beforeEach(() => {
1827+
TestBed.configureTestingModule({
1828+
providers: [
1829+
provideTranslateService({ loader: provideTranslateLoader(FakeLoader) }),
1830+
],
1831+
});
1832+
translate = TestBed.inject(TranslateService);
1833+
});
1834+
1835+
it("getCurrentLang() should return null before use() is called", () => {
1836+
expect(translate.getCurrentLang()).toBeNull();
1837+
});
1838+
1839+
it("currentLang signal should be null before use() is called", () => {
1840+
expect(translate.currentLang()).toBeNull();
1841+
});
1842+
1843+
it("instant() should return the key before use() is called", () => {
1844+
expect(translate.instant("TEST")).toEqual("TEST");
1845+
});
1846+
1847+
it("get() should return the key before use() is called", (done) => {
1848+
translate.get("TEST").subscribe((result) => {
1849+
expect(result).toEqual("TEST");
1850+
done();
1851+
});
1852+
});
1853+
});

0 commit comments

Comments
 (0)