Skip to content

Commit f6a664b

Browse files
authored
fix: gracefully treat locales removed by @react-aria/optimize-locales-plugin (#6066)
fix: gracefully treat locales removed by @react-aria/optimize-locales-plugin (#6066)
1 parent 1697a7f commit f6a664b

File tree

6 files changed

+124
-4
lines changed

6 files changed

+124
-4
lines changed

packages/@internationalized/message/src/MessageDictionary.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ export class MessageDictionary {
2626

2727
constructor(messages: LocalizedStrings, defaultLocale: string = 'en-US') {
2828
// Clone messages so we don't modify the original object.
29-
this.messages = {...messages};
29+
// Filter out entries with falsy values which may have been caused by applying optimize-locales-plugin.
30+
this.messages = Object.fromEntries(
31+
Object.entries(messages).filter(([, v]) => v)
32+
);
3033
this.defaultLocale = defaultLocale;
3134
}
3235

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {MessageDictionary} from '..';
14+
15+
describe('MessageDictionary', function () {
16+
17+
describe('getStringForLocale', function () {
18+
const messages = Object.freeze({
19+
'en-US': {
20+
'hello': 'Hello',
21+
'goodbye': 'Good bye'
22+
},
23+
'ja-JP': undefined,
24+
'es-ES': {
25+
'hello': 'Hola',
26+
'goodbye': 'Adiós'
27+
},
28+
'it-IT': {
29+
'hello': 'Ciao',
30+
'goodbye': 'Arrivederci'
31+
}
32+
});
33+
34+
it('throws when attempt to get a missing message key is made', function () {
35+
expect(() => {
36+
const messageDictionary = new MessageDictionary(messages);
37+
messageDictionary.getStringForLocale('missing.key', 'it-IT');
38+
}).toThrow(/Could not find intl message/);
39+
});
40+
41+
it('uses closest match by language when specified locale does not contain requested message key', function () {
42+
const messageDictionary = new MessageDictionary(messages);
43+
expect(messageDictionary.getStringForLocale('goodbye', 'es-MX')).toBe('Adiós');
44+
});
45+
46+
it('uses default locale when specified locale is missing and there is no closest match found', function () {
47+
const messageDictionary = new MessageDictionary(messages, 'it-IT');
48+
expect(messageDictionary.getStringForLocale('hello', 'hy-AM')).toBe('Ciao');
49+
});
50+
51+
it('uses default locale when specified locale key exists, but the value is falsy', function () {
52+
const messageDictionary = new MessageDictionary(messages, 'es-ES');
53+
expect(messageDictionary.getStringForLocale('hello', 'ja-JP')).toBe('Hola');
54+
});
55+
});
56+
});

packages/@internationalized/string/src/LocalizedStringDictionary.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ export class LocalizedStringDictionary<K extends string = string, T extends Loca
3030

3131
constructor(messages: LocalizedStrings<K, T>, defaultLocale: string = 'en-US') {
3232
// Clone messages so we don't modify the original object.
33-
this.strings = {...messages};
33+
// Filter out entries with falsy values which may have been caused by applying optimize-locales-plugin.
34+
this.strings = Object.fromEntries(
35+
Object.entries(messages).filter(([, v]) => v)
36+
);
3437
this.defaultLocale = defaultLocale;
3538
}
3639

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {LocalizedStringDictionary} from '..';
14+
15+
describe('LocalizedStringDictionary', function () {
16+
17+
describe('getStringForLocale', function () {
18+
const messages = Object.freeze({
19+
'en-US': {
20+
'hello': 'Hello',
21+
'goodbye': 'Good bye'
22+
},
23+
'ja-JP': undefined,
24+
'es-ES': {
25+
'hello': 'Hola',
26+
'goodbye': 'Adiós'
27+
},
28+
'it-IT': {
29+
'hello': 'Ciao',
30+
'goodbye': 'Arrivederci'
31+
}
32+
});
33+
34+
it('throws when attempt to get a missing message key is made', function () {
35+
expect(() => {
36+
const localizedStringDictionary = new LocalizedStringDictionary(messages);
37+
localizedStringDictionary.getStringForLocale('missing.key', 'it-IT');
38+
}).toThrow(/Could not find intl message/);
39+
});
40+
41+
it('uses closest match by language when specified locale does not contain requested message key', function () {
42+
const localizedStringDictionary = new LocalizedStringDictionary(messages);
43+
expect(localizedStringDictionary.getStringForLocale('goodbye', 'es-MX')).toBe('Adiós');
44+
});
45+
46+
it('uses default locale when specified locale is missing and there is no closest match found', function () {
47+
const localizedStringDictionary = new LocalizedStringDictionary(messages, 'it-IT');
48+
expect(localizedStringDictionary.getStringForLocale('hello', 'hy-AM')).toBe('Ciao');
49+
});
50+
51+
it('uses default locale when specified locale key exists, but the value is falsy', function () {
52+
const localizedStringDictionary = new LocalizedStringDictionary(messages, 'es-ES');
53+
expect(localizedStringDictionary.getStringForLocale('hello', 'ja-JP')).toBe('Hola');
54+
});
55+
});
56+
});
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export default {};
1+
const removedLocale = undefined;
2+
export default removedLocale;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export default {};
1+
const removedLocale = undefined;
2+
export default removedLocale;

0 commit comments

Comments
 (0)