/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/localize/test/translate_spec.ts
137 строк
4 KB
arshiya tabasum
fix(localize): build runtime translations map with a null prototype
09 июл 2026, 19:56
09 июл 2026, 19:56
5de0ea5
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ // Ensure that `$localize` is loaded to the global scope. import '../init'; import {clearTranslations, loadTranslations} from '../index'; import {computeMsgId, MessageId, TargetMessage} from '../src/utils'; describe('$localize tag with translations', () => { describe('identities', () => { beforeEach(() => { loadTranslations( computeIds({ 'abc': 'abc', 'abc{$PH}': 'abc{$PH}', 'abc{$PH}def': 'abc{$PH}def', 'abc{$PH}def{$PH_1}': 'abc{$PH}def{$PH_1}', 'Hello, {$PH}!': 'Hello, {$PH}!', }), ); }); afterEach(() => { clearTranslations(); }); it('should render template literals as-is', () => { expect($localize`abc`).toEqual('abc'); expect($localize`abc${1 + 2 + 3}`).toEqual('abc6'); expect($localize`abc${1 + 2 + 3}def`).toEqual('abc6def'); expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('abc6def15'); const getName = () => 'World'; expect($localize`Hello, ${getName()}!`).toEqual('Hello, World!'); }); }); describe('to upper-case messageParts', () => { beforeEach(() => { loadTranslations( computeIds({ 'abc': 'ABC', 'abc{$PH}': 'ABC{$PH}', 'abc{$PH}def': 'ABC{$PH}DEF', 'abc{$PH}def{$PH_1}': 'ABC{$PH}DEF{$PH_1}', 'Hello, {$PH}!': 'HELLO, {$PH}!', }), ); }); afterEach(() => { clearTranslations(); }); it('should render template literals with messages upper-cased', () => { expect($localize`abc`).toEqual('ABC'); expect($localize`abc${1 + 2 + 3}`).toEqual('ABC6'); expect($localize`abc${1 + 2 + 3}def`).toEqual('ABC6DEF'); expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6}`).toEqual('ABC6DEF15'); const getName = () => 'World'; expect($localize`Hello, ${getName()}!`).toEqual('HELLO, World!'); }); }); describe('to reverse expressions', () => { beforeEach(() => { loadTranslations( computeIds({ 'abc{$PH}def{$PH_1} - Hello, {$PH_2}!': 'abc{$PH_2}def{$PH_1} - Hello, {$PH}!', }), ); }); afterEach(() => { clearTranslations(); }); it('should render template literals with expressions reversed', () => { const getName = () => 'World'; expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`).toEqual( 'abcWorlddef15 - Hello, 6!', ); }); }); describe('to remove expressions', () => { beforeEach(() => { loadTranslations( computeIds({ 'abc{$PH}def{$PH_1} - Hello, {$PH_2}!': 'abc{$PH} - Hello, {$PH_2}!', }), ); }); afterEach(() => { clearTranslations(); }); it('should render template literals with expressions removed', () => { const getName = () => 'World'; expect($localize`abc${1 + 2 + 3}def${4 + 5 + 6} - Hello, ${getName()}!`).toEqual( 'abc6 - Hello, World!', ); }); }); describe('with a `__proto__` message id', () => { afterEach(() => { clearTranslations(); }); it('should treat `__proto__` as an ordinary translation key', () => { loadTranslations({ ['__proto__' as MessageId]: 'pwned' as TargetMessage, [computeMsgId('abc', '')]: 'abc' as TargetMessage, }); const store = ($localize as unknown as {TRANSLATIONS: Record<string, unknown>}).TRANSLATIONS; // The malicious id must be stored as an own key rather than assigned through the inherited // `__proto__` setter, which would reparent the map (and drop the entry). expect(Object.getPrototypeOf(store)).toBe(null); expect(Object.prototype.hasOwnProperty.call(store, '__proto__')).toBe(true); // A translation loaded alongside it still resolves. expect($localize`abc`).toEqual('abc'); }); }); }); function computeIds( translations: Record<MessageId, TargetMessage>, ): Record<MessageId, TargetMessage> { const processed: Record<MessageId, TargetMessage> = {}; Object.keys(translations).forEach( (key) => (processed[computeMsgId(key, '')] = translations[key]), ); return processed; }