Skip to content

Commit dd9123d

Browse files
Merge pull request #157 from THEOplayer/i18n
Add localization support
2 parents 7f77375 + 79d974a commit dd9123d

75 files changed

Lines changed: 1761 additions & 440 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/gold-pants-sleep.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@theoplayer/react-ui': minor
3+
'@theoplayer/web-ui': minor
4+
---
5+
6+
Added localization support. Use `addLocale()` to register a locale, and set the `lang` attribute on the UI to apply it.

docs/guides/custom-component.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
description: Build custom components that integrate with the player, and which you can use in your own custom UI.
33
sidebar_position: 2
4+
sidebar_custom_props: { 'icon': '🧩' }
45
---
56

67
# Making a custom component

docs/guides/custom-ui.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ title: Making a custom UI
44
permalink: /guides/custom-ui
55
description: Build a custom player from scratch, starting from a basic player and gradually adding more features.
66
sidebar_position: 1
7+
sidebar_custom_props: { 'icon': '🎨' }
78
---
89

910
Although the default UI was designed to support a variety of usage scenarios, you may still run into a case that it doesn't handle very well. Perhaps you want to move some buttons around, or add like and dislike buttons to the control bar, or perhaps integrate a text chat component inside your player. In these situations, you may want to build a custom player UI to create a truly unique experience for your viewers.

docs/guides/localization.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
layout: page
3+
title: Localization
4+
permalink: /guides/localization
5+
description: Localize your UI to support different languages.
6+
sidebar_position: 3
7+
sidebar_custom_props: { 'icon': '🌍' }
8+
---
9+
10+
The Open Video UI for Web can be localized to different languages,
11+
enabling you to reach audiences from different regions of the world.
12+
13+
Localization works by [registering one or more locales](#register-a-locale)
14+
and then [selecting one of the registered locales using the `lang` attribute](#select-a-language).
15+
16+
## Register a locale
17+
18+
A locale is a JavaScript object mapping translation IDs to translated messages.
19+
You can register a locale with the `addLocale` function:
20+
21+
```javascript title="French locale"
22+
import { addLocale } from '@theoplayer/web-ui';
23+
24+
addLocale('fr', {
25+
playAria: 'lire',
26+
pauseAria: 'pauser',
27+
replayAria: 'revoir'
28+
// ...
29+
});
30+
```
31+
32+
Some messages may need to be formatted with one or more values, based on the configuration or active state of the
33+
player. For those messages, the translation ID maps to a JavaScript function that takes those values as arguments
34+
and returns the formatted translation.
35+
36+
```javascript title="French locale (continued)"
37+
addLocale('fr', {
38+
// ...
39+
seekForwardAria: (offset) => `avancer de ${offset}`,
40+
seekBackwardAria: (offset) => `reculer de ${offset}`
41+
// ...
42+
});
43+
```
44+
45+
Refer to the [`Locale` interface definition](https://theoplayer.github.io/web-ui/api/interfaces/Locale.html)
46+
in the API references for the complete list of translatable messages.
47+
48+
## Select a language
49+
50+
The UI automatically selects the locale based on the `lang` attribute of the `<theoplayer-ui>`
51+
(or `<theoplayer-default-ui>`) element, or from the closest parent element with such an attribute.
52+
53+
The value of the `lang` attribute must exactly match the locale name as it was passed to `addLocale`.
54+
55+
```html title="Setting the language on the UI"
56+
<theoplayer-default-ui lang="fr" source='{"sources":{"src":"https://example.com/stream.m3u8"}}'></theoplayer-default-ui>
57+
```
58+
59+
You can also put the `lang` attribute on any parent element. For example, if the entire page is in French, you could put
60+
the attribute on the `<html>` element:
61+
62+
```html title="Setting the language on the HTML document"
63+
<!doctype html>
64+
<!-- highlight-next-line -->
65+
<html lang="fr">
66+
<head>
67+
<title>Ma page</title>
68+
</head>
69+
<body>
70+
<theoplayer-default-ui source='{"sources":{"src":"https://example.com/stream.m3u8"}}'></theoplayer-default-ui>
71+
</body>
72+
</html>
73+
```
74+
75+
## Remarks
76+
77+
### Update translations when upgrading Open Video UI
78+
79+
Newer versions of the Open Video UI for Web may add new messages that need to be translated.
80+
We follow [semantic versioning](https://semver.org/), so new messages can only be added in _major_ or _minor_ versions.
81+
82+
When using custom translations in your app, we recommend pinning the `@theoplayer/web-ui` dependency
83+
in your app's `package.json` to a specific minor version using a tilde constraint (`~`).
84+
Avoid using a caret constraint (`^`), since this may cause upgrading past your currently selected minor version.
85+
86+
```json title="package.json"
87+
{
88+
"dependencies": {
89+
"@theoplayer/web-ui": "~2.2.0"
90+
}
91+
}
92+
```
93+
94+
When you decide to upgrade Open Video UI to the latest version, make sure to also update your translations.
95+
Check the history for [`i18n/Locale.ts`](https://github.com/THEOplayer/web-ui/commits/main/src/i18n/Locale.ts)
96+
to see whether any messages were added or changed since the previous version.

docs/guides/migrating-to-v2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ layout: page
33
title: Migrating to Open Video UI for Web 2.x
44
permalink: /guides/migrating-to-v2
55
sidebar_position: 10
6+
sidebar_custom_props: { 'icon': '⬆️' }
67
---
78

89
This article will guide you through updating to Open Video UI for Web version 2 (from version 1),

docs/react/guides/localization.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
layout: page
3+
title: Localization
4+
slug: /react/guides/localization
5+
description: Localize your UI to support different languages.
6+
sidebar_position: 1
7+
sidebar_custom_props: { 'icon': '🌍' }
8+
---
9+
10+
The Open Video UI for React can be localized to different languages,
11+
enabling you to reach audiences from different regions of the world.
12+
13+
Localization works by [registering one or more locales](#register-a-locale)
14+
and then [selecting one of the registered locales using the `lang` prop](#select-a-language).
15+
16+
## Register a locale
17+
18+
A locale is a JavaScript object mapping translation IDs to translated messages.
19+
You can register a locale with the `addLocale` function:
20+
21+
```javascript title="French locale"
22+
import { addLocale } from '@theoplayer/react-ui';
23+
24+
addLocale('fr', {
25+
playAria: 'lire',
26+
pauseAria: 'pauser',
27+
replayAria: 'revoir'
28+
// ...
29+
});
30+
```
31+
32+
Some messages may need to be formatted with one or more values, based on the configuration or active state of the
33+
player. For those messages, the translation ID maps to a JavaScript function that takes those values as arguments
34+
and returns the formatted translation.
35+
36+
```javascript title="French locale (continued)"
37+
addLocale('fr', {
38+
// ...
39+
seekForwardAria: (offset) => `avancer de ${offset}`,
40+
seekBackwardAria: (offset) => `reculer de ${offset}`
41+
// ...
42+
});
43+
```
44+
45+
Refer to the [`Locale` interface definition](https://theoplayer.github.io/web-ui/react-api/interfaces/Locale.html)
46+
in the API references for the complete list of translatable messages.
47+
48+
## Select a language
49+
50+
The UI automatically selects the locale based on the `lang` prop of the `<UIContainer>` (or `<DefaultUI>`) component.
51+
52+
The value of the `lang` prop must exactly match the locale name as it was passed to `addLocale`.
53+
54+
```jsx title="Setting the language on the UI"
55+
<DefaultUI lang="fr" source={{ sources: { src: 'https://example.com/stream.m3u8' } }} />
56+
```
57+
58+
## Remarks
59+
60+
### Update translations when upgrading Open Video UI
61+
62+
Newer versions of the Open Video UI for React may add new messages that need to be translated.
63+
We follow [semantic versioning](https://semver.org/), so new messages can only be added in _major_ or _minor_ versions.
64+
65+
When using custom translations in your app, we recommend pinning the `@theoplayer/react-ui` dependency
66+
in your app's `package.json` to a specific minor version using a tilde constraint (`~`).
67+
Avoid using a caret constraint (`^`), since this may cause upgrading past your currently selected minor version.
68+
69+
```json title="package.json"
70+
{
71+
"dependencies": {
72+
"@theoplayer/react-ui": "~2.2.0"
73+
}
74+
}
75+
```
76+
77+
When you decide to upgrade Open Video UI to the latest version, make sure to also update your translations.
78+
Check the history for [`i18n/Locale.ts`](https://github.com/THEOplayer/web-ui/commits/main/src/i18n/Locale.ts)
79+
to see whether any messages were added or changed since the previous version.

docs/react/guides/migrating-to-v2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
layout: page
33
title: Migrating to Open Video UI for React 2.x
44
slug: /react/guides/migrating-to-v2
5+
sidebar_position: 10
6+
sidebar_custom_props: { 'icon': '⬆️' }
57
---
68

79
This article will guide you through updating to Open Video UI for React version 2 (from version 1),

examples/default-ui.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
<script async src="https://ga.jspm.io/npm:es-module-shims@2.6.2/dist/es-module-shims.js" crossorigin="anonymous"></script>
4747
<script type="module">
4848
import * as THEOplayerUI from '@theoplayer/web-ui';
49+
import './locale/nl.js';
50+
4951
self.THEOplayerUI = THEOplayerUI;
5052
</script>
5153
<!-- Legacy browsers -->
@@ -70,6 +72,15 @@ <h1>Default UI</h1>
7072
</select>
7173
</label>
7274
</div>
75+
<div>
76+
<label style="user-select: none">
77+
Language:
78+
<select id="language-select">
79+
<option value="en" selected>English</option>
80+
<option value="nl">Nederlands (Dutch)</option>
81+
</select>
82+
</label>
83+
</div>
7384
<script>
7485
const ui = document.querySelector('theoplayer-default-ui');
7586
const sources = {
@@ -112,6 +123,9 @@ <h1>Default UI</h1>
112123
document.querySelector('#source-select').addEventListener('change', (e) => {
113124
ui.source = sources[e.target.value];
114125
});
126+
document.querySelector('#language-select').addEventListener('change', (e) => {
127+
ui.lang = e.target.value;
128+
});
115129
</script>
116130
</body>
117131
</html>

examples/locale/nl.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { addLocale } from '@theoplayer/web-ui';
2+
3+
/**
4+
* Dutch (Nederlands)
5+
*/
6+
addLocale('nl', {
7+
playAria: 'afspelen',
8+
pauseAria: 'pauzeren',
9+
replayAria: 'opnieuw afspelen',
10+
muteAria: 'dempen',
11+
unmuteAria: 'dempen opheffen',
12+
volumeAria: 'volume',
13+
seekAria: 'zoeken',
14+
seekForwardAria: (offset) => `spring voorwaarts met ${offset}`,
15+
seekBackwardAria: (offset) => `spring achterwaarts met ${offset}`,
16+
live: 'LIVE',
17+
seekToLiveAria: 'spring naar live',
18+
fullscreenAria: 'volledig scherm',
19+
fullscreenExitAria: 'volledig scherm sluiten',
20+
airplayAria: 'start met afspelen op AirPlay',
21+
airplayConnectedAria: 'stop met afspelen op AirPlay',
22+
chromecastAria: 'start met afspelen op Chromecast',
23+
chromecastConnectedAria: 'stop met afspelen op Chromecast',
24+
chromecastHeading: 'Aan het afspelen op',
25+
chromecastDefaultReceiverName: 'Chromecast',
26+
timeOfTotalAria: (currentTime, totalDuration) => `${currentTime} van ${totalDuration}`,
27+
playbackTimeAria: 'afspeeltijd',
28+
unknownTimeAria: 'video niet ingeladen, onbekende tijd',
29+
adText: 'Advertentie',
30+
adBreakText: (currentAd, totalAds) => `Advertentie ${currentAd} van ${totalAds}`,
31+
adClickThroughText: 'Adverteerder bezoeken',
32+
adCountdownText: (remainingDuration) => `Video wordt hervat in ${remainingDuration}`,
33+
adSkipButtonText: 'Overslaan',
34+
adSkipCountdownText: (remainingDuration) => `Overslaan in ${remainingDuration}`,
35+
liveStreamLoading: 'Laden...',
36+
liveStreamOffline: `De livestream is nog niet gestart`,
37+
closeMenuAria: 'menu sluiten',
38+
openLanguageMenuAria: 'taalmenu openen',
39+
languageMenuHeading: 'Taal',
40+
audioMenuHeading: 'Audio',
41+
subtitleMenuHeading: 'Ondertitels',
42+
subtitleOff: 'Uit',
43+
openPlaybackRateMenuAria: 'afspeelsnelheidmenu openen',
44+
playbackRateMenuHeading: 'Afspeelsnelheid',
45+
formatPlaybackRate: (rate) => (rate === 1 ? 'Normaal' : `${rate}x`),
46+
openSettingsMenuAria: 'instellingenmenu openen',
47+
settingsMenuHeading: 'Instellingen',
48+
qualityMenuHeading: 'Kwaliteit',
49+
textTrackStyleMenuHeading: 'Opties voor ondertitels',
50+
textTrackStyleFontFamily: 'Lettertype',
51+
textTrackStyleFontColor: 'Tekstkleur',
52+
textTrackStyleFontOpacity: 'Ondoorzichtigheid van tekst',
53+
textTrackStyleFontSize: 'Lettergrootte',
54+
textTrackStyleBackgroundColor: 'Achtergrondkleur',
55+
textTrackStyleBackgroundOpacity: 'Ondoorzichtigheid van achtergrond',
56+
textTrackStyleWindowColor: 'Vensterkleur',
57+
textTrackStyleWindowOpacity: 'Ondoorzichtigheid van venster',
58+
textTrackStyleEdgeStyle: 'Randstijl van tekens',
59+
textTrackStyleDefaultLabel: 'Standaard',
60+
textTrackStyleCustomLabel: 'Aangepast',
61+
textTrackStyleResetLabel: 'Opnieuw instellen',
62+
fontFamilyLabels: {
63+
'Monospace Serif': 'Serif met gelijke tekenbreedte',
64+
'Proportional Serif': 'Proportionele serif',
65+
'Monospace Sans': 'Sans-serif met gelijke tekenbreedte',
66+
'Proportional Sans': 'Proportionele sans-serif'
67+
},
68+
colorLabels: {
69+
White: 'Wit',
70+
Yellow: 'Geel',
71+
Green: 'Groen',
72+
Cyan: 'Cyaan',
73+
Blue: 'Blauw',
74+
Magenta: 'Magenta',
75+
Red: 'Rood',
76+
Black: 'Zwart'
77+
},
78+
edgeStyleLabels: {
79+
none: 'Geen',
80+
dropshadow: 'Slagschaduw',
81+
raised: 'Verhoogd',
82+
depressed: 'Verlaagd',
83+
uniform: 'Uniform'
84+
},
85+
automaticQualityLabel: 'Automatisch',
86+
unknownQualityLabel: 'Onbekend',
87+
highQualityLabel: 'Hoge kwaliteit',
88+
lowQualityLabel: 'Lage kwaliteit',
89+
errorHeading: 'Er is een fout opgetreden',
90+
openBadNetworkModeMenuAria: 'menu voor slecht netwerk openen',
91+
formatRemainingDuration: (duration) => `${duration} resterend`
92+
});

0 commit comments

Comments
 (0)