Comments (3)
Here's a simple script to localize (static) labels of UI from the outside, without such a feature actually being implemented in the UI:
// German translations
var translationMap = {
// Settings menu
'Video Quality': 'Videoqualität',
'Audio Quality': 'Audioqualität',
'Audio Track': 'Audiospur',
'Speed': 'Geschwindigkeit',
// Subtitle settings menu
'Subtitles': 'Untertitel',
'Font size': 'Größe',
'Font family': 'Schriftart',
'Font color': 'Farbe',
'Font opacity': 'Deckkraft',
'Character edge': 'Ränder',
'Background color': 'Hintergrundfarbe (Text)',
'Background opacity': 'Hintergrunddeckkraft (Text)',
'Window color': 'Hintergrundfarbe (Textbox)',
'Window opacity': 'Hintergrunddeckkraft (Textbox)',
'Back': 'Zurück',
'Reset': 'Zurücksetzen',
};
function translateElementContent(element) {
var currentText = element.innerHTML;
var translatedText = translationMap[currentText];
// If a translation exists, apply it
if (translatedText) {
element.innerHTML = translatedText;
}
}
function translateUi(uicontainer) {
// Gets the labels that are "Label" components
var labelComponents = Array.from(uicontainer.getElementsByClassName('bmpui-ui-label'));
// Gets labels that aren't distinct components but part of other components' DOM tree
var innerLabels = Array.from(uicontainer.getElementsByClassName('bmpui-label'));
// Concatenate all elements into a single array so we can iterate over all of them easily
var elements = labelComponents.concat(innerLabels);
// Translate the content of all elements
// A label is usually a <span> with text inside
elements.forEach(function(element) {
translateElementContent(element);
// Workaround for the "Subtitles" label
// This label unfortunately does not follow the label-pattern and carries
// its text inside a child <span>, so we check that too
var innerSpans = element.getElementsByTagName('span');
if (innerSpans.length > 0) {
translateElementContent(innerSpans[0]);
}
});
}
function waitForUiAndTranslateOnceLoaded() {
var uiContainers = player.getFigure().getElementsByClassName('bmpui-ui-uicontainer');
// When the UI is loaded, there are one or more UI containers
// (more if different UI variants are loaded, e.g. normal and ads UI)
if(uiContainers.length > 0) {
// UI container(s) found, translate them
Array.from(uiContainers).forEach(function(uiContainer) {
translateUi(uiContainer);
});
} else {
// When no UI containers are found, the UI is probably not loaded yet.
// There's no event that tells us when the UI is loaded, so we need to
// poll it's existence again later.
setTimeout(waitForUiAndTranslateOnceLoaded, 50);
}
}
// This is where we will apply the translation
player.setup(config).then(function() {
// The player is loaded, UI will soon be as well, so translate the UI
waitForUiAndTranslateOnceLoaded();
}, function(errorEvent) {
// Play setup failed, and there's no player UI yet to display the error in
console.log(errorEvent);
});
The problem with this approach is that it does not work for dynamically generated UI elements, which includes
- the "example subtitle" which is displayed while the subtitle settings are open and no actual subtitle is active
- the cast overlay which displays the connection status with a Cast receiver
- all selectboxes in settings and subtitle settings (e.g. font color)
We could extend the script above and listen to various player and/or DOM events to catch dynamic updates and retranslate labels by recalling waitForUiAndTranslateOnceLoaded
. That would also be necessary when UI variants are switched, e.g, when the ads UI or mobile UI are loaded.
from bitmovin-player-ui.
The UIConfig should take a map of string translations that allows customers to translate the UI whose texts are currently hardcoded in english.
It may look like this (providing an exemplary German translation):
var uiconfig = {
localization: {
'Video Quality': 'Videoqualität',
'Subtitles': 'Untertitel',
'Connecting to <strong>:castDeviceName</strong>...': 'Verbindung mit <strong>:castDeviceName</strong> wird hergestellt...',
...
},
};
A Component
that uses a string would ideally call a simple translate
function, that returns the translated string if a translation is available, or the input if no translation is available. The translation interface must also support placeholders. Examples:
// A simple translation
var translatedText = translate('Video Quality');
assert(translatedText === 'Videoqualität')
// A text without a translation
var translatedText2 = translate('foo');
assert(translatedText2 === 'foo');
// A translation with a placeholder
var translatedText3 = translate('Connecting to <strong>:castDeviceName</strong>...', { castDeviceName: 'Wohnzimmer' });
assert(translatedText3 === 'Verbindung mit <strong>Wohnzimmer</strong> wird hergestellt...');
The UIConfig
could be easily passed in as a property of the Player config, e.g. config.ui
and read from there. This would enable customers to also translate the default UI supplied by the player and save them the overhead of running their own UI (in case they don't need other customizations).
The approach outlined above would be simple but has one drawback: when e.g. calling label.setText('Video Quality')
, it would get translated because there is a translation for the input string available. It can be argued though that when someone calls setText
, he wants to set the supplied text and not have it automatically translated. The solution would be to use IDs for strings that should be translated instead, e.g. setText('settings.label.videoquality')
, and we'd also define a default map for the English labels. The advantage would be that all translatable strings with their IDs would be collected in a single file which makes it much easier for customers to write their own translations. Example:
var defaultLocalization: {
'settings.label.videoquality': 'Video Quality',
'settings.label.subtitles': 'Subtitles',
'castoverlay.status.connecting': 'Connecting to <strong>:castDeviceName</strong>...',
...
};
A customer can now easily copy this file and adjust it to his language. Added/changed/removed labels could easily be traced through the Git revision log.
While this seems to be easily implementable, the actual challenges that will have to be tackled are:
- The DOM elements of a
Component
are created before configuration throughcomponent.configure
happens, which is when theUIConfig
is passed in and they get access to the localization strings. Theconstructor
of aComponent
is called much earlier, at the time the UI structure is assembled. This means that all labels need to be updated once configuration happens. (unnecessary performance impact?) - Some components dynamically regenerate their DOM on certain events, but the
UIConfig
is only passed intoconfigure
. Storing the UIConfig into a field in every component seems not nice, also the translation functionality is not a feature of a component itself, so a nice pattern must be found to easily create atranslate
function (however that may look like in the end), without requiring boilerplate in the components. (A singleton does now work because we can have multiple different UI instances) - What happens if
label.setText
is called, and theonTextChanged
event fires? Does it convey the input text, or the translated text? Or doessetText
not translate texts on its own? (it probably should not; i.e. we don't want an unnecessary translation lookup on the playback time label when the time updates)
from bitmovin-player-ui.
Localization is available for quite some time now, closing this issue.
from bitmovin-player-ui.
Related Issues (20)
- seekbar on safari in livestream is visible
- SOURCE_HLS_STREAM_ERROR with Safari Native Player HOT 1
- How to make autoplay keep fullscreen mode?
- npm ERR! Build failed with error code: 1 while running npm install HOT 2
- Full screen button on player for Android App
- Bitmovin encoding distorts colors HOT 1
- Livestream stutters at higher playback speed HOT 1
- Overlapping Subtitle Queues break Subtitles in Safari HOT 1
- getMaxTimeShift value is negative non-zero for Safari HLS live streams HOT 1
- Feature Request: Use Document Picture-in-Picture Web API HOT 4
- pausedTimeshiftUpdater is not cleared HOT 2
- Accessing UIManager when replacing the built-in UI through location HOT 3
- I want to update the version of player HOT 1
- bleacherreport.com - impossible to go to exit fullscreen HOT 9
- when the UI autohides with the SettingsPanel open, one touch is need to make the visible SettingsPanel interactive again HOT 2
- seekbar markers overflowing player in some UI configurations HOT 3
- overlapping issue with seekbar thumbnails HOT 1
- error ReferenceError: self is not defined HOT 4
- <button> should not have superfluous aria attributes HOT 1
- listbox does not implement listbox a11y pattern HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bitmovin-player-ui.