Merge pull request #1056 from jitsi/translations-update

Translations update
This commit is contained in:
hristoterezov 2016-10-25 18:11:15 -05:00 committed by GitHub
commit 221f6d1d68
24 changed files with 237 additions and 342 deletions

View File

@ -195,11 +195,7 @@ function maybeRedirectToWelcomePage(showThankYou) {
if (showThankYou) { if (showThankYou) {
APP.UI.messageHandler.openMessageDialog( APP.UI.messageHandler.openMessageDialog(
null, null, null, null, "dialog.thankYou", {appName:interfaceConfig.APP_NAME});
APP.translation.translateString(
"dialog.thankYou", {appName:interfaceConfig.APP_NAME}
)
);
} }
if (!config.enableWelcomePage) { if (!config.enableWelcomePage) {
@ -1045,21 +1041,20 @@ export default {
// TrackErrors.GENERAL // TrackErrors.GENERAL
// and any other // and any other
let dialogTxt; let dialogTxt;
let dialogTitle; let dialogTitleKey;
if (err.name === TrackErrors.PERMISSION_DENIED) { if (err.name === TrackErrors.PERMISSION_DENIED) {
dialogTxt = APP.translation.generateTranslationHTML( dialogTxt = APP.translation.generateTranslationHTML(
"dialog.screenSharingPermissionDeniedError"); "dialog.screenSharingPermissionDeniedError");
dialogTitle = APP.translation.generateTranslationHTML( dialogTitleKey = "dialog.error";
"dialog.error");
} else { } else {
dialogTxt = APP.translation.generateTranslationHTML( dialogTxt = APP.translation.generateTranslationHTML(
"dialog.failtoinstall"); "dialog.failtoinstall");
dialogTitle = APP.translation.generateTranslationHTML( dialogTitleKey = "dialog.permissionDenied";
"dialog.permissionDenied");
} }
APP.UI.messageHandler.openDialog(dialogTitle, dialogTxt, false); APP.UI.messageHandler.openDialog(
dialogTitleKey, dialogTxt, false);
}); });
} else { } else {
createLocalTracks({ devices: ['video'] }).then( createLocalTracks({ devices: ['video'] }).then(

View File

@ -105,7 +105,7 @@
<div id="subject" class="hide"></div> <div id="subject" class="hide"></div>
<div id="extendedToolbar" class="toolbar"> <div id="extendedToolbar" class="toolbar">
<a class="button" id="toolbar_button_profile" data-container="body" data-placement="right" data-i18n="[content]toolbar.profile" content="Edit your profile"> <a class="button" id="toolbar_button_profile" data-container="body" data-placement="right" data-i18n="[content]toolbar.profile">
<img id="avatar" src="images/avatar2.png"/> <img id="avatar" src="images/avatar2.png"/>
</a> </a>
<a class="button icon-contactList" id="toolbar_contact_list" shortcut="contactlistpopover"> <a class="button icon-contactList" id="toolbar_contact_list" shortcut="contactlistpopover">
@ -138,11 +138,11 @@
<div class="title" data-i18n="profile.title"></div> <div class="title" data-i18n="profile.title"></div>
<div class="sideToolbarBlock first"> <div class="sideToolbarBlock first">
<label class="first" data-i18n="profile.setDisplayNameLabel"></label> <label class="first" data-i18n="profile.setDisplayNameLabel"></label>
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name"> <input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name">
</div> </div>
<div class="sideToolbarBlock"> <div class="sideToolbarBlock">
<label data-i18n="profile.setEmailLabel"></label> <label data-i18n="profile.setEmailLabel"></label>
<input type="text" id="setEmail" placeholder="Enter e-mail"> <input data-i18n="[placeholder]profile.setEmailInput" type="text" id="setEmail">
</div> </div>
<div class="sideToolbarBlock auth_container" id="authenticationContainer"> <div class="sideToolbarBlock auth_container" id="authenticationContainer">
<p data-i18n="toolbar.authenticate"></p> <p data-i18n="toolbar.authenticate"></p>
@ -175,7 +175,7 @@
</div> </div>
</div> </div>
<div id="contacts_container" class="sideToolbarContainer__inner"> <div id="contacts_container" class="sideToolbarContainer__inner">
<div class="title" data-i18n="contactlist"></div> <div class="title" data-i18n="contactlist" data-i18n-options='{"pcount":"1"}'></div>
<ul id="contacts"></ul> <ul id="contacts"></ul>
</div> </div>
<div id="settings_container" class="sideToolbarContainer__inner"> <div id="settings_container" class="sideToolbarContainer__inner">

View File

@ -1,5 +1,5 @@
{ {
"contactlist": "Participants", "contactlist": "Participants (__pcount__)",
"addParticipants": "Add Participants", "addParticipants": "Add Participants",
"roomLocked": "Callers must enter a password", "roomLocked": "Callers must enter a password",
"roomUnlocked": "Anyone with the link can join", "roomUnlocked": "Anyone with the link can join",
@ -99,6 +99,7 @@
"cameraDisabled": "Camera is not available", "cameraDisabled": "Camera is not available",
"micDisabled": "Microphone is not available", "micDisabled": "Microphone is not available",
"filmstrip": "Show / hide videos", "filmstrip": "Show / hide videos",
"profile": "Edit your profile",
"raiseHand": "Raise hand to speak" "raiseHand": "Raise hand to speak"
}, },
"bottomtoolbar": { "bottomtoolbar": {
@ -135,7 +136,8 @@
"profile": { "profile": {
"title": "Profile", "title": "Profile",
"setDisplayNameLabel": "Set your display name", "setDisplayNameLabel": "Set your display name",
"setEmailLabel": "Set your gravatar email" "setEmailLabel": "Set your gravatar email",
"setEmailInput": "Enter e-mail"
}, },
"videothumbnail": "videothumbnail":
{ {
@ -342,7 +344,7 @@
"ATTACHED": "Attached", "ATTACHED": "Attached",
"FETCH_SESSION_ID": "Obtaining session-id...", "FETCH_SESSION_ID": "Obtaining session-id...",
"GOT_SESSION_ID": "Obtaining session-id... Done", "GOT_SESSION_ID": "Obtaining session-id... Done",
"GET_SESSION_ID_ERROR": "Get session-id error: ", "GET_SESSION_ID_ERROR": "Get session-id error: __code__",
"USER_CONNECTION_INTERRUPTED": "__displayName__ is having connectivity issues..." "USER_CONNECTION_INTERRUPTED": "__displayName__ is having connectivity issues..."
}, },
"recording": "recording":

View File

@ -1,20 +1,20 @@
Jitsi Meet Translation Jitsi Meet Translation
========================== ==========================
Jitsi Meet uses [i18next](http://i18next.com) library for translation. Jitsi Meet uses [i18next](http://i18next.com) library for translation.
i18next uses separate json files for each language. i18next uses separate json files for each language.
Translating Jitsi Meet Translating Jitsi Meet
====================== ======================
The translation of Jitsi Meet is integrated with Pootle. You can translate Jitsi Meet via our Pootle user interface on The translation of Jitsi Meet is integrated with Pootle. You can translate Jitsi Meet via our Pootle user interface on
[http://translate.jitsi.org](http://translate.jitsi.org). [http://translate.jitsi.org](http://translate.jitsi.org).
**WARNING: Please don't create or edit manually the language files! Please use our Pootle user interface!** **WARNING: Please don't create or edit manually the language files! Please use our Pootle user interface!**
Development Development
=========== ===========
If you want to add new functionality for Jitsi Meet and you have texts that need to be translated please use our translation module. If you want to add new functionality for Jitsi Meet and you have texts that need to be translated please use our translation module.
It is located in modules/translation. You must add key and value in main.json file in English for each translatable text. It is located in modules/translation. You must add key and value in main.json file in English for each translatable text.
Than you can use the key to get the translated text for the current language. Than you can use the key to get the translated text for the current language.
**WARNING: Please don't change the other language files except main.json! They must be updated and translated via our Pootle user interface!** **WARNING: Please don't change the other language files except main.json! They must be updated and translated via our Pootle user interface!**
@ -36,21 +36,19 @@ You can add translatable text in the HTML:
``` ```
APP.translation.generateTranslationHTML("dialog.OK") // returns <span data-i18n="dialog.OK">OK</span> APP.translation.generateTranslationHTML("dialog.OK") // returns <span data-i18n="dialog.OK">OK</span>
``` ```
The value in the options parameter will be added in data-i18n-options attribute of the element. The value in the options parameter will be added in data-i18n-options attribute of the element.
**Note:** If you dynamically add HTML elements don't forget to call APP.translation.translateElement(jquery_selector) to translate the text initially. **Note:** If you dynamically add HTML elements don't forget to call APP.translation.translateElement(jquery_selector) to translate the text initially.
* **via Javascript string** - call APP.translation.translateString(key, options). You can use that method to get the translated string in Javascript and then attach it in the HTML.
``` ```
APP.translation.translateString("dialog.OK") // returns the value for the key of the current language file. "OK" for example. APP.translation.translateString("dialog.OK") // returns the value for the key of the current language file. "OK" for example.
``` ```
For the available values of ``options`` parameter for the above methods of translation module see [i18next documentation](http://i18next.com/pages/doc_features). For the available values of ``options`` parameter for the above methods of translation module see [i18next documentation](http://i18next.com/pages/doc_features).
**Note:** It is useful to add attributes in the HTML for persistent HTML elements because when the language is changed the text will be automatically translated. **Note:** It is useful to add attributes in the HTML for persistent HTML elements because when the language is changed the text will be automatically translated.
Otherwise you should call ``APP.translation.translateString`` and manually change the text every time the language is changed.

View File

@ -75,17 +75,13 @@ JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.NO_DATA_FROM_SOURCE]
*/ */
function promptDisplayName() { function promptDisplayName() {
let labelKey = 'dialog.enterDisplayName'; let labelKey = 'dialog.enterDisplayName';
let labelStr = APP.translation.translateString(labelKey);
let titleStr
= APP.translation.translateString('dialog.displayNameRequired');
let defaultNickMsg = APP.translation.translateString("defaultNickname");
let message = ( let message = (
`<div class="input-control"> `<div class="input-control">
<label class="input-control__label">${labelStr}</label> <label data-i18n="${labelKey}" class="input-control__label"></label>
<input name="displayName" type="text" <input name="displayName" type="text"
data-i18n="[placeholder]defaultNickname" data-i18n="[placeholder]defaultNickname"
class="input-control__input" class="input-control__input"
placeholder="${defaultNickMsg}" autofocus> autofocus>
</div>` </div>`
); );
@ -94,7 +90,7 @@ function promptDisplayName() {
let buttons = {Ok:true}; let buttons = {Ok:true};
let dialog = messageHandler.openDialog( let dialog = messageHandler.openDialog(
titleStr, 'dialog.displayNameRequired',
message, message,
true, true,
buttons, buttons,
@ -167,11 +163,10 @@ UI.notifyGracefulShutdown = function () {
* Notify user that reservation error happened. * Notify user that reservation error happened.
*/ */
UI.notifyReservationError = function (code, msg) { UI.notifyReservationError = function (code, msg) {
var title = APP.translation.generateTranslationHTML(
"dialog.reservationError");
var message = APP.translation.generateTranslationHTML( var message = APP.translation.generateTranslationHTML(
"dialog.reservationErrorMsg", {code: code, msg: msg}); "dialog.reservationErrorMsg", {code: code, msg: msg});
messageHandler.openDialog(title, message, true, {}, () => false); messageHandler.openDialog(
"dialog.reservationError", message, true, {}, () => false);
}; };
/** /**
@ -190,9 +185,8 @@ UI.notifyKicked = function () {
UI.notifyConferenceDestroyed = function (reason) { UI.notifyConferenceDestroyed = function (reason) {
//FIXME: use Session Terminated from translation, but //FIXME: use Session Terminated from translation, but
// 'reason' text comes from XMPP packet and is not translated // 'reason' text comes from XMPP packet and is not translated
const title messageHandler.openDialog(
= APP.translation.generateTranslationHTML("dialog.sessTerminated"); "dialog.sessTerminated", reason, true, {}, () => false);
messageHandler.openDialog(title, reason, true, {}, () => false);
}; };
/** /**
@ -750,8 +744,6 @@ UI.connectionIndicatorShowMore = function(id) {
// FIXME check if someone user this // FIXME check if someone user this
UI.showLoginPopup = function(callback) { UI.showLoginPopup = function(callback) {
console.log('password is required'); console.log('password is required');
let titleKey = "dialog.passwordRequired";
let titleString = APP.translation.translateString(titleKey);
let message = ( let message = (
`<input name="username" type="text" `<input name="username" type="text"
@ -770,8 +762,7 @@ UI.showLoginPopup = function(callback) {
}; };
messageHandler.openTwoButtonDialog({ messageHandler.openTwoButtonDialog({
titleKey, titleKey : "dialog.passwordRequired",
titleString,
msgString: message, msgString: message,
leftButtonKey: 'dialog.Ok', leftButtonKey: 'dialog.Ok',
submitFunction, submitFunction,
@ -904,9 +895,6 @@ UI.setUserAvatarUrl = function (id, url) {
* @param {string} stropheErrorMsg raw Strophe error message * @param {string} stropheErrorMsg raw Strophe error message
*/ */
UI.notifyConnectionFailed = function (stropheErrorMsg) { UI.notifyConnectionFailed = function (stropheErrorMsg) {
var title = APP.translation.generateTranslationHTML(
"dialog.error");
var message; var message;
if (stropheErrorMsg) { if (stropheErrorMsg) {
message = APP.translation.generateTranslationHTML( message = APP.translation.generateTranslationHTML(
@ -916,7 +904,7 @@ UI.notifyConnectionFailed = function (stropheErrorMsg) {
"dialog.connectError"); "dialog.connectError");
} }
messageHandler.openDialog(title, message, true, {}, () => false); messageHandler.openDialog("dialog.error", message, true, {}, () => false);
}; };
@ -924,13 +912,10 @@ UI.notifyConnectionFailed = function (stropheErrorMsg) {
* Notify user that maximum users limit has been reached. * Notify user that maximum users limit has been reached.
*/ */
UI.notifyMaxUsersLimitReached = function () { UI.notifyMaxUsersLimitReached = function () {
var title = APP.translation.generateTranslationHTML(
"dialog.error");
var message = APP.translation.generateTranslationHTML( var message = APP.translation.generateTranslationHTML(
"dialog.maxUsersLimitReached"); "dialog.maxUsersLimitReached");
messageHandler.openDialog(title, message, true, {}, () => false); messageHandler.openDialog("dialog.error", message, true, {}, () => false);
}; };
/** /**
@ -1046,7 +1031,7 @@ UI.updateDTMFSupport = function (isDTMFSupported) {
* @returns {Promise} Resolved with value - false if the dialog is enabled and * @returns {Promise} Resolved with value - false if the dialog is enabled and
* resolved with true if the dialog is disabled or the feedback was already * resolved with true if the dialog is disabled or the feedback was already
* submitted. Rejected if another dialog is already displayed. This values are * submitted. Rejected if another dialog is already displayed. This values are
* used to display or not display the thank you dialog from * used to display or not display the thank you dialog from
* conference.maybeRedirectToWelcomePage method. * conference.maybeRedirectToWelcomePage method.
*/ */
UI.requestFeedbackOnHangup = function () { UI.requestFeedbackOnHangup = function () {
@ -1191,10 +1176,8 @@ UI.getLargeVideo = function () {
UI.showExtensionRequiredDialog = function (url) { UI.showExtensionRequiredDialog = function (url) {
messageHandler.openMessageDialog( messageHandler.openMessageDialog(
"dialog.extensionRequired", "dialog.extensionRequired",
null, "dialog.firefoxExtensionPrompt",
null, {url: url});
APP.translation.generateTranslationHTML(
"dialog.firefoxExtensionPrompt", {url: url}));
}; };
/** /**
@ -1216,9 +1199,7 @@ UI.showExtensionExternalInstallationDialog = function (url) {
messageHandler.openTwoButtonDialog({ messageHandler.openTwoButtonDialog({
titleKey: 'dialog.externalInstallationTitle', titleKey: 'dialog.externalInstallationTitle',
titleString: '',
msgKey: 'dialog.externalInstallationMsg', msgKey: 'dialog.externalInstallationMsg',
msgString: '',
leftButtonKey: 'dialog.goToStore', leftButtonKey: 'dialog.goToStore',
submitFunction, submitFunction,
loadedFunction: $.noop, loadedFunction: $.noop,
@ -1263,8 +1244,6 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
} }
} }
let title = getTitleKey();
let titleMsg = `<span data-i18n="${title}"></span>`;
let cameraJitsiTrackErrorMsg = cameraError let cameraJitsiTrackErrorMsg = cameraError
? JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[cameraError.name] ? JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[cameraError.name]
: undefined; : undefined;
@ -1318,7 +1297,7 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
deviceErrorDialog && deviceErrorDialog.close(); deviceErrorDialog && deviceErrorDialog.close();
deviceErrorDialog = messageHandler.openDialog( deviceErrorDialog = messageHandler.openDialog(
titleMsg, getTitleKey(),
message, message,
false, false,
{Ok: true}, {Ok: true},
@ -1342,8 +1321,6 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
} }
); );
APP.translation.translateElement($(".jqibox"));
function getTitleKey() { function getTitleKey() {
let title = "dialog.error"; let title = "dialog.error";
@ -1369,9 +1346,7 @@ UI.showTrackNotWorkingDialog = function (stream) {
messageHandler.openMessageDialog( messageHandler.openMessageDialog(
"dialog.error", "dialog.error",
stream.isAudioTrack()? "dialog.micNotSendingData" : stream.isAudioTrack()? "dialog.micNotSendingData" :
"dialog.cameraNotSendingData", "dialog.cameraNotSendingData");
null,
null);
}; };
UI.updateDevicesAvailability = function (id, devices) { UI.updateDevicesAvailability = function (id, devices) {
@ -1483,12 +1458,11 @@ UI.hideUserMediaPermissionsGuidanceOverlay = function () {
*/ */
UI.toggleKeyboardShortcutsPanel = function() { UI.toggleKeyboardShortcutsPanel = function() {
if (!messageHandler.isDialogOpened()) { if (!messageHandler.isDialogOpened()) {
let titleKey = 'keyboardShortcuts.keyboardShortcuts';
let title = APP.translation.translateString(titleKey);
let msg = $('#keyboard-shortcuts').html(); let msg = $('#keyboard-shortcuts').html();
let buttons = { Close: true }; let buttons = { Close: true };
messageHandler.openDialog(title, msg, true, buttons); messageHandler.openDialog(
'keyboardShortcuts.keyboardShortcuts', msg, true, buttons);
} else { } else {
messageHandler.closeDialog(); messageHandler.closeDialog();
} }

View File

@ -146,16 +146,13 @@ function doXmppAuth (room, lockPassword) {
room.getName(), APP.conference._getConferenceOptions() room.getName(), APP.conference._getConferenceOptions()
); );
loginDialog.displayConnectionStatus( loginDialog.displayConnectionStatus('connection.FETCH_SESSION_ID');
APP.translation.translateString('connection.FETCH_SESSION_ID')
);
newRoom.room.moderator.authenticate().then(function () { newRoom.room.moderator.authenticate().then(function () {
connection.disconnect(); connection.disconnect();
loginDialog.displayConnectionStatus( loginDialog.displayConnectionStatus(
APP.translation.translateString('connection.GOT_SESSION_ID') 'connection.GOT_SESSION_ID');
);
// authenticate conference on the fly // authenticate conference on the fly
room.join(lockPassword); room.join(lockPassword);
@ -166,11 +163,8 @@ function doXmppAuth (room, lockPassword) {
console.error('Auth on the fly failed', error); console.error('Auth on the fly failed', error);
let errorMsg = APP.translation.translateString( loginDialog.displayError(
'connection.GET_SESSION_ID_ERROR' 'connection.GET_SESSION_ID_ERROR', {code: code});
);
loginDialog.displayError(errorMsg + code);
}); });
}, function (err) { }, function (err) {
loginDialog.displayError(err); loginDialog.displayError(err);

View File

@ -1,4 +1,4 @@
/* global APP, config */ /* global $, APP, config */
/** /**
* Build html for "password required" dialog. * Build html for "password required" dialog.
@ -15,8 +15,7 @@ function getPasswordInputHtml() {
placeholder=${placeholder} autofocus> placeholder=${placeholder} autofocus>
<input name="password" type="password" <input name="password" type="password"
class="input-control__input" class="input-control__input"
data-i18n="[placeholder]dialog.userPassword" data-i18n="[placeholder]dialog.userPassword">`;
placeholder="user password">`;
} }
/** /**
@ -67,7 +66,7 @@ function LoginDialog(successCallback, cancelCallback) {
value: true value: true
}]; }];
let finishedButtons = [{ let finishedButtons = [{
title: APP.translation.translateString('dialog.retry'), title: APP.translation.generateTranslationHTML('dialog.retry'),
value: 'retry' value: 'retry'
}]; }];
@ -79,7 +78,7 @@ function LoginDialog(successCallback, cancelCallback) {
const states = { const states = {
login: { login: {
title: APP.translation.translateString('dialog.passwordRequired'), titleKey: 'dialog.passwordRequired',
html: getPasswordInputHtml(), html: getPasswordInputHtml(),
buttons: loginButtons, buttons: loginButtons,
focus: ':input:first', focus: ':input:first',
@ -99,13 +98,13 @@ function LoginDialog(successCallback, cancelCallback) {
} }
}, },
connecting: { connecting: {
title: APP.translation.translateString('dialog.connecting'), titleKey: 'dialog.connecting',
html: '<div id="connectionStatus"></div>', html: '<div id="connectionStatus"></div>',
buttons: [], buttons: [],
defaultButton: 0 defaultButton: 0
}, },
finished: { finished: {
title: APP.translation.translateString('dialog.error'), titleKey: 'dialog.error',
html: '<div id="errorMessage"></div>', html: '<div id="errorMessage"></div>',
buttons: finishedButtons, buttons: finishedButtons,
defaultButton: 0, defaultButton: 0,
@ -128,27 +127,31 @@ function LoginDialog(successCallback, cancelCallback) {
/** /**
* Displays error message in 'finished' state which allows either to cancel * Displays error message in 'finished' state which allows either to cancel
* or retry. * or retry.
* @param message the final message to be displayed. * @param messageKey the key to the message to be displayed.
* @param options the options to the error message (optional)
*/ */
this.displayError = function (message) { this.displayError = function (messageKey, options) {
let finishedState = connDialog.getState('finished'); let finishedState = connDialog.getState('finished');
let errorMessageElem = finishedState.find('#errorMessage'); let errorMessageElem = finishedState.find('#errorMessage');
errorMessageElem.text(message); errorMessageElem.attr("data-i18n", messageKey);
APP.translation.translateElement($(errorMessageElem), options);
connDialog.goToState('finished'); connDialog.goToState('finished');
}; };
/** /**
* Show message as connection status. * Show message as connection status.
* @param {string} message * @param {string} messageKey the key to the message
*/ */
this.displayConnectionStatus = function (message) { this.displayConnectionStatus = function (messageKey) {
let connectingState = connDialog.getState('connecting'); let connectingState = connDialog.getState('connecting');
let connectionStatus = connectingState.find('#connectionStatus'); let connectionStatus = connectingState.find('#connectionStatus');
connectionStatus.text(message); connectionStatus.attr("data-i18n", messageKey);
APP.translation.translateElement($(connectionStatus));
}; };
/** /**
@ -202,9 +205,6 @@ export default {
* @returns dialog * @returns dialog
*/ */
showAuthRequiredDialog: function (roomName, onAuthNow) { showAuthRequiredDialog: function (roomName, onAuthNow) {
var title = APP.translation.generateTranslationHTML(
"dialog.WaitingForHost"
);
var msg = APP.translation.generateTranslationHTML( var msg = APP.translation.generateTranslationHTML(
"dialog.WaitForHostMsg", {room: roomName} "dialog.WaitForHostMsg", {room: roomName}
); );
@ -215,7 +215,7 @@ export default {
var buttons = [{title: buttonTxt, value: "authNow"}]; var buttons = [{title: buttonTxt, value: "authNow"}];
return APP.UI.messageHandler.openDialog( return APP.UI.messageHandler.openDialog(
title, "dialog.WaitingForHost",
msg, msg,
true, true,
buttons, buttons,

View File

@ -35,7 +35,6 @@ function toggleStars(starCount) {
* @returns {string} the contructed html string * @returns {string} the contructed html string
*/ */
function createRateFeedbackHTML() { function createRateFeedbackHTML() {
let feedbackHelp = APP.translation.translateString('dialog.feedbackHelp');
let starClassName = (interfaceConfig.ENABLE_FEEDBACK_ANIMATION) let starClassName = (interfaceConfig.ENABLE_FEEDBACK_ANIMATION)
? "icon-star-full shake-rotate" ? "icon-star-full shake-rotate"
@ -67,8 +66,8 @@ function createRateFeedbackHTML() {
</div> </div>
</div> </div>
<div class="details"> <div class="details">
<textarea id="feedbackTextArea" class="input-control__input" <textarea id="feedbackTextArea" class="input-control__input"
placeholder="${ feedbackHelp }"></textarea> data-i18n="[placeholder]dialog.feedbackHelp"></textarea>
</div> </div>
</form>`; </form>`;
} }
@ -147,10 +146,10 @@ export default class Dialog {
this.submitted = false; this.submitted = false;
this.onCloseCallback = function() {}; this.onCloseCallback = function() {};
this.setDefoulOptions(); this.setDefaultOptions();
} }
setDefoulOptions() { setDefaultOptions() {
var self = this; var self = this;
this.options = { this.options = {

View File

@ -8,12 +8,10 @@ import UIUtil from '../util/UIUtil';
*/ */
export default function askForPassword () { export default function askForPassword () {
let titleKey = "dialog.passwordRequired"; let titleKey = "dialog.passwordRequired";
let passMsg = APP.translation.translateString("dialog.password");
let msgString = ` let msgString = `
<input name="lockKey" type="text" <input name="lockKey" type="text"
data-i18n="[placeholder]dialog.password" data-i18n="[placeholder]dialog.password"
placeholder="${passMsg}" autofocus> autofocus>`;
`;
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog({ APP.UI.messageHandler.openTwoButtonDialog({
titleKey, titleKey,

View File

@ -191,8 +191,10 @@ class Invite {
* @param isLocked * @param isLocked
*/ */
setLockedFromElsewhere(isLocked) { setLockedFromElsewhere(isLocked) {
let oldLockState = this.roomLocker.lockedElsewhere; // isLocked can be 1, true or false
if (oldLockState !== isLocked) { let newLockState = (isLocked === 1) || isLocked;
let oldLockState = this.roomLocker.isLocked;
if (oldLockState !== newLockState) {
this.roomLocker.lockedElsewhere = isLocked; this.roomLocker.lockedElsewhere = isLocked;
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK); APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
this.updateView(); this.updateView();

View File

@ -15,20 +15,15 @@ const States = {
*/ */
export default class InviteDialogView { export default class InviteDialogView {
constructor(model) { constructor(model) {
let InviteAttributesKey = 'inviteUrlDefaultMsg';
let title = APP.translation.translateString(InviteAttributesKey);
this.unlockHint = "unlockHint"; this.unlockHint = "unlockHint";
this.lockHint = "lockHint"; this.lockHint = "lockHint";
this.model = model; this.model = model;
if (this.model.inviteUrl === null) { if (this.model.inviteUrl === null) {
this.inviteAttributes = ( this.inviteAttributes = `data-i18n="[value]inviteUrlDefaultMsg"`;
`data-i18n="[value]inviteUrlDefaultMsg" value="${title}"`
);
} else { } else {
let encodedInviteUrl = this.model.getEncodedInviteUrl(); this.inviteAttributes
this.inviteAttributes = `value="${encodedInviteUrl}"`; = `value="${this.model.getEncodedInviteUrl()}"`;
} }
this.initDialog(); this.initDialog();
@ -43,11 +38,7 @@ export default class InviteDialogView {
dialog.submitFunction = this.submitFunction.bind(this); dialog.submitFunction = this.submitFunction.bind(this);
dialog.loadedFunction = this.loadedFunction.bind(this); dialog.loadedFunction = this.loadedFunction.bind(this);
let titleKey = "dialog.shareLink"; dialog.titleKey = "dialog.shareLink";
let titleString = APP.translation.generateTranslationHTML(titleKey);
dialog.titleKey = titleKey;
dialog.titleString = titleString;
this.dialog = dialog; this.dialog = dialog;
this.dialog.states = this.getStates(); this.dialog.states = this.getStates();
@ -101,21 +92,20 @@ export default class InviteDialogView {
*/ */
getStates() { getStates() {
let { let {
titleString titleKey
} = this.dialog; } = this.dialog;
let doneKey = 'dialog.done'; let doneMsg = APP.translation.generateTranslationHTML('dialog.done');
let doneMsg = APP.translation.translateString(doneKey);
let states = {}; let states = {};
let buttons = {}; let buttons = {};
buttons[`${doneMsg}`] = true; buttons[`${doneMsg}`] = true;
states[States.UNLOCKED] = { states[States.UNLOCKED] = {
title: titleString, titleKey,
html: this.getShareLinkBlock() + this.getAddPasswordBlock(), html: this.getShareLinkBlock() + this.getAddPasswordBlock(),
buttons buttons
}; };
states[States.LOCKED] = { states[States.LOCKED] = {
title: titleString, titleKey,
html: this.getShareLinkBlock() + this.getPasswordBlock(), html: this.getShareLinkBlock() + this.getPasswordBlock(),
buttons buttons
}; };
@ -128,34 +118,24 @@ export default class InviteDialogView {
* @returns {string} * @returns {string}
*/ */
getShareLinkBlock() { getShareLinkBlock() {
let copyKey = 'dialog.copy';
let copyText = APP.translation.translateString(copyKey);
let roomLockDescKey = 'dialog.roomLocked';
let roomLockDesc = APP.translation.translateString(roomLockDescKey);
let roomUnlockKey = 'roomUnlocked';
let roomUnlock = APP.translation.translateString(roomUnlockKey);
let classes = 'button-control button-control_light copyInviteLink'; let classes = 'button-control button-control_light copyInviteLink';
return ( return (
`<div class="input-control"> `<div class="input-control">
<label class="input-control__label" for="inviteLinkRef"> <label class="input-control__label" for="inviteLinkRef"
${this.dialog.titleString} data-i18n="${this.dialog.titleKey}"></label>
</label>
<div class="input-control__container"> <div class="input-control__container">
<input class="input-control__input inviteLink" <input class="input-control__input inviteLink"
id="inviteLinkRef" type="text" id="inviteLinkRef" type="text"
${this.inviteAttributes} readonly> ${this.inviteAttributes} readonly>
<button data-i18n="${copyKey}" <button data-i18n="dialog.copy" class="${classes}"></button>
class="${classes}">
${copyText}
</button>
</div> </div>
<p class="input-control__hint ${this.lockHint}"> <p class="input-control__hint ${this.lockHint}">
<span class="icon-security-locked"></span> <span class="icon-security-locked"></span>
<span data-i18n="${roomLockDescKey}">${roomLockDesc}</span> <span data-i18n="dialog.roomLocked"></span>
</p> </p>
<p class="input-control__hint ${this.unlockHint}"> <p class="input-control__hint ${this.unlockHint}">
<span class="icon-security"></span> <span class="icon-security"></span>
<span data-i18n="${roomUnlockKey}">${roomUnlock}</span> <span data-i18n="roomUnlocked"></span>
</p> </p>
</div>` </div>`
); );
@ -166,27 +146,21 @@ export default class InviteDialogView {
* @returns {string} * @returns {string}
*/ */
getAddPasswordBlock() { getAddPasswordBlock() {
let addPassKey = 'dialog.addPassword';
let addPassText = APP.translation.translateString(addPassKey);
let addKey = 'dialog.add';
let addText = APP.translation.translateString(addKey);
let hintKey = 'dialog.createPassword';
let hintMsg = APP.translation.translateString(hintKey);
let html; let html;
if (this.model.isModerator) { if (this.model.isModerator) {
html = (` html = (`
<div class="input-control"> <div class="input-control">
<label class="input-control__label <label class="input-control__label"
for="newPasswordInput" for="newPasswordInput" data-i18n="dialog.addPassword">
data-i18n="${addPassKey}">${addPassText}</label> </label>
<div class="input-control__container"> <div class="input-control__container">
<input class="input-control__input" id="newPasswordInput" <input class="input-control__input" id="newPasswordInput"
type="text" placeholder="${hintMsg}"> type="text"
data-i18n="[placeholder]dialog.createPassword">
<button id="addPasswordBtn" id="inviteDialogAddPassword" <button id="addPasswordBtn" id="inviteDialogAddPassword"
disabled data-i18n="${addKey}" disabled data-i18n="dialog.add"
class="button-control button-control_light"> class="button-control button-control_light">
${addText}
</button> </button>
</div> </div>
</div> </div>
@ -204,22 +178,16 @@ export default class InviteDialogView {
*/ */
getPasswordBlock() { getPasswordBlock() {
let { password, isModerator } = this.model; let { password, isModerator } = this.model;
let removePassKey = 'dialog.removePassword';
let removePassText = APP.translation.translateString(removePassKey);
let currentPassKey = 'dialog.currentPassword';
let currentPassText = APP.translation.translateString(currentPassKey);
let passwordKey = "dialog.passwordLabel";
let passwordText = APP.translation.translateString(passwordKey);
if (isModerator) { if (isModerator) {
return (` return (`
<div class="input-control"> <div class="input-control">
<label class="input-control__label" <label class="input-control__label"
data-i18n="${passwordKey}">${passwordText}</label> data-i18n="dialog.passwordLabel"></label>
<div class="input-control__container"> <div class="input-control__container">
<p class="input-control__text" <p>
data-i18n="${currentPassKey}"> <span class="input-control__text"
${currentPassText} data-i18n="dialog.currentPassword"></span>
<span id="inviteDialogPassword" <span id="inviteDialogPassword"
class="input-control__em"> class="input-control__em">
${password} ${password}
@ -227,9 +195,7 @@ export default class InviteDialogView {
</p> </p>
<a class="link input-control__right" <a class="link input-control__right"
id="inviteDialogRemovePassword" id="inviteDialogRemovePassword"
data-i18n="${removePassKey}"> data-i18n="dialog.removePassword"></a>
${removePassText}
</a>
</div> </div>
</div> </div>
`); `);
@ -355,10 +321,17 @@ export default class InviteDialogView {
*/ */
updateView() { updateView() {
let pass = this.model.getPassword(); let pass = this.model.getPassword();
if (!pass) if (this.model.getRoomLocker().lockedElsewhere || !pass)
pass = APP.translation.translateString("passwordSetRemotely"); $('#inviteDialogPassword').attr("data-i18n", "passwordSetRemotely");
else
$('#inviteDialogPassword').text(pass);
// if we are not moderator we cannot remove password
if (APP.conference.isModerator)
$('#inviteDialogRemovePassword').show();
else
$('#inviteDialogRemovePassword').hide();
$('#inviteDialogPassword').text(pass);
$('#newPasswordInput').val(''); $('#newPasswordInput').val('');
this.disableAddPassIfInputEmpty(); this.disableAddPassIfInputEmpty();

View File

@ -41,8 +41,6 @@ function _isRecordingButtonEnabled() {
* @returns {Promise} * @returns {Promise}
*/ */
function _requestLiveStreamId() { function _requestLiveStreamId() {
const msg = APP.translation.generateTranslationHTML("dialog.liveStreaming");
const token = APP.translation.translateString("dialog.streamKey");
const cancelButton const cancelButton
= APP.translation.generateTranslationHTML("dialog.Cancel"); = APP.translation.generateTranslationHTML("dialog.Cancel");
const backButton = APP.translation.generateTranslationHTML("dialog.Back"); const backButton = APP.translation.generateTranslationHTML("dialog.Back");
@ -55,11 +53,11 @@ function _requestLiveStreamId() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
dialog = APP.UI.messageHandler.openDialogWithStates({ dialog = APP.UI.messageHandler.openDialogWithStates({
state0: { state0: {
title: msg, titleKey: "dialog.liveStreaming",
html: html:
`<input name="streamId" type="text" `<input name="streamId" type="text"
data-i18n="[placeholder]dialog.streamKey" data-i18n="[placeholder]dialog.streamKey"
placeholder="${token}" autofocus>`, autofocus>`,
persistent: false, persistent: false,
buttons: [ buttons: [
{title: cancelButton, value: false}, {title: cancelButton, value: false},
@ -89,7 +87,7 @@ function _requestLiveStreamId() {
}, },
state1: { state1: {
title: msg, titleKey: "dialog.liveStreaming",
html: streamIdRequired, html: streamIdRequired,
persistent: false, persistent: false,
buttons: [ buttons: [
@ -122,11 +120,10 @@ function _requestLiveStreamId() {
*/ */
function _requestRecordingToken () { function _requestRecordingToken () {
let titleKey = "dialog.recordingToken"; let titleKey = "dialog.recordingToken";
let token = APP.translation.translateString("dialog.token");
let messageString = ( let messageString = (
`<input name="recordingToken" type="text" `<input name="recordingToken" type="text"
data-i18n="[placeholder]dialog.token" data-i18n="[placeholder]dialog.token"
placeholder="${token}" autofocus>` autofocus>`
); );
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
dialog = APP.UI.messageHandler.openTwoButtonDialog({ dialog = APP.UI.messageHandler.openTwoButtonDialog({
@ -297,8 +294,7 @@ var Recording = {
selector.addClass(this.baseClass); selector.addClass(this.baseClass);
selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip); selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
selector.attr("content", APP.translation.translateElement(selector);
APP.translation.translateString(this.recordingButtonTooltip));
var self = this; var self = this;
selector.click(function () { selector.click(function () {
@ -365,7 +361,7 @@ var Recording = {
dialog = APP.UI.messageHandler.openMessageDialog( dialog = APP.UI.messageHandler.openMessageDialog(
self.recordingTitle, self.recordingTitle,
self.recordingBusy, self.recordingBusy,
null, null, null,
function () { function () {
dialog = null; dialog = null;
} }
@ -376,7 +372,7 @@ var Recording = {
dialog = APP.UI.messageHandler.openMessageDialog( dialog = APP.UI.messageHandler.openMessageDialog(
self.recordingTitle, self.recordingTitle,
self.recordingUnavailable, self.recordingUnavailable,
null, null, null,
function () { function () {
dialog = null; dialog = null;
} }
@ -505,7 +501,7 @@ var Recording = {
moveToCorner(labelSelector, !isCentered); moveToCorner(labelSelector, !isCentered);
labelTextSelector.attr("data-i18n", textKey); labelTextSelector.attr("data-i18n", textKey);
labelTextSelector.text(APP.translation.translateString(textKey)); APP.translation.translateElement(labelSelector);
}, },
/** /**

View File

@ -40,7 +40,9 @@ class PageReloadOverlayImpl extends Overlay{
<div id='reloadProgressBar' class="aui-progress-indicator"> <div id='reloadProgressBar' class="aui-progress-indicator">
<span class="aui-progress-indicator-value"></span> <span class="aui-progress-indicator-value"></span>
</div> </div>
<span id='reloadSecRemaining' class='reload_overlay_msg'> <span id='reloadSecRemaining'
data-i18n="dialog.conferenceReloadTimeLeft"
class='reload_overlay_msg'>
</span> </span>
</div>`; </div>`;
} }
@ -50,11 +52,8 @@ class PageReloadOverlayImpl extends Overlay{
*/ */
updateDisplay() { updateDisplay() {
const timeLeftTxt APP.translation.translateElement(
= APP.translation.translateString( $("#reloadSecRemaining"), { seconds: this.timeLeft });
"dialog.conferenceReloadTimeLeft",
{ seconds: this.timeLeft });
$("#reloadSecRemaining").text(timeLeftTxt);
const ratio = (this.timeout - this.timeLeft) / this.timeout; const ratio = (this.timeout - this.timeLeft) / this.timeout;
AJS.progressBars.update("#reloadProgressBar", ratio); AJS.progressBars.update("#reloadProgressBar", ratio);

View File

@ -93,7 +93,7 @@ export default class SharedVideoManager {
dialog = APP.UI.messageHandler.openMessageDialog( dialog = APP.UI.messageHandler.openMessageDialog(
"dialog.shareVideoTitle", "dialog.shareVideoTitle",
"dialog.alreadySharedVideoMsg", "dialog.alreadySharedVideoMsg",
null, null, null,
function () { function () {
dialog = null; dialog = null;
} }
@ -750,24 +750,19 @@ function showStopVideoPropmpt() {
*/ */
function requestVideoLink() { function requestVideoLink() {
let i18n = APP.translation; let i18n = APP.translation;
const title = i18n.generateTranslationHTML("dialog.shareVideoTitle");
const cancelButton = i18n.generateTranslationHTML("dialog.Cancel"); const cancelButton = i18n.generateTranslationHTML("dialog.Cancel");
const shareButton = i18n.generateTranslationHTML("dialog.Share"); const shareButton = i18n.generateTranslationHTML("dialog.Share");
const backButton = i18n.generateTranslationHTML("dialog.Back"); const backButton = i18n.generateTranslationHTML("dialog.Back");
const linkError const linkError
= i18n.generateTranslationHTML("dialog.shareVideoLinkError"); = i18n.generateTranslationHTML("dialog.shareVideoLinkError");
const i18nOptions = {url: defaultSharedVideoLink};
const defaultUrl = i18n.translateString("defaultLink", i18nOptions);
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
dialog = APP.UI.messageHandler.openDialogWithStates({ dialog = APP.UI.messageHandler.openDialogWithStates({
state0: { state0: {
title: title, titleKey: "dialog.shareVideoTitle",
html: ` html: `
<input name="sharedVideoUrl" type="text" <input name="sharedVideoUrl" type="text"
data-i18n="[placeholder]defaultLink" data-i18n="[placeholder]defaultLink"
data-i18n-options="${JSON.stringify(i18nOptions)}"
placeholder="${defaultUrl}"
autofocus>`, autofocus>`,
persistent: false, persistent: false,
buttons: [ buttons: [
@ -802,7 +797,7 @@ function requestVideoLink() {
}, },
state1: { state1: {
title: title, titleKey: "dialog.shareVideoTitle",
html: linkError, html: linkError,
persistent: false, persistent: false,
buttons: [ buttons: [
@ -825,7 +820,8 @@ function requestVideoLink() {
close: function () { close: function () {
dialog = null; dialog = null;
} }
}, {
url: defaultSharedVideoLink
}); });
}); });
} }

View File

@ -21,9 +21,8 @@ function updateNumberOfParticipants(delta) {
$("#numberOfParticipants").text(numberOfContacts); $("#numberOfParticipants").text(numberOfContacts);
$("#contacts_container>div.title").text( APP.translation.translateElement(
APP.translation.translateString("contactlist") $("#contacts_container>div.title"), {pcount: numberOfContacts});
+ ' (' + numberOfContacts + ')');
} }
/** /**
@ -50,7 +49,6 @@ function createDisplayNameParagraph(key, displayName) {
p.innerHTML = displayName; p.innerHTML = displayName;
} else if(key) { } else if(key) {
p.setAttribute("data-i18n",key); p.setAttribute("data-i18n",key);
p.innerHTML = APP.translation.translateString(key);
} }
return p; return p;
@ -82,10 +80,11 @@ var ContactListView = {
*/ */
addInviteButton() { addInviteButton() {
let container = document.getElementById('contacts_container'); let container = document.getElementById('contacts_container');
let title = container.firstElementChild;
let htmlLayout = this.getInviteButtonLayout(); container.firstElementChild // this is the title
title.insertAdjacentHTML('afterend', htmlLayout); .insertAdjacentHTML('afterend', this.getInviteButtonLayout());
APP.translation.translateElement($(container));
$(document).on('click', '#addParticipantsBtn', () => { $(document).on('click', '#addParticipantsBtn', () => {
APP.UI.emitEvent(UIEvents.INVITE_CLICKED); APP.UI.emitEvent(UIEvents.INVITE_CLICKED);
}); });
@ -97,32 +96,26 @@ var ContactListView = {
let classes = 'button-control button-control_primary'; let classes = 'button-control button-control_primary';
classes += ' button-control_full-width'; classes += ' button-control_full-width';
let key = 'addParticipants'; let key = 'addParticipants';
let text = APP.translation.translateString(key);
let lockedHtml = this.getLockDescriptionLayout(this.lockKey); let lockedHtml = this.getLockDescriptionLayout(this.lockKey);
let unlockedHtml = this.getLockDescriptionLayout(this.unlockKey); let unlockedHtml = this.getLockDescriptionLayout(this.unlockKey);
let html = ( return (
`<div class="sideToolbarBlock first"> `<div class="sideToolbarBlock first">
<button id="addParticipantsBtn" <button id="addParticipantsBtn"
data-i18n="${key}" data-i18n="${key}"
class="${classes}"> class="${classes}"></button>
${text}
</button>
<div> <div>
${lockedHtml} ${lockedHtml}
${unlockedHtml} ${unlockedHtml}
</div> </div>
</div>`); </div>`);
return html;
}, },
/** /**
* Adds layout for lock description * Adds layout for lock description
*/ */
getLockDescriptionLayout(key) { getLockDescriptionLayout(key) {
let classes = "input-control__hint input-control_full-width"; let classes = "input-control__hint input-control_full-width";
let description = APP.translation.translateString(key);
let padlockSuffix = ''; let padlockSuffix = '';
if (key === this.lockKey) { if (key === this.lockKey) {
padlockSuffix = '-locked'; padlockSuffix = '-locked';
@ -130,7 +123,7 @@ var ContactListView = {
return `<p id="contactList${key}" class="${classes}"> return `<p id="contactList${key}" class="${classes}">
<span class="icon-security${padlockSuffix}"></span> <span class="icon-security${padlockSuffix}"></span>
<span data-i18n="${key}">${description}</span> <span data-i18n="${key}"></span>
</p>`; </p>`;
}, },
/** /**
@ -198,6 +191,7 @@ var ContactListView = {
createDisplayNameParagraph( createDisplayNameParagraph(
isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null, isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME)); isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
APP.translation.translateElement($(newContact));
if (APP.conference.isLocalId(id)) { if (APP.conference.isLocalId(id)) {
contactlist.prepend(newContact); contactlist.prepend(newContact);

View File

@ -279,7 +279,6 @@ function showSipNumberInput () {
? config.defaultSipNumber ? config.defaultSipNumber
: ''; : '';
let titleKey = "dialog.sipMsg"; let titleKey = "dialog.sipMsg";
let sipMsg = APP.translation.generateTranslationHTML("dialog.sipMsg");
let msgString = (` let msgString = (`
<input name="sipNumber" type="text" <input name="sipNumber" type="text"
value="${defaultNumber}" autofocus> value="${defaultNumber}" autofocus>
@ -287,7 +286,6 @@ function showSipNumberInput () {
APP.UI.messageHandler.openTwoButtonDialog({ APP.UI.messageHandler.openTwoButtonDialog({
titleKey, titleKey,
titleString: sipMsg,
msgString, msgString,
leftButtonKey: "dialog.Dial", leftButtonKey: "dialog.Dial",
submitFunction: function (e, v, m, f) { submitFunction: function (e, v, m, f) {

View File

@ -1,22 +1,26 @@
/* global $ */ /* global $ */
var JitsiPopover = (function () { var JitsiPopover = (function () {
/**
* The default options
*/
const defaultOptions = {
skin: 'white',
content: '',
hasArrow: true,
onBeforePosition: undefined
};
/** /**
* Constructs new JitsiPopover and attaches it to the element * Constructs new JitsiPopover and attaches it to the element
* @param element jquery selector * @param element jquery selector
* @param options the options for the popover. * @param options the options for the popover.
* - {Function} onBeforePosition - function executed just before
* positioning the popover. Useful for translation.
* @constructor * @constructor
*/ */
function JitsiPopover(element, options) function JitsiPopover(element, options)
{ {
let { skin, content, hasArrow } = options; this.options = Object.assign({}, defaultOptions, options);
this.options = {};
this.options.skin = skin || 'white';
this.options.content = content || '';
this.options.hasArrow = true;
if (typeof(hasArrow) !== 'undefined') {
this.options.hasArrow = false;
}
this.elementIsHovered = false; this.elementIsHovered = false;
this.popoverIsHovered = false; this.popoverIsHovered = false;
@ -86,7 +90,11 @@ var JitsiPopover = (function () {
*/ */
JitsiPopover.prototype.createPopover = function () { JitsiPopover.prototype.createPopover = function () {
$("body").append(this.template); $("body").append(this.template);
$(".jitsipopover > .jitsipopover__content").html(this.options.content); let popoverElem = $(".jitsipopover > .jitsipopover__content");
popoverElem.html(this.options.content);
if(typeof this.options.onBeforePosition === "function") {
this.options.onBeforePosition($(".jitsipopover"));
}
var self = this; var self = this;
$(".jitsipopover").on("mouseenter", function () { $(".jitsipopover").on("mouseenter", function () {
self.popoverIsHovered = true; self.popoverIsHovered = true;
@ -136,4 +144,4 @@ var JitsiPopover = (function () {
return JitsiPopover; return JitsiPopover;
})(); })();
module.exports = JitsiPopover; module.exports = JitsiPopover;

View File

@ -29,30 +29,20 @@ var messageHandler = {
* *
* @param titleKey the key used to find the translation of the title of the * @param titleKey the key used to find the translation of the title of the
* message, if a message title is not provided. * message, if a message title is not provided.
* @param messageKey the key used to find the translation of the message, * @param messageKey the key used to find the translation of the message
* if a message is not provided. * @param i18nOptions the i18n options (optional)
* @param title the title of the message. If a falsy value is provided,
* titleKey will be used to get a title via the translation API.
* @param message the message to show. If a falsy value is provided,
* messageKey will be used to get a message via the translation API.
* @param closeFunction function to be called after * @param closeFunction function to be called after
* the prompt is closed (optional) * the prompt is closed (optional)
* @return the prompt that was created, or null * @return the prompt that was created, or null
*/ */
openMessageDialog: function(titleKey, messageKey, title, message, openMessageDialog:
closeFunction) { function(titleKey, messageKey, i18nOptions, closeFunction) {
if (!popupEnabled) if (!popupEnabled)
return null; return null;
if (!title) { let dialog = $.prompt(
title = APP.translation.generateTranslationHTML(titleKey); APP.translation.generateTranslationHTML(messageKey, i18nOptions), {
} title: this._getFormattedTitleString(titleKey),
if (!message) {
message = APP.translation.generateTranslationHTML(messageKey);
}
return $.prompt(message, {
title: this._getFormattedTitleString(title),
persistent: false, persistent: false,
promptspeed: 0, promptspeed: 0,
classes: this._getDialogClasses(), classes: this._getDialogClasses(),
@ -61,12 +51,14 @@ var messageHandler = {
closeFunction(e, v, m, f); closeFunction(e, v, m, f);
} }
}); });
APP.translation.translateElement(dialog, i18nOptions);
return dialog;
}, },
/** /**
* Shows a message to the user with two buttons: first is given as a * Shows a message to the user with two buttons: first is given as a
* parameter and the second is Cancel. * parameter and the second is Cancel.
* *
* @param titleString the title of the message * @param titleKey the key for the title of the message
* @param msgString the text of the message * @param msgString the text of the message
* @param persistent boolean value which determines whether the message is * @param persistent boolean value which determines whether the message is
* persistent or not * persistent or not
@ -84,7 +76,6 @@ var messageHandler = {
openTwoButtonDialog: function(options) { openTwoButtonDialog: function(options) {
let { let {
titleKey, titleKey,
titleString,
msgKey, msgKey,
msgString, msgString,
leftButtonKey, leftButtonKey,
@ -112,10 +103,7 @@ var messageHandler = {
= APP.translation.generateTranslationHTML("dialog.Cancel"); = APP.translation.generateTranslationHTML("dialog.Cancel");
buttons.push({title: cancelButton, value: false}); buttons.push({title: cancelButton, value: false});
var message = msgString, title = titleString; var message = msgString;
if (titleKey) {
title = APP.translation.generateTranslationHTML(titleKey);
}
if (msgKey) { if (msgKey) {
message = APP.translation.generateTranslationHTML(msgKey); message = APP.translation.generateTranslationHTML(msgKey);
} }
@ -125,7 +113,7 @@ var messageHandler = {
} }
twoButtonDialog = $.prompt(message, { twoButtonDialog = $.prompt(message, {
title: this._getFormattedTitleString(title), title: this._getFormattedTitleString(titleKey),
persistent: false, persistent: false,
buttons: buttons, buttons: buttons,
defaultButton: defaultButton, defaultButton: defaultButton,
@ -147,6 +135,7 @@ var messageHandler = {
} }
} }
}); });
APP.translation.translateElement(twoButtonDialog);
return twoButtonDialog; return twoButtonDialog;
}, },
@ -154,7 +143,7 @@ var messageHandler = {
* Shows a message to the user with two buttons: first is given as a * Shows a message to the user with two buttons: first is given as a
* parameter and the second is Cancel. * parameter and the second is Cancel.
* *
* @param titleString the title of the message * @param titleKey the key for the title of the message
* @param msgString the text of the message * @param msgString the text of the message
* @param persistent boolean value which determines whether the message is * @param persistent boolean value which determines whether the message is
* persistent or not * persistent or not
@ -166,13 +155,13 @@ var messageHandler = {
* loaded * loaded
* @param closeFunction function to be called on dialog close * @param closeFunction function to be called on dialog close
*/ */
openDialog: function (titleString, msgString, persistent, buttons, openDialog: function (titleKey, msgString, persistent, buttons,
submitFunction, loadedFunction, closeFunction) { submitFunction, loadedFunction, closeFunction) {
if (!popupEnabled) if (!popupEnabled)
return; return;
let args = { let args = {
title: this._getFormattedTitleString(titleString), title: this._getFormattedTitleString(titleKey),
persistent: persistent, persistent: persistent,
buttons: buttons, buttons: buttons,
defaultButton: 1, defaultButton: 1,
@ -187,7 +176,9 @@ var messageHandler = {
args.closeText = ''; args.closeText = '';
} }
return new Impromptu(msgString, args); let dialog = new Impromptu(msgString, args);
APP.translation.translateElement(dialog.getPrompt());
return dialog;
}, },
/** /**
@ -195,13 +186,11 @@ var messageHandler = {
* *
* @return the title string formatted as a div. * @return the title string formatted as a div.
*/ */
_getFormattedTitleString(titleString) { _getFormattedTitleString(titleKey) {
let $titleString = $('<h2>'); let $titleString = $('<h2>');
$titleString.addClass('aui-dialog2-header-main'); $titleString.addClass('aui-dialog2-header-main');
$titleString.append(titleString); $titleString.attr('data-i18n',titleKey);
titleString = $('<div>').append($titleString).html(); return $('<div>').append($titleString).html();
return titleString;
}, },
/** /**
@ -235,8 +224,10 @@ var messageHandler = {
* Shows a dialog with different states to the user. * Shows a dialog with different states to the user.
* *
* @param statesObject object containing all the states of the dialog. * @param statesObject object containing all the states of the dialog.
* @param options impromptu options
* @param translateOptions options passed to translation
*/ */
openDialogWithStates: function (statesObject, options) { openDialogWithStates: function (statesObject, options, translateOptions) {
if (!popupEnabled) if (!popupEnabled)
return; return;
let { classes, size } = options; let { classes, size } = options;
@ -246,12 +237,14 @@ var messageHandler = {
for (let state in statesObject) { for (let state in statesObject) {
let currentState = statesObject[state]; let currentState = statesObject[state];
if(currentState.title) { if(currentState.titleKey) {
let title = currentState.title; currentState.title
currentState.title = this._getFormattedTitleString(title); = this._getFormattedTitleString(currentState.titleKey);
} }
} }
return new Impromptu(statesObject, options); let dialog = new Impromptu(statesObject, options);
APP.translation.translateElement(dialog.getPrompt(), translateOptions);
return dialog;
}, },
/** /**
@ -340,20 +333,18 @@ var messageHandler = {
if (displayName) { if (displayName) {
displayNameSpan += ">" + UIUtil.escapeHtml(displayName); displayNameSpan += ">" + UIUtil.escapeHtml(displayName);
} else { } else {
displayNameSpan += "data-i18n='" + displayNameKey + displayNameSpan += "data-i18n='" + displayNameKey + "'>";
"'>" + APP.translation.translateString(displayNameKey);
} }
displayNameSpan += "</span>"; displayNameSpan += "</span>";
return toastr.info( let element = toastr.info(
displayNameSpan + '<br>' + displayNameSpan + '<br>' +
'<span class=' + cls + ' data-i18n="' + messageKey + '"' + '<span class=' + cls + ' data-i18n="' + messageKey + '"' +
(messageArguments? (messageArguments?
" data-i18n-options='" + JSON.stringify(messageArguments) " data-i18n-options='"
+ "'" + JSON.stringify(messageArguments) + "'"
: "") + ">" + : "") + "></span>", null, options);
APP.translation.translateString(messageKey, APP.translation.translateElement(element);
messageArguments) + return element;
'</span>', null, options);
}, },
/** /**

View File

@ -1,4 +1,4 @@
/* global APP, $, config */ /* global $, APP, config */
/* jshint -W101 */ /* jshint -W101 */
import JitsiPopover from "../util/JitsiPopover"; import JitsiPopover from "../util/JitsiPopover";
import VideoLayout from "./VideoLayout"; import VideoLayout from "./VideoLayout";
@ -63,8 +63,6 @@ ConnectionIndicator.getStringFromArray = function (array) {
ConnectionIndicator.prototype.generateText = function () { ConnectionIndicator.prototype.generateText = function () {
var downloadBitrate, uploadBitrate, packetLoss, i; var downloadBitrate, uploadBitrate, packetLoss, i;
var translate = APP.translation.translateString;
if(this.bitrate === null) { if(this.bitrate === null) {
downloadBitrate = "N/A"; downloadBitrate = "N/A";
uploadBitrate = "N/A"; uploadBitrate = "N/A";
@ -99,9 +97,7 @@ ConnectionIndicator.prototype.generateText = function () {
`<table class="connection-info__container" style='width:100%'> `<table class="connection-info__container" style='width:100%'>
<tr> <tr>
<td> <td>
<span data-i18n='connectionindicator.bitrate'> <span data-i18n='connectionindicator.bitrate'></span>
${translate("connectionindicator.bitrate")}
</span>
</td> </td>
<td> <td>
<span class='connection-info__download'>&darr;</span>${downloadBitrate} <span class='connection-info__download'>&darr;</span>${downloadBitrate}
@ -110,17 +106,13 @@ ConnectionIndicator.prototype.generateText = function () {
</tr> </tr>
<tr> <tr>
<td> <td>
<span data-i18n='connectionindicator.packetloss'> <span data-i18n='connectionindicator.packetloss'></span>
${translate("connectionindicator.packetloss")}
</span>
</td> </td>
<td>${packetLoss}</td> <td>${packetLoss}</td>
</tr> </tr>
<tr> <tr>
<td> <td>
<span data-i18n='connectionindicator.resolution'> <span data-i18n='connectionindicator.resolution'></span>
${translate("connectionindicator.resolution")}
</span>
</td> </td>
<td> <td>
${resolutionStr} ${resolutionStr}
@ -134,9 +126,7 @@ ConnectionIndicator.prototype.generateText = function () {
// FIXME: we do not know local id when this text is generated // FIXME: we do not know local id when this text is generated
//this.id + "')\" data-i18n='connectionindicator." + //this.id + "')\" data-i18n='connectionindicator." +
"local')\" data-i18n='connectionindicator." + "local')\" data-i18n='connectionindicator." +
(this.showMoreValue ? "less" : "more") + "'>" + (this.showMoreValue ? "less" : "more") + "'></a>";
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
"</a>";
} }
if (this.showMoreValue) { if (this.showMoreValue) {
@ -156,8 +146,7 @@ ConnectionIndicator.prototype.generateText = function () {
if (!this.transport || this.transport.length === 0) { if (!this.transport || this.transport.length === 0) {
transport = "<tr>" + transport = "<tr>" +
"<td><span " + "<td><span " +
"data-i18n='connectionindicator.address'>" + "data-i18n='connectionindicator.address'></span></td>" +
translate("connectionindicator.address") + "</span></td>" +
"<td> N/A</td></tr>"; "<td> N/A</td></tr>";
} else { } else {
var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]}; var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
@ -190,18 +179,15 @@ ConnectionIndicator.prototype.generateText = function () {
var localTransport = var localTransport =
"<tr><td><span data-i18n='" + "<tr><td><span data-i18n='" +
local_address_key +"' data-i18n-options='" + local_address_key +"' data-i18n-options='" +
JSON.stringify({count: data.localIP.length}) + "'>" + JSON.stringify({count: data.localIP.length})
translate(local_address_key, {count: data.localIP.length}) + + "'></span></td><td> " +
"</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.localIP) + ConnectionIndicator.getStringFromArray(data.localIP) +
"</td></tr>"; "</td></tr>";
transport = transport =
"<tr><td><span data-i18n='" + "<tr><td><span data-i18n='" +
remote_address_key + "' data-i18n-options='" + remote_address_key + "' data-i18n-options='" +
JSON.stringify({count: data.remoteIP.length}) + "'>" + JSON.stringify({count: data.remoteIP.length})
translate(remote_address_key, + "'></span></td><td> " +
{count: data.remoteIP.length}) +
"</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.remoteIP) + ConnectionIndicator.getStringFromArray(data.remoteIP) +
"</td></tr>"; "</td></tr>";
@ -212,16 +198,14 @@ ConnectionIndicator.prototype.generateText = function () {
"<td>" + "<td>" +
"<span data-i18n='" + key_remote + "<span data-i18n='" + key_remote +
"' data-i18n-options='" + "' data-i18n-options='" +
JSON.stringify({count: this.transport.length}) + "'>" + JSON.stringify({count: this.transport.length})
translate(key_remote, {count: this.transport.length}) + + "'></span></td><td>";
"</span></td><td>";
localTransport += "<tr>" + localTransport += "<tr>" +
"<td>" + "<td>" +
"<span data-i18n='" + key_local + "<span data-i18n='" + key_local +
"' data-i18n-options='" + "' data-i18n-options='" +
JSON.stringify({count: this.transport.length}) + "'>" + JSON.stringify({count: this.transport.length})
translate(key_local, {count: this.transport.length}) + + "'></span></td><td>";
"</span></td><td>";
transport += transport +=
ConnectionIndicator.getStringFromArray(data.remotePort); ConnectionIndicator.getStringFromArray(data.remotePort);
@ -231,7 +215,7 @@ ConnectionIndicator.prototype.generateText = function () {
transport += localTransport + "</td></tr>"; transport += localTransport + "</td></tr>";
transport +="<tr>" + transport +="<tr>" +
"<td><span data-i18n='connectionindicator.transport'>" + "<td><span data-i18n='connectionindicator.transport'>" +
translate("connectionindicator.transport") + "</span></td>" + "</span></td>" +
"<td>" + this.transport[0].type + "</td></tr>"; "<td>" + this.transport[0].type + "</td></tr>";
} }
@ -239,8 +223,7 @@ ConnectionIndicator.prototype.generateText = function () {
result += "<table class='connection-info__container' style='width:100%'>" + result += "<table class='connection-info__container' style='width:100%'>" +
"<tr>" + "<tr>" +
"<td>" + "<td>" +
"<span data-i18n='connectionindicator.bandwidth'>" + "<span data-i18n='connectionindicator.bandwidth'></span>" +
translate("connectionindicator.bandwidth") + "</span>" +
"</td><td>" + "</td><td>" +
"<span class='connection-info__download'>&darr;</span>" + "<span class='connection-info__download'>&darr;</span>" +
downloadBandwidth + downloadBandwidth +
@ -282,10 +265,12 @@ ConnectionIndicator.prototype.create = function () {
this.videoContainer.container.appendChild( this.videoContainer.container.appendChild(
this.connectionIndicatorContainer); this.connectionIndicatorContainer);
this.popover = new JitsiPopover( this.popover = new JitsiPopover(
$("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), {
{content: "<div class=\"connection-info\" data-i18n='connectionindicator.na'>" + content: "<div class=\"connection-info\" " +
APP.translation.translateString("connectionindicator.na") + "</div>", "data-i18n='connectionindicator.na'></div>",
skin: "black"}); skin: "black",
onBeforePosition: el => APP.translation.translateElement(el)
});
// override popover show method to make sure we will update the content // override popover show method to make sure we will update the content
// before showing the popover // before showing the popover
@ -398,7 +383,6 @@ ConnectionIndicator.prototype.updatePopoverData = function (force) {
this.popover.updateContent( this.popover.updateContent(
`<div class="connection-info">${this.generateText()}</div>` `<div class="connection-info">${this.generateText()}</div>`
); );
APP.translation.translateElement($(".connection-info"));
} }
}; };

View File

@ -379,9 +379,10 @@ export default class LargeVideoManager {
*/ */
_setRemoteConnectionMessage (msgKey, msgOptions) { _setRemoteConnectionMessage (msgKey, msgOptions) {
if (msgKey) { if (msgKey) {
let text = APP.translation.translateString(msgKey, msgOptions);
$('#remoteConnectionMessage') $('#remoteConnectionMessage')
.attr("data-i18n", msgKey).text(text); .attr("data-i18n", msgKey)
.attr("data-i18n-options", JSON.stringify(msgOptions));
APP.translation.translateElement($('#remoteConnectionMessage'));
} }
this.videoContainer.positionRemoteConnectionMessage(); this.videoContainer.positionRemoteConnectionMessage();
@ -400,7 +401,8 @@ export default class LargeVideoManager {
_setLocalConnectionMessage (msgKey, msgOptions) { _setLocalConnectionMessage (msgKey, msgOptions) {
$('#localConnectionMessage') $('#localConnectionMessage')
.attr("data-i18n", msgKey) .attr("data-i18n", msgKey)
.text(APP.translation.translateString(msgKey, msgOptions)); .attr("data-i18n-options", JSON.stringify(msgOptions));
APP.translation.translateElement($('#localConnectionMessage'));
} }
/** /**

View File

@ -96,14 +96,12 @@ LocalVideo.prototype.setDisplayName = function(displayName) {
editableText.value = displayName; editableText.value = displayName;
} }
var defaultNickname = APP.translation.translateString(
"defaultNickname", {name: "Jane Pink"});
editableText.setAttribute('style', 'display:none;'); editableText.setAttribute('style', 'display:none;');
editableText.setAttribute('data-18n', editableText.setAttribute('data-i18n',
'[placeholder]defaultNickname'); '[placeholder]defaultNickname');
editableText.setAttribute("data-i18n-options", editableText.setAttribute("data-i18n-options",
JSON.stringify({name: "Jane Pink"})); JSON.stringify({name: "Jane Pink"}));
editableText.setAttribute("placeholder", defaultNickname); APP.translation.translateElement($(editableText));
this.container this.container
.querySelector('.videocontainer__toolbar') .querySelector('.videocontainer__toolbar')
@ -253,7 +251,8 @@ LocalVideo.prototype._buildContextMenu = function () {
events: { events: {
show : function(options){ show : function(options){
options.items.flip.name = options.items.flip.name =
APP.translation.translateString("videothumbnail.flip"); APP.translation.generateTranslationHTML(
"videothumbnail.flip");
} }
} }
}); });

View File

@ -78,7 +78,8 @@ RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
let options = { let options = {
content: popupMenuElement.outerHTML, content: popupMenuElement.outerHTML,
skin: "black", skin: "black",
hasArrow: false hasArrow: false,
onBeforePosition: el => APP.translation.translateElement(el)
}; };
let element = $("#" + this.videoSpanId + " .remotevideomenu"); let element = $("#" + this.videoSpanId + " .remotevideomenu");
this.popover = new JitsiPopover(element, options); this.popover = new JitsiPopover(element, options);
@ -112,16 +113,10 @@ RemoteVideo.prototype._generatePopupContent = function () {
var mutedIndicator = "<i class='icon-mic-disabled'></i>"; var mutedIndicator = "<i class='icon-mic-disabled'></i>";
var doMuteHTML = mutedIndicator + var doMuteHTML = mutedIndicator +
" <div " + " <div data-i18n='videothumbnail.domute'></div>";
"data-i18n='videothumbnail.domute'>" +
APP.translation.translateString("videothumbnail.domute") +
"</div>";
var mutedHTML = mutedIndicator + var mutedHTML = mutedIndicator +
" <div " + " <div data-i18n='videothumbnail.muted'></div>";
"data-i18n='videothumbnail.muted'>" +
APP.translation.translateString("videothumbnail.muted") +
"</div>";
muteLinkItem.id = "mutelink_" + this.id; muteLinkItem.id = "mutelink_" + this.id;
@ -153,10 +148,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
var ejectMenuItem = document.createElement('li'); var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a'); var ejectLinkItem = document.createElement('a');
var ejectText = "<div " + var ejectText = "<div data-i18n='videothumbnail.kick'></div>";
"data-i18n='videothumbnail.kick'>" +
APP.translation.translateString("videothumbnail.kick") +
"</div>";
ejectLinkItem.className = 'ejectlink'; ejectLinkItem.className = 'ejectlink';
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText; ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
@ -170,6 +162,8 @@ RemoteVideo.prototype._generatePopupContent = function () {
ejectMenuItem.appendChild(ejectLinkItem); ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem); popupmenuElement.appendChild(ejectMenuItem);
APP.translation.translateElement($(popupmenuElement));
return popupmenuElement; return popupmenuElement;
}; };

View File

@ -190,8 +190,7 @@ var KeyboardShortcut = {
let descriptionClass = "shortcuts-list__description"; let descriptionClass = "shortcuts-list__description";
descriptionElement.className = descriptionClass; descriptionElement.className = descriptionClass;
descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey); descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
descriptionElement.innerHTML APP.translation.translateElement($(descriptionElement));
= APP.translation.translateString(shortcutDescriptionKey);
listElement.appendChild(spanElement); listElement.appendChild(spanElement);
listElement.appendChild(descriptionElement); listElement.appendChild(descriptionElement);

View File

@ -89,9 +89,6 @@ module.exports = {
i18n.init(options, initCompleted); i18n.init(options, initCompleted);
}, },
translateString: function (key, options) {
return i18n.t(key, options);
},
setLanguage: function (lang) { setLanguage: function (lang) {
if(!lang) if(!lang)
lang = DEFAULT_LANG; lang = DEFAULT_LANG;
@ -100,16 +97,19 @@ module.exports = {
getCurrentLanguage: function () { getCurrentLanguage: function () {
return i18n.lng(); return i18n.lng();
}, },
translateElement: function (selector) { translateElement: function (selector, options) {
selector.i18n(); // i18next expects undefined if options are missing, check if its null
selector.i18n(
options === null ? undefined : options);
}, },
generateTranslationHTML: function (key, options) { generateTranslationHTML: function (key, options) {
var str = "<span data-i18n=\"" + key + "\""; var str = "<span data-i18n=\"" + key + "\"";
if (options) { if (options) {
str += " data-i18n-options=\"" + JSON.stringify(options) + "\""; str += " data-i18n-options='" + JSON.stringify(options) + "'";
} }
str += ">"; str += ">";
str += this.translateString(key, options); // i18next expects undefined if options ARE missing, check if its null
str += i18n.t(key, options === null ? undefined : options);
str += "</span>"; str += "</span>";
return str; return str;