Sitelet https://github.com/angular/angular/commit/6acea54
Skip to content

Commit 6acea54

Browse files
Artem HalasAndrewKushnir
authored andcommitted
fix(common): mark locale data arrays as readonly (#30397)
To discourage developers from mutating the arrays returned from the following methods, their return types have been marked as readonly. * `getLocaleDayPeriods()` * `getLocaleDayNames()` * `getLocaleMonthNames()` * `getLocaleEraNames()` Fixes #27003 BREAKING CHANGE: The locale data API has been marked as returning readonly arrays, rather than mutable arrays, since these arrays are shared across calls to the API. If you were mutating them (e.g. calling `sort()`, `push()`, `splice()`, etc) then your code will not longer compile. If you need to mutate the array, you should now take a copy (e.g. by calling `slice()`) and mutate the copy. PR Close #30397
1 parent 2d52c80 commit 6acea54

3 files changed

Lines changed: 101 additions & 10 deletions

File tree

‎goldens/public-api/common/common.d.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ export declare function getLocaleDateFormat(locale: string, width: FormatWidth):
6161

6262
export declare function getLocaleDateTimeFormat(locale: string, width: FormatWidth): string;
6363

64-
export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[];
64+
export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string>;
6565

66-
export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string];
66+
export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]>;
6767

6868
export declare function getLocaleDirection(locale: string): 'ltr' | 'rtl';
6969

70-
export declare function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string];
70+
export declare function getLocaleEraNames(locale: string, width: TranslationWidth): Readonly<[string, string]>;
7171

7272
export declare function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[];
7373

@@ -77,7 +77,7 @@ export declare function getLocaleFirstDayOfWeek(locale: string): WeekDay;
7777

7878
export declare function getLocaleId(locale: string): string;
7979

80-
export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[];
80+
export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string>;
8181

8282
export declare function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): string;
8383

‎packages/common/src/i18n/locale_data_api.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export function getLocaleId(locale: string): string {
233233
* @publicApi
234234
*/
235235
export function getLocaleDayPeriods(
236-
locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string] {
236+
locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]> {
237237
const data = ɵfindLocaleData(locale);
238238
const amPmData = <[string, string][][]>[
239239
data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone]
@@ -255,7 +255,7 @@ export function getLocaleDayPeriods(
255255
* @publicApi
256256
*/
257257
export function getLocaleDayNames(
258-
locale: string, formStyle: FormStyle, width: TranslationWidth): string[] {
258+
locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string> {
259259
const data = ɵfindLocaleData(locale);
260260
const daysData =
261261
<string[][][]>[data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone]];
@@ -276,7 +276,7 @@ export function getLocaleDayNames(
276276
* @publicApi
277277
*/
278278
export function getLocaleMonthNames(
279-
locale: string, formStyle: FormStyle, width: TranslationWidth): string[] {
279+
locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string> {
280280
const data = ɵfindLocaleData(locale);
281281
const monthsData =
282282
<string[][][]>[data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone]];
@@ -287,7 +287,6 @@ export function getLocaleMonthNames(
287287
/**
288288
* Retrieves Gregorian-calendar eras for the given locale.
289289
* @param locale A locale code for the locale format rules to use.
290-
* @param formStyle The required grammatical form.
291290
* @param width The required character width.
292291
293292
* @returns An array of localized era strings.
@@ -296,7 +295,8 @@ export function getLocaleMonthNames(
296295
*
297296
* @publicApi
298297
*/
299-
export function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string] {
298+
export function getLocaleEraNames(
299+
locale: string, width: TranslationWidth): Readonly<[string, string]> {
300300
const data = ɵfindLocaleData(locale);
301301
const erasData = <[string, string][]>data[ɵLocaleDataIndex.Eras];
302302
return getLastDefinedValue(erasData, width);

‎packages/common/test/i18n/locale_data_api_spec.ts‎

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import localeHe from '@angular/common/locales/he';
1313
import localeZh from '@angular/common/locales/zh';
1414
import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
1515

16-
import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';
16+
import {FormatWidth, FormStyle, getCurrencySymbol, getLocaleDateFormat, getLocaleDayNames, getLocaleDirection, getLocaleMonthNames, getNumberOfCurrencyDigits, TranslationWidth} from '../../src/i18n/locale_data_api';
1717

1818
{
1919
describe('locale data api', () => {
@@ -71,5 +71,96 @@ import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection,
7171
expect(getLocaleDirection('en')).toEqual('ltr');
7272
});
7373
});
74+
75+
describe('getLocaleDayNames', () => {
76+
it('should return english short list of days', () => {
77+
expect(
78+
getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short),
79+
)
80+
.toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']);
81+
});
82+
83+
it('should return french short list of days', () => {
84+
expect(
85+
getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Short),
86+
)
87+
.toEqual(['di', 'lu', 'ma', 'me', 'je', 've', 'sa']);
88+
});
89+
90+
it('should return english wide list of days', () => {
91+
expect(
92+
getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Wide),
93+
)
94+
.toEqual(
95+
['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']);
96+
});
97+
98+
it('should return french wide list of days', () => {
99+
expect(
100+
getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Wide),
101+
)
102+
.toEqual(['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']);
103+
});
104+
105+
it('should return the full short list of days after manipulations', () => {
106+
const days =
107+
Array.from(getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short));
108+
109+
days.splice(2);
110+
days.push('unexisting_day');
111+
112+
const newDays = getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short);
113+
114+
expect(newDays.length).toBe(7);
115+
116+
expect(newDays).toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']);
117+
});
118+
});
119+
120+
describe('getLocaleMonthNames', () => {
121+
it('should return english abbreviated list of month', () => {
122+
expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated))
123+
.toEqual([
124+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
125+
]);
126+
});
127+
128+
it('should return french abbreviated list of month', () => {
129+
expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Abbreviated))
130+
.toEqual([
131+
'janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
132+
'nov.', 'déc.'
133+
]);
134+
});
135+
136+
it('should return english wide list of month', () => {
137+
expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Wide)).toEqual([
138+
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
139+
'October', 'November', 'December'
140+
]);
141+
});
142+
143+
it('should return french wide list of month', () => {
144+
expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Wide)).toEqual([
145+
'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
146+
'octobre', 'novembre', 'décembre'
147+
]);
148+
});
149+
150+
it('should return the full abbreviated list of month after manipulations', () => {
151+
const month = Array.from(
152+
getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated));
153+
month.splice(2);
154+
month.push('unexisting_month');
155+
156+
const newMonth =
157+
getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated);
158+
159+
expect(newMonth.length).toBe(12);
160+
161+
expect(newMonth).toEqual(
162+
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']);
163+
});
164+
});
74165
});
75166
}

0 commit comments

Comments
 (0)