shaka-player 4.4.1 → 4.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/deflake.yaml +8 -5
- package/.github/workflows/release-please.yaml +47 -7
- package/.github/workflows/shaka-bot-commands/command-test.sh +1 -1
- package/CHANGELOG.md +43 -0
- package/CONTRIBUTORS +1 -0
- package/demo/common/asset.js +5 -0
- package/demo/common/assets.js +20 -5
- package/demo/common/message_ids.js +1 -1
- package/demo/custom.js +5 -5
- package/demo/locales/en.json +2 -1
- package/demo/locales/source.json +5 -1
- package/demo/main.js +1 -1
- package/demo/search.js +3 -3
- package/dist/demo.compiled.debug.js +75 -73
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +75 -73
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +2 -2
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +38 -36
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +38 -36
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +533 -296
- package/dist/shaka-player.compiled.debug.d.ts +533 -296
- package/dist/shaka-player.compiled.debug.externs.js +308 -1
- package/dist/shaka-player.compiled.debug.js +280 -265
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +308 -1
- package/dist/shaka-player.compiled.js +1037 -1027
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +577 -340
- package/dist/shaka-player.ui.debug.d.ts +577 -340
- package/dist/shaka-player.ui.debug.externs.js +308 -1
- package/dist/shaka-player.ui.debug.js +341 -326
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +308 -1
- package/dist/shaka-player.ui.js +1149 -1141
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +8 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/offline.md +1 -1
- package/externs/shaka/text.js +7 -0
- package/lib/dash/dash_parser.js +4 -1
- package/lib/dash/segment_template.js +3 -0
- package/lib/hls/hls_parser.js +68 -28
- package/lib/media/media_source_engine.js +1 -1
- package/lib/media/segment_index.js +4 -4
- package/lib/media/segment_reference.js +6 -1
- package/lib/media/streaming_engine.js +14 -24
- package/lib/player.js +8 -57
- package/lib/text/cue.js +47 -47
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +8 -1
- package/lib/text/ttml_text_parser.js +10 -1
- package/lib/text/ui_text_displayer.js +49 -3
- package/lib/text/vtt_text_parser.js +19 -3
- package/lib/transmuxer/ts_transmuxer.js +44 -43
- package/lib/util/cmcd_manager.js +6 -1
- package/lib/util/error.js +16 -10
- package/lib/util/manifest_parser_utils.js +10 -4
- package/lib/util/platform.js +14 -0
- package/lib/util/player_configuration.js +2 -2
- package/lib/util/stream_utils.js +13 -11
- package/lib/util/ts_parser.js +73 -16
- package/package.json +2 -2
- package/test/dash/dash_parser_manifest_unit.js +5 -0
- package/test/dash/dash_parser_segment_base_unit.js +5 -0
- package/test/dash/dash_parser_segment_list_unit.js +21 -17
- package/test/dash/dash_parser_segment_template_unit.js +47 -0
- package/test/hls/hls_live_unit.js +33 -0
- package/test/hls/hls_parser_unit.js +2 -57
- package/test/test/util/dash_parser_util.js +16 -7
- package/test/text/text_engine_unit.js +4 -2
- package/test/text/ttml_text_parser_unit.js +3 -4
- package/test/text/web_vtt_layout_integration.js +0 -1
- package/test/transmuxer/transmuxer_integration.js +4 -4
- package/test/util/ts_parser_unit.js +1 -1
- package/ui/language_utils.js +0 -15
|
@@ -43,6 +43,11 @@ describe('DashParser SegmentBase', () => {
|
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
// Dash parser stop is synchronous.
|
|
48
|
+
parser.stop();
|
|
49
|
+
});
|
|
50
|
+
|
|
46
51
|
it('requests init data for WebM', async () => {
|
|
47
52
|
const source = [
|
|
48
53
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
@@ -351,24 +351,28 @@ describe('DashParser SegmentList', () => {
|
|
|
351
351
|
updateDuration: () => {},
|
|
352
352
|
newDrmInfo: (stream) => {},
|
|
353
353
|
};
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
354
|
+
try {
|
|
355
|
+
const manifest = await dashParser.start('dummy://foo', playerInterface);
|
|
356
|
+
const stream = manifest.variants[0].video;
|
|
357
|
+
await stream.createSegmentIndex();
|
|
358
|
+
goog.asserts.assert(stream.segmentIndex, 'Expected index to be created');
|
|
358
359
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
360
|
+
// Don't use Dash.testSegmentIndex since it uses SegmentIndex.find, which
|
|
361
|
+
// doesn't reproduce this issue. We want to use Array.from which uses
|
|
362
|
+
// the iterator.
|
|
363
|
+
const expected = [
|
|
364
|
+
ManifestParser.makeReference('s1.mp4', 0, 10, baseUri),
|
|
365
|
+
ManifestParser.makeReference('s2.mp4', 10, 20, baseUri),
|
|
366
|
+
ManifestParser.makeReference('s3.mp4', 20, 30, baseUri),
|
|
366
367
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
368
|
+
ManifestParser.makeReference('s3.mp4', 30, 40, baseUri),
|
|
369
|
+
ManifestParser.makeReference('s4.mp4', 40, 50, baseUri),
|
|
370
|
+
ManifestParser.makeReference('s5.mp4', 50, 60, baseUri),
|
|
371
|
+
];
|
|
372
|
+
const actual = Array.from(stream.segmentIndex);
|
|
373
|
+
expect(actual).toEqual(expected);
|
|
374
|
+
} finally {
|
|
375
|
+
dashParser.stop();
|
|
376
|
+
}
|
|
373
377
|
});
|
|
374
378
|
});
|
|
@@ -52,6 +52,11 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
52
52
|
};
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
// Dash parser stop is synchronous.
|
|
57
|
+
parser.stop();
|
|
58
|
+
});
|
|
59
|
+
|
|
55
60
|
shaka.test.Dash.makeTimelineTests(
|
|
56
61
|
'SegmentTemplate', 'media="s$Number$.mp4"', []);
|
|
57
62
|
|
|
@@ -670,6 +675,48 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
670
675
|
expect(pos).toBe(1);
|
|
671
676
|
});
|
|
672
677
|
|
|
678
|
+
it('finds correct position in multiperiod content', async () => {
|
|
679
|
+
const source = [
|
|
680
|
+
'<MPD type="static" availabilityStartTime="1970-01-01T00:00:00Z">',
|
|
681
|
+
' <Period duration="PT30S">',
|
|
682
|
+
' <AdaptationSet mimeType="video/mp4">',
|
|
683
|
+
' <Representation bandwidth="500">',
|
|
684
|
+
' <BaseURL>http://example.com</BaseURL>',
|
|
685
|
+
' <SegmentTemplate startNumber="0"',
|
|
686
|
+
' media="$Number$-$Time$-$Bandwidth$.mp4">',
|
|
687
|
+
' <SegmentTimeline>',
|
|
688
|
+
' <S t="0" d="5" r="6" />',
|
|
689
|
+
' </SegmentTimeline>',
|
|
690
|
+
' </SegmentTemplate>',
|
|
691
|
+
' </Representation>',
|
|
692
|
+
' </AdaptationSet>',
|
|
693
|
+
' </Period>',
|
|
694
|
+
' <Period duration="PT30S">',
|
|
695
|
+
' <AdaptationSet mimeType="video/mp4">',
|
|
696
|
+
' <Representation bandwidth="500">',
|
|
697
|
+
' <BaseURL>http://example.com</BaseURL>',
|
|
698
|
+
' <SegmentTemplate startNumber="6"',
|
|
699
|
+
' media="$Number$-$Time$-$Bandwidth$.mp4">',
|
|
700
|
+
' <SegmentTimeline>',
|
|
701
|
+
' <S t="0" d="5" r="6" />',
|
|
702
|
+
' </SegmentTimeline>',
|
|
703
|
+
' </SegmentTemplate>',
|
|
704
|
+
' </Representation>',
|
|
705
|
+
' </AdaptationSet>',
|
|
706
|
+
' </Period>',
|
|
707
|
+
'</MPD>',
|
|
708
|
+
].join('\n');
|
|
709
|
+
|
|
710
|
+
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
711
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
712
|
+
const stream = manifest.variants[0].video;
|
|
713
|
+
await stream.createSegmentIndex();
|
|
714
|
+
|
|
715
|
+
// simulate a seek into the second period
|
|
716
|
+
const segmentIterator = stream.segmentIndex.getIteratorForTime(42);
|
|
717
|
+
const ref = segmentIterator.next().value;
|
|
718
|
+
expect(ref.startTime).toBe(40);
|
|
719
|
+
});
|
|
673
720
|
|
|
674
721
|
it('returns null if time === last end time', async () => {
|
|
675
722
|
const info = makeTemplateInfo(makeRanges(0, 2.0, 2));
|
|
@@ -540,10 +540,43 @@ describe('HlsParser live', () => {
|
|
|
540
540
|
|
|
541
541
|
it('sets 3 times target duration as presentation delay if not configured',
|
|
542
542
|
async () => {
|
|
543
|
+
const media = [
|
|
544
|
+
'#EXTM3U\n',
|
|
545
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
546
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
547
|
+
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
548
|
+
'#EXTINF:2,\n',
|
|
549
|
+
'main.mp4\n',
|
|
550
|
+
'#EXTINF:2,\n',
|
|
551
|
+
'main.mp4\n',
|
|
552
|
+
'#EXTINF:2,\n',
|
|
553
|
+
'main.mp4\n',
|
|
554
|
+
'#EXTINF:2,\n',
|
|
555
|
+
'main.mp4\n',
|
|
556
|
+
'#EXTINF:2,\n',
|
|
557
|
+
'main.mp4\n',
|
|
558
|
+
'#EXTINF:2,\n',
|
|
559
|
+
'main.mp4\n',
|
|
560
|
+
].join('');
|
|
543
561
|
const manifest = await testInitialManifest(master, media);
|
|
544
562
|
expect(manifest.presentationTimeline.getDelay()).toBe(15);
|
|
545
563
|
});
|
|
546
564
|
|
|
565
|
+
it('sets 1 times target duration as presentation delay if there are not enough segments', async () => { // eslint-disable-line max-len
|
|
566
|
+
const media = [
|
|
567
|
+
'#EXTM3U\n',
|
|
568
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
569
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
570
|
+
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
571
|
+
'#EXTINF:2,\n',
|
|
572
|
+
'main.mp4\n',
|
|
573
|
+
'#EXTINF:2,\n',
|
|
574
|
+
'main.mp4\n',
|
|
575
|
+
].join('');
|
|
576
|
+
const manifest = await testInitialManifest(master, media);
|
|
577
|
+
expect(manifest.presentationTimeline.getDelay()).toBe(5);
|
|
578
|
+
});
|
|
579
|
+
|
|
547
580
|
it('sets presentation delay if defined', async () => {
|
|
548
581
|
const media = [
|
|
549
582
|
'#EXTM3U\n',
|
|
@@ -1136,63 +1136,6 @@ describe('HlsParser', () => {
|
|
|
1136
1136
|
expect(references[6].discontinuitySequence).toBe(3);
|
|
1137
1137
|
});
|
|
1138
1138
|
|
|
1139
|
-
it('sets reference timetampOffset based on discontinuity start time',
|
|
1140
|
-
async () => {
|
|
1141
|
-
const master = [
|
|
1142
|
-
'#EXTM3U\n',
|
|
1143
|
-
'#EXT-X-STREAM-INF:BANDWIDTH=2000000,CODECS="avc1"\n',
|
|
1144
|
-
'video\n',
|
|
1145
|
-
].join('');
|
|
1146
|
-
|
|
1147
|
-
const media = [
|
|
1148
|
-
'#EXTM3U\n',
|
|
1149
|
-
'#EXT-X-VERSION:3\n',
|
|
1150
|
-
'#EXT-X-TARGETDURATION:5\n',
|
|
1151
|
-
'#EXT-X-MEDIA-SEQUENCE:0\n',
|
|
1152
|
-
'#EXTINF:3,\n',
|
|
1153
|
-
'clip0-video-0.ts\n',
|
|
1154
|
-
'#EXTINF:1,\n',
|
|
1155
|
-
'clip0-video-1.ts\n',
|
|
1156
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1157
|
-
'#EXTINF:2,\n',
|
|
1158
|
-
'clip1-video-1.ts\n',
|
|
1159
|
-
'#EXTINF:3,\n',
|
|
1160
|
-
'clip1-video-2.ts\n',
|
|
1161
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1162
|
-
'#EXTINF:1,\n',
|
|
1163
|
-
'media-clip2-video-0.ts\n',
|
|
1164
|
-
'#EXTINF:1,\n',
|
|
1165
|
-
'media-clip2-video-1.ts\n',
|
|
1166
|
-
'#EXT-X-DISCONTINUITY\n',
|
|
1167
|
-
'#EXTINF:4,\n',
|
|
1168
|
-
'media-clip3-video-1.ts\n',
|
|
1169
|
-
'#EXT-X-ENDLIST\n',
|
|
1170
|
-
].join('');
|
|
1171
|
-
|
|
1172
|
-
fakeNetEngine
|
|
1173
|
-
.setResponseText('test:/master', master)
|
|
1174
|
-
.setResponseText('test:/video', media);
|
|
1175
|
-
|
|
1176
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
1177
|
-
await manifest.variants[0].video.createSegmentIndex();
|
|
1178
|
-
|
|
1179
|
-
const segmentIndex = manifest.variants[0].video.segmentIndex;
|
|
1180
|
-
const references = [];
|
|
1181
|
-
|
|
1182
|
-
for (let i = 0; i < 7; i++) {
|
|
1183
|
-
references.push(segmentIndex.get(i));
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
expect(references[0].timestampOffset).toBe(0);
|
|
1187
|
-
expect(references[1].timestampOffset).toBe(0);
|
|
1188
|
-
expect(references[2].timestampOffset).toBe(4);
|
|
1189
|
-
expect(references[3].timestampOffset).toBe(4);
|
|
1190
|
-
expect(references[4].timestampOffset).toBe(9);
|
|
1191
|
-
expect(references[5].timestampOffset).toBe(9);
|
|
1192
|
-
expect(references[6].timestampOffset).toBe(11);
|
|
1193
|
-
},
|
|
1194
|
-
);
|
|
1195
|
-
|
|
1196
1139
|
it('parses characteristics from audio tags', async () => {
|
|
1197
1140
|
const master = [
|
|
1198
1141
|
'#EXTM3U\n',
|
|
@@ -3182,8 +3125,10 @@ describe('HlsParser', () => {
|
|
|
3182
3125
|
|
|
3183
3126
|
const firstMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(0);
|
|
3184
3127
|
expect(firstMp4Segment.hlsAes128Key).toBeDefined();
|
|
3128
|
+
expect(firstMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();
|
|
3185
3129
|
const secondMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(1);
|
|
3186
3130
|
expect(secondMp4Segment.hlsAes128Key).toBeNull();
|
|
3131
|
+
expect(secondMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();
|
|
3187
3132
|
|
|
3188
3133
|
const tsAesEncryptionVideo = actual.variants[2].video;
|
|
3189
3134
|
await tsAesEncryptionVideo.createSegmentIndex();
|
|
@@ -46,11 +46,15 @@ shaka.test.Dash = class {
|
|
|
46
46
|
updateDuration: () => {},
|
|
47
47
|
newDrmInfo: (stream) => {},
|
|
48
48
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
try {
|
|
50
|
+
const manifest = await dashParser.start('dummy://foo', playerInterface);
|
|
51
|
+
const stream = manifest.variants[0].video;
|
|
52
|
+
await stream.createSegmentIndex();
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
shaka.test.ManifestParser.verifySegmentIndex(stream, references);
|
|
55
|
+
} finally {
|
|
56
|
+
dashParser.stop();
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/**
|
|
@@ -82,9 +86,14 @@ shaka.test.Dash = class {
|
|
|
82
86
|
updateDuration: () => {},
|
|
83
87
|
newDrmInfo: (stream) => {},
|
|
84
88
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const p = dashParser.start('dummy://foo', playerInterface);
|
|
92
|
+
await expectAsync(p).toBeRejectedWith(
|
|
93
|
+
shaka.test.Util.jasmineError(expectedError));
|
|
94
|
+
} finally {
|
|
95
|
+
dashParser.stop();
|
|
96
|
+
}
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
/**
|
|
@@ -49,7 +49,8 @@ describe('TextEngine', () => {
|
|
|
49
49
|
textEngine.initParser(
|
|
50
50
|
dummyMimeType,
|
|
51
51
|
/* sequenceMode= */ false,
|
|
52
|
-
/* segmentRelativeVttTiming= */ false
|
|
52
|
+
/* segmentRelativeVttTiming= */ false,
|
|
53
|
+
shaka.media.ManifestParser.UNKNOWN);
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
afterEach(() => {
|
|
@@ -298,7 +299,8 @@ describe('TextEngine', () => {
|
|
|
298
299
|
textEngine.initParser(
|
|
299
300
|
dummyMimeType,
|
|
300
301
|
/* sequenceMode= */ false,
|
|
301
|
-
/* segmentRelativeVttTiming= */ true
|
|
302
|
+
/* segmentRelativeVttTiming= */ true,
|
|
303
|
+
shaka.media.ManifestParser.UNKNOWN);
|
|
302
304
|
|
|
303
305
|
mockParseMedia.and.callFake((data, time) => {
|
|
304
306
|
return [
|
|
@@ -1866,9 +1866,8 @@ describe('TtmlTextParser', () => {
|
|
|
1866
1866
|
endTime: 60,
|
|
1867
1867
|
payload: '',
|
|
1868
1868
|
fontSize: '15px',
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
color: '',
|
|
1869
|
+
backgroundColor: 'transparent',
|
|
1870
|
+
color: 'blue',
|
|
1872
1871
|
displayAlign: Cue.displayAlign.CENTER,
|
|
1873
1872
|
textAlign: Cue.textAlign.CENTER,
|
|
1874
1873
|
|
|
@@ -1970,7 +1969,7 @@ describe('TtmlTextParser', () => {
|
|
|
1970
1969
|
startTime: 0,
|
|
1971
1970
|
endTime: 60,
|
|
1972
1971
|
payload: '',
|
|
1973
|
-
fontSize: '',
|
|
1972
|
+
fontSize: '16px',
|
|
1974
1973
|
nestedCues: [
|
|
1975
1974
|
{
|
|
1976
1975
|
startTime: 0,
|
|
@@ -213,7 +213,6 @@ filterDescribe('WebVTT layout', shaka.test.TextLayoutTests.supported, () => {
|
|
|
213
213
|
await helper.checkScreenshot('line-negative-2');
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
-
// FIXME: UI version is wrong: not positioned by line
|
|
217
216
|
it('line 0', async () => {
|
|
218
217
|
parseAndDisplay([
|
|
219
218
|
'WEBVTT\n',
|
|
@@ -36,8 +36,8 @@ describe('Transmuxer Player', () => {
|
|
|
36
36
|
// We must enable this, once it is resolved:
|
|
37
37
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1450313
|
|
38
38
|
const chromeVersion = shaka.util.Platform.chromeVersion();
|
|
39
|
-
if (shaka.util.Platform.isEdge() &&
|
|
40
|
-
chromeVersion && chromeVersion <=
|
|
39
|
+
if (shaka.util.Platform.isWindows() && shaka.util.Platform.isEdge() &&
|
|
40
|
+
chromeVersion && chromeVersion <= 118) {
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
return true;
|
|
@@ -57,8 +57,8 @@ describe('Transmuxer Player', () => {
|
|
|
57
57
|
// We must enable this, once it is resolved:
|
|
58
58
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1450313
|
|
59
59
|
const chromeVersion = shaka.util.Platform.chromeVersion();
|
|
60
|
-
if (shaka.util.Platform.isEdge() &&
|
|
61
|
-
chromeVersion && chromeVersion <=
|
|
60
|
+
if (shaka.util.Platform.isWindows() && shaka.util.Platform.isEdge() &&
|
|
61
|
+
chromeVersion && chromeVersion <= 118) {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
return true;
|
package/ui/language_utils.js
CHANGED
|
@@ -193,21 +193,6 @@ shaka.ui.LanguageUtils = class {
|
|
|
193
193
|
// Extract the base language from the locale as a fallback step.
|
|
194
194
|
const language = shaka.util.LanguageUtils.getBase(locale);
|
|
195
195
|
|
|
196
|
-
// If Intl.DisplayNames is supported we prefer it, because the list of
|
|
197
|
-
// languages is up to date.
|
|
198
|
-
if (window.Intl && 'DisplayNames' in Intl) {
|
|
199
|
-
try {
|
|
200
|
-
if (Intl.DisplayNames.supportedLocalesOf([locale]).length) {
|
|
201
|
-
const languageNames =
|
|
202
|
-
new Intl.DisplayNames([locale], {type: 'language'});
|
|
203
|
-
const language = languageNames.of(locale);
|
|
204
|
-
return language.charAt(0).toUpperCase() + language.slice(1);
|
|
205
|
-
}
|
|
206
|
-
} catch (e) {
|
|
207
|
-
// Ignore errors and try the fallback
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
196
|
// First try to resolve the full language name.
|
|
212
197
|
// If that fails, try the base.
|
|
213
198
|
// Finally, report "unknown".
|