Implements basic multi language support.

This commit is contained in:
hristoterezov 2015-02-06 17:46:50 +02:00
parent 04cfbafc33
commit 3032ea7684
16 changed files with 2854 additions and 247 deletions

View File

@ -1,5 +1,5 @@
BROWSERIFY = browserify
GLOBAL_FLAGS = -e
GLOBAL_FLAGS = -x jquery -e
OUTPUT_DIR = .
DEPLOY_DIR = libs
@ -21,3 +21,4 @@ clean:
deploy:
@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR)

3
app.js
View File

@ -13,6 +13,7 @@ var APP =
this.desktopsharing = require("./modules/desktopsharing/desktopsharing");
this.xmpp = require("./modules/xmpp/xmpp");
this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut");
this.translation = require("./modules/translation/translation");
}
};
@ -34,6 +35,8 @@ $(document).ready(function () {
APP.init();
APP.translation.init();
if(APP.API.isEnabled())
APP.API.init();

View File

@ -3,7 +3,7 @@
color: #00ccff;
}
#settingsmenu input {
#settingsmenu input, select {
margin-top: 10px;
margin-left: 10%;
width: 80%;
@ -43,3 +43,7 @@
#settingsmenu .icon-settings {
padding: 34px;
}
#languages_selectbox{
height: 40px;
}

View File

@ -19,7 +19,7 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=5"></script>
<script src="libs/app.bundle.js?v=6"></script>
<script src="libs/app.bundle.js?v=7"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<link rel="stylesheet" href="css/font.css?v=6"/>
@ -52,76 +52,66 @@
<a target="_new">
<div class="watermark rightwatermark"></div>
</a>
<a class="poweredby" href="http://jitsi.org" target="_new" >powered by jitsi.org</a>
<a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
<div id="enter_room_container">
<div id="enter_room_form" >
<div id="domain_name"></div>
<div id="enter_room">
<input id="enter_room_field" type="text" autofocus placeholder="Enter room name" />
<input id="enter_room_field" type="text" autofocus data-i18n="[placeholder]welcomepage.roomname" placeholder="Enter room name" />
<div class="icon-reload" id="reload_roomname"></div>
<input id="enter_room_button" type="button" value="GO" />
<input id="enter_room_button" type="button" data-i18n="[value]welcomepage.go" value="GO" />
</div>
</div>
</div>
<div id="brand_header"></div>
<input type='checkbox' name='checkbox' id="disable_welcome"/>
<label for="disable_welcome" class="disable_welcome_position">Don't show this page next time I enter</label>
<label for="disable_welcome" class="disable_welcome_position" data-i18n="welcomepage.disable"></label>
<div id="header_text"></div>
</div>
<div id="welcome_page_main">
<div id="features">
<div class="feature_row">
<div class="feature_holder">
<div class="feature_icon">Simple to use</div>
<div class="feature_description">
No downloads required. <span name="appName"></span> works directly within your browser. Simply share your conference URL with others to get started.
<div class="feature_icon" data-i18n="welcomepage.feature1.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature1.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
</div>
</div>
<div class="feature_holder">
<div class="feature_icon">Low bandwidth</div>
<div class="feature_description">
Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less.
<div class="feature_icon" data-i18n="welcomepage.feature2.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature2.content">
</div>
</div>
<div class="feature_holder">
<div class="feature_icon">Open source</div>
<div class="feature_description">
<span name="appName"></span> is licensed under MIT. You are free to download, use, modify, and share them as per these licenses.
<div class="feature_icon" data-i18n="welcomepage.feature3.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature3.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
</div>
</div>
<div class="feature_holder">
<div class="feature_icon">Unlimited users</div>
<div class="feature_description">
There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors.
<div class="feature_icon" data-i18n="welcomepage.feature4.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature4.content">
</div>
</div>
</div>
<div class="feature_row">
<div class="feature_holder">
<div class="feature_icon">Screen sharing</div>
<div class="feature_description">
It's easy to share your screen with others. <span name="appName"></span> is ideal for on-line presentations, lectures, and tech support sessions.
</div>
<div class="feature_icon" data-i18n="welcomepage.feature5.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature5.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
</div>
</div>
<div class="feature_holder">
<div class="feature_icon">Secure rooms</div>
<div class="feature_description">
Need some privacy? <span name="appName"></span> conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions.
</div>
<div class="feature_icon" data-i18n="welcomepage.feature6.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature6.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
</div>
</div>
<div class="feature_holder">
<div class="feature_icon">Shared notes</div>
<div class="feature_description">
<span name="appName"></span> features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more.
</div>
<div class="feature_icon" data-i18n="welcomepage.feature7.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature7.content" data-i18n-options='{ "postProcess": "resolveAppName" }'></div>
</div>
<div class="feature_holder">
<div class="feature_icon">Usage statistics</div>
<div class="feature_description">
Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems.
</div>
<div class="feature_icon" data-i18n="welcomepage.feature8.title" ></div>
<div class="feature_description" data-i18n="welcomepage.feature8.content"></div>
</div>
</div>
</div>
@ -131,36 +121,36 @@
<div style="position: relative;" id="header_container">
<div id="header">
<span id="toolbar">
<a class="button" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" content="Mute / Unmute">
<a class="button" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute">
<i id="mute" class="icon-microphone"></i>
</a>
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" content="Start / stop camera">
<a class="button" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera">
<i id="video" class="icon-camera"></i>
</a>
<span id="authentication" style="display: none">
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_authentication" data-container="body" data-toggle="popover" data-placement="bottom" content="Authenticate">
<a class="button" id="toolbar_button_authentication" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.authenticate" content="Authenticate">
<i id="authButton" class="icon-avatar"></i>
</a>
</span>
<span id="recording" style="display: none">
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" content="Record">
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record">
<i id="recordButton" class="icon-recEnable"></i>
</a>
</span>
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" content="Lock / unlock room">
<a class="button" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room">
<i id="lockIcon" class="icon-security"></i>
</a>
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" content="Invite others">
<a class="button" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others">
<i class="icon-link"></i>
</a>
<div class="header_button_separator"></div>
<span class="toolbar_span">
<a class="button" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" content="Open / close chat">
<a class="button" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
<i id="chatButton" class="icon-chat">
<span id="unreadMessages"></span>
</i>
@ -168,38 +158,38 @@
</span>
<span id="prezi_button">
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" content="Share Prezi">
<a class="button" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.prezi" content="Share Prezi">
<i class="icon-prezi"></i>
</a>
</span>
<span id="etherpadButton">
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document">
<a class="button" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad">
<i class="icon-share-doc"></i>
</a>
</span>
<div class="header_button_separator"></div>
<span id="desktopsharing" style="display: none">
<a class="button" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen">
<a class="button" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen" data-i18n="[content]toolbar.sharescreen">
<i class="icon-share-desktop"></i>
</a>
</span>
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen">
<a class="button" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen">
<i id="fullScreen" class="icon-full-screen"></i>
</a>
<span id="sipCallButton" style="display: none">
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number">
<a class="button" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip">
<i class="icon-telephone"></i></a>
</span>
<div class="header_button_separator"></div>
<a class="button" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" >
<a class="button" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" data-i18n="[content]toolbar.Settings">
<i id="settingsButton" class="icon-settings"></i>
</a>
<div class="header_button_separator"></div>
<span id="hangup">
<a class="button" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up">
<a class="button" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up" data-i18n="[content]toolbar.hangup">
<i class="icon-hangup" style="color:#ff0000;font-size: 1.4em;"></i>
</a>
</span>
@ -208,7 +198,7 @@
<div id="subject"></div>
</div>
<div id="settings">
<h1>Connection Settings</h1>
<h1 data-i18n="connectionsettings"></h1>
<form id="loginInfo">
<label>JID: <input id="jid" type="text" name="jid" placeholder="me@example.com"/></label>
<label>Password: <input id="password" type="password" name="password" placeholder="secret"/></label>
@ -223,7 +213,7 @@
<div id="etherpad"></div>
<a target="_new"><div class="watermark leftwatermark"></div></a>
<a target="_new"><div class="watermark rightwatermark"></div></a>
<a class="poweredby" href="http://jitsi.org" target="_new" >powered by jitsi.org</a>
<a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
<div id="activeSpeaker">
<img id="activeSpeakerAvatar" src=""/>
<canvas id="activeSpeakerAudioLevel"></canvas>
@ -238,18 +228,13 @@
</span>
<audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio>
<span class="focusindicator"></span>
<!--<div class="connectionindicator">
<span class="connection connection_empty"><i class="icon-connection"></i></span>
<span class="connection connection_full"><i class="icon-connection"></i></span>
</div>-->
</span>
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
</div>
<span id="bottomToolbar">
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" content="Open / close chat">
<a class="bottomToolbarButton" id="bottom_toolbar_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" data-i18n="[content]bottomtoolbar.chat" content="Open / close chat">
<i id="chatBottomButton" class="icon-chat-simple">
<span id="bottomUnreadMessages"></span>
</i>
@ -257,7 +242,7 @@
</span>
<div class="bottom_button_separator"></div>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="top" id="contactlistpopover" content="Open / close contact list">
<a class="bottomToolbarButton" id="bottom_toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="top" id="contactlistpopover" data-i18n="[content]bottomtoolbar.contactlist" content="Open / close contact list">
<i id="contactListButton" class="icon-contactList">
<span id="numberOfParticipants"></span>
</i>
@ -265,7 +250,7 @@
</span>
<div class="bottom_button_separator"></div>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" content="Show / hide film strip">
<a class="bottomToolbarButton" id="bottom_toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" data-i18n="[content]bottomtoolbar.filmstrip" content="Show / hide film strip">
<i id="filmStripButton" class="icon-filmstrip"></i>
</a>
</span>
@ -273,16 +258,16 @@
</div>
<div id="chatspace" class="right-panel">
<div id="nickname">
Enter a nickname in the box below
<span data-i18n="chat.nickname.title"></span>
<form>
<input type='text' id="nickinput" placeholder='Choose a nickname' autofocus>
<input type='text' id="nickinput" data-i18n="[placeholder]chat.nickname.popover" autofocus>
</form>
</div>
<!--div><i class="fa fa-comments">&nbsp;</i><span class='nick'></span>:&nbsp;<span class='chattext'></span></div-->
<div id="chatconversation"></div>
<audio id="chatNotification" src="sounds/incomingMessage.wav" preload="auto"></audio>
<textarea id="usermsg" placeholder='Enter text...' autofocus></textarea>
<textarea id="usermsg" data-i18n="[placeholder]chat.messagebox" autofocus></textarea>
<div id="smileysarea">
<div id="smileys" id="toggle_smileys">
<img src="images/smile.svg"/>
@ -291,18 +276,18 @@
</div>
<div id="contactlist" class="right-panel">
<ul>
<li class="title"><i class="icon-contact-list"></i> CONTACT LIST</li>
<li class="title"><i class="icon-contact-list"></i><span data-i18n="contactlist"></span></li>
</ul>
</div>
<div id="settingsmenu" class="right-panel">
<div class="icon-settings"> SETTINGS</div>
<div class="icon-settings" data-i18n="settings.title"></div>
<img id="avatar" src="https://www.gravatar.com/avatar/87291c37c25be69a072a4514931b1749?d=wavatar&size=30"/>
<div class="arrow-up"></div>
<input type="text" id="setDisplayName" placeholder="Name">
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
<input type="text" id="setEmail" placeholder="E-Mail">
<button id="updateSettings">Update</button>
<button id="updateSettings" data-i18n="settings.update"></button>
</div>
<a id="downloadlog" onclick='dump(event.target);' data-container="body" data-toggle="popover" data-placement="right" data-content="Download logs" ><i class="fa fa-cloud-download"></i></a>
<a id="downloadlog" onclick='dump(event.target);' data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
</div>
</body>
</html>

4
lang/languages.json Normal file
View File

@ -0,0 +1,4 @@
{
"en": "English",
"bg": "Bulgarian"
}

120
lang/main.json Normal file
View File

@ -0,0 +1,120 @@
{
"contactlist": "CONTACT LIST",
"connectionsettings": "Connection Settings",
"poweredby": "powered by",
"downloadlogs": "Download logs",
"welcomepage":{
"go": "GO",
"roomname": "Enter room name",
"disable": "Don't show this page next time I enter",
"feature1": {
"title": "Simple to use",
"content": "No downloads required. __app__ works directly within your browser. Simply share your conference URL with others to get started."
},
"feature2": {
"title": "Low bandwidth",
"content": "Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less."
},
"feature3": {
"title": "Open source",
"content": "__app__ is licensed under MIT. You are free to download, use, modify, and share them as per these licenses."
},
"feature4": {
"title": "Unlimited users",
"content": "There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors."
},
"feature5": {
"title": "Screen sharing",
"content": "It's easy to share your screen with others. __app__ is ideal for on-line presentations, lectures, and tech support sessions."
},
"feature6": {
"title": "Secure rooms",
"content": "Need some privacy? __app__ conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions."
},
"feature7": {
"title": "Shared notes",
"content": "__app__ features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more."
},
"feature8": {
"title": "Usage statistics",
"content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems."
}
},
"toolbar": {
"mute": "Mute / Unmute",
"videomute": "Start / stop camera",
"authenticate": "Authenticate",
"record": "Record",
"lock": "Lock / unlock room",
"invite": "Invite others",
"chat": "Open / close chat",
"prezi": "Share Prezi",
"etherpad": "Shared document",
"sharescreen": "Share screen",
"fullscreen": "Enter / Exit Full Screen",
"sip": "Call SIP number",
"Settings": "Settings",
"hangup": "Hang Up"
},
"bottomtoolbar": {
"chat": "Open / close chat",
"filmstrip": "Open / close contact list",
"contactlist": "Show / hide film strip"
},
"chat":{
"nickname": {
"title": "Enter a nickname in the box below",
"popover": "Choose a nickname"
},
"messagebox": "Enter text..."
},
"settings":
{
"title": "SETTINGS",
"update": "Update",
"name": "Name"
},
"videothumbnail":
{
"editnickname": "Click to edit your<br/>display name",
"moderator": "The owner of<br/>this conference",
"videomute": "Participant has<br/>stopped the camera.",
"mute": "Participant is muted",
"kick": "Kick out",
"muted": "Muted",
"domute": "Mute"
},
"connectionindicator":
{
"bitrate": "Bitrate:",
"packetloss": "Packet loss: ",
"resolution": "Resolution:",
"less": "Show less",
"more": "Show more",
"address": "Address:",
"remoteports": "Remote ports:",
"localports": "Local ports:",
"remoteport": "Remote port:",
"localport": "Local port:",
"localaddress": "Local address: ",
"localaddresses": "Local addresses: ",
"remoteaddress": "Remote address: ",
"remoteaddresses": "Remote addresses: ",
"transport": "Transport: ",
"bandwidth": "Estimated bandwidth:",
"na": "Come back here for connection information once the conference starts"
},
"notify": {
"disconnected": "disconnected",
"moderator": "Moderator rights granted !",
"connected": "connected"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -340,15 +340,7 @@ UI.start = function (init) {
"newestOnTop": false
};
$('#settingsmenu>input').keyup(function(event){
if(event.keyCode === 13) {//enter
SettingsMenu.update();
}
});
$("#updateSettings").click(function () {
SettingsMenu.update();
});
SettingsMenu.init();
};

View File

@ -1,10 +1,43 @@
var Avatar = require("../../avatar/Avatar");
var Settings = require("./Settings");
var UIUtil = require("../../util/UIUtil");
var languages = require("../../../../service/translation/languages");
function generateLanguagesSelectBox()
{
var currentLang = APP.translation.getCurrentLanguage();
var html = "<select id=\"languages_selectbox\">";
var langArray = languages.getLanguages();
for(var i = 0; i < langArray.length; i++)
{
var lang = langArray[i];
html += "<option ";
if(lang === currentLang)
html += "selected ";
html += "value=\"" + lang + "\" data-i18n='languages:" + lang + "'>";
html += "</option>";
}
return html + "</select>";
}
var SettingsMenu = {
init: function () {
$("#updateSettings").before(generateLanguagesSelectBox());
$('#settingsmenu>input').keyup(function(event){
if(event.keyCode === 13) {//enter
SettingsMenu.update();
}
});
$("#updateSettings").click(function () {
SettingsMenu.update();
});
},
update: function() {
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
@ -14,6 +47,7 @@ var SettingsMenu = {
APP.xmpp.addToPresence("displayName", displayName, true);
}
APP.translation.setLanguage($("#languages_selectbox").val());
APP.xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(newEmail);

View File

@ -69,8 +69,8 @@ module.exports = {
context.putImageData(imgData, 0, 0);
},
setTooltip: function (element, tooltipText, position) {
element.setAttribute("data-content", tooltipText);
setTooltip: function (element, key, position) {
element.setAttribute("data-i18n", "[data-content]" + key);
element.setAttribute("data-toggle", "popover");
element.setAttribute("data-placement", position);
element.setAttribute("data-html", true);

View File

@ -64,6 +64,8 @@ ConnectionIndicator.getStringFromArray = function (array) {
ConnectionIndicator.prototype.generateText = function () {
var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
var translate = APP.translation.translateString;
if(this.bitrate === null)
{
downloadBitrate = "N/A";
@ -145,22 +147,28 @@ ConnectionIndicator.prototype.generateText = function () {
var result = "<table style='width:100%'>" +
"<tr>" +
"<td><span class='jitsipopover_blue'>Bitrate:</span></td>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.bitrate'>" +
translate("connectionindicator.bitrate") + "</span></td>" +
"<td><span class='jitsipopover_green'>&darr;</span>" +
downloadBitrate + " <span class='jitsipopover_orange'>&uarr;</span>" +
uploadBitrate + "</td>" +
"</tr><tr>" +
"<td><span class='jitsipopover_blue'>Packet loss: </span></td>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.packetloss'>" +
translate("connectionindicator.packetloss") + "</span></td>" +
"<td>" + packetLoss + "</td>" +
"</tr><tr>" +
"<td><span class='jitsipopover_blue'>Resolution:</span></td>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.resolution'>" +
translate("connectionindicator.resolution") + "</span></td>" +
"<td>" + resolution + "</td></tr></table>";
if(this.videoContainer.id == "localVideoContainer")
if(this.videoContainer.id == "localVideoContainer") {
result += "<div class=\"jitsipopover_showmore\" " +
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
this.videoContainer.id + "')\">" +
(this.showMoreValue? "Show less" : "Show More") + "</div><br />";
this.videoContainer.id + "')\" data-i18n='connectionindicator." +
(this.showMoreValue ? "less" : "more") + "'>" +
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
"</div><br />";
}
if(this.showMoreValue)
{
@ -183,7 +191,9 @@ ConnectionIndicator.prototype.generateText = function () {
if(!this.transport || this.transport.length === 0)
{
transport = "<tr>" +
"<td><span class='jitsipopover_blue'>Address:</span></td>" +
"<td><span class='jitsipopover_blue' " +
"data-i18n='connectionindicator.address'>" +
translate("connectionindicator.address") + "</span></td>" +
"<td> N/A</td></tr>";
}
else
@ -218,41 +228,49 @@ ConnectionIndicator.prototype.generateText = function () {
}
}
var local_address_key = "connectionindicator." +
(data.localIP.length > 1? "localaddresses" : "localaddress");
var remote_address_key = "connectionindicator." +
(data.remoteIP.length > 1? "remoteaddresses" : "remoteaddress");
var localTransport =
"<tr><td><span class='jitsipopover_blue'>Local address" +
(data.localIP.length > 1? "es" : "") + ": </span></td><td> " +
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
local_address_key +"'>" +
translate(local_address_key) + "</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.localIP) +
"</td></tr>";
transport =
"<tr><td><span class='jitsipopover_blue'>Remote address"+
(data.remoteIP.length > 1? "es" : "") + ":</span></td><td> " +
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
remote_address_key + "'>" +
translate(remote_address_key) + "</span></td><td> " +
ConnectionIndicator.getStringFromArray(data.remoteIP) +
"</td></tr>";
var key_remote = "connectionindicator.",
key_local = "connectionindicator.";
if(this.transport.length > 1)
{
transport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue'>Remote ports:</span>" +
"</td><td>";
localTransport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue'>Local ports:</span>" +
"</td><td>";
key_remote += "remoteports";
key_local += "localports";
}
else
{
transport +=
"<tr>" +
"<td>" +
"<span class='jitsipopover_blue'>Remote port:</span>" +
"</td><td>";
localTransport +=
"<tr>" +
"<td>" +
"<span class='jitsipopover_blue'>Local port:</span>" +
"</td><td>";
key_remote += "remoteport";
key_local += "localport";
}
transport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue' data-i18n='" + key_remote +
"'>" +
translate(key_remote) + "</span></td><td>";
localTransport += "<tr>" +
"<td>" +
"<span class='jitsipopover_blue' data-i18n='" + key_local +
"'>" +
translate(key_local) + "</span></td><td>";
transport +=
ConnectionIndicator.getStringFromArray(data.remotePort);
localTransport +=
@ -260,7 +278,8 @@ ConnectionIndicator.prototype.generateText = function () {
transport += "</td></tr>";
transport += localTransport + "</td></tr>";
transport +="<tr>" +
"<td><span class='jitsipopover_blue'>Transport:</span></td>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.transport'>" +
translate("connectionindicator.transport") + "</span></td>" +
"<td>" + this.transport[0].type + "</td></tr>";
}
@ -268,7 +287,8 @@ ConnectionIndicator.prototype.generateText = function () {
result += "<table style='width:100%'>" +
"<tr>" +
"<td>" +
"<span class='jitsipopover_blue'>Estimated bandwidth:</span>" +
"<span class='jitsipopover_blue' data-i18n='connectionindicator.bandwidth'>" +
translate("connectionindicator.bandwidth") + "</span>" +
"</td><td>" +
"<span class='jitsipopover_green'>&darr;</span>" +
downloadBandwidth +
@ -313,8 +333,8 @@ ConnectionIndicator.prototype.create = function () {
this.videoContainer.appendChild(this.connectionIndicatorContainer);
this.popover = new JitsiPopover(
$("#" + this.videoContainer.id + " > .connectionindicator"),
{content: "<div class=\"connection_info\">Come back here for " +
"connection information once the conference starts</div>",
{content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
APP.translation.translateString("connectionindicator.na") + "</div>",
skin: "black"});
this.emptyIcon = this.connectionIndicatorContainer.appendChild(
@ -388,7 +408,8 @@ ConnectionIndicator.prototype.updateResolution = function (resolution) {
*/
ConnectionIndicator.prototype.updatePopoverData = function () {
this.popover.updateContent(
"<div class=\"connection_info\">" + this.generateText() + "</div>");
"<div class=\"connection_info\">" + this.generateText() + "</div>");
APP.translation.translateElement($(".connection_info"));
};
/**

View File

@ -235,6 +235,8 @@ function setDisplayName(videoSpanId, displayName) {
} else {
nameSpan.id = 'localDisplayName';
$('#' + videoSpanId)[0].appendChild(editButton);
//translates popover of edit button
APP.translation.translateElement($("a.displayname"));
var editableText = document.createElement('input');
editableText.className = 'displayname';
@ -345,14 +347,16 @@ function addRemoteVideoMenu(jid, parentElement) {
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
if (!mutedAudios[jid]) {
muteLinkItem.innerHTML = mutedIndicator + 'Mute';
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
muteLinkItem.innerHTML = mutedIndicator + ' Muted';
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
@ -366,11 +370,13 @@ function addRemoteVideoMenu(jid, parentElement) {
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator + ' Muted';
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
}
else {
this.innerHTML = mutedIndicator + ' Mute';
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
}
};
@ -378,11 +384,12 @@ function addRemoteVideoMenu(jid, parentElement) {
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i class='fa fa-eject'></i>";
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
ejectLinkItem.innerHTML = ejectIndicator + ' Kick out';
var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(jid);
popupmenuElement.setAttribute('style', 'display:none;');
@ -394,6 +401,7 @@ function addRemoteVideoMenu(jid, parentElement) {
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement($("#" + popupmenuElement.id + " > li > a > div"));
}
/**
@ -468,7 +476,7 @@ function createEditDisplayNameButton() {
var editButton = document.createElement('a');
editButton.className = 'displayname';
UIUtil.setTooltip(editButton,
'Click to edit your<br/>display name',
"videothumbnail.editnickname",
"top");
editButton.innerHTML = '<i class="fa fa-pencil"></i>';
@ -487,7 +495,7 @@ function createModeratorIndicatorElement(parentElement) {
parentElement.appendChild(moderatorIndicator);
UIUtil.setTooltip(parentElement,
"The owner of<br/>this conference",
"videothumbnail.moderator",
"top");
}
@ -1043,13 +1051,11 @@ var VideoLayout = (function (my) {
container.id = spanId;
container.className = 'videocontainer';
var remotes = document.getElementById('remoteVideos');
remotes.appendChild(container);
// If the peerJid is null then this video span couldn't be directly
// associated with a participant (this could happen in the case of prezi).
if (APP.xmpp.isModerator() && peerJid !== null)
addRemoteVideoMenu(peerJid, container);
remotes.appendChild(container);
AudioLevels.updateAudioLevelCanvas(peerJid, VideoLayout);
return container;
@ -1335,6 +1341,8 @@ var VideoLayout = (function (my) {
if (indicatorSpan.children().length === 0)
{
createModeratorIndicatorElement(indicatorSpan[0]);
//translates text in focus indicator
APP.translation.translateElement($('#localVideoContainer .focusindicator'));
}
}
@ -1375,6 +1383,8 @@ var VideoLayout = (function (my) {
videoContainer.appendChild(indicatorSpan);
createModeratorIndicatorElement(indicatorSpan);
//translates text in focus indicators
APP.translation.translateElement($('#' + videoSpanId + ' .focusindicator'));
}
} else if (isModerator) {
// We are moderator, but user is not - add menu
@ -1408,9 +1418,11 @@ var VideoLayout = (function (my) {
var mutedIndicator = document.createElement('i');
mutedIndicator.className = 'icon-camera-disabled';
UIUtil.setTooltip(mutedIndicator,
"Participant has<br/>stopped the camera.",
"videothumbnail.videomute",
"top");
videoMutedSpan.appendChild(mutedIndicator);
//translate texts for muted indicator
APP.translation.translateElement($('#' + videoSpanId + " > i"));
}
VideoLayout.updateMutePosition(videoSpanId);
@ -1451,10 +1463,11 @@ var VideoLayout = (function (my) {
audioMutedSpan = document.createElement('span');
audioMutedSpan.className = 'audioMuted';
UIUtil.setTooltip(audioMutedSpan,
"Participant is muted",
"videothumbnail.mute",
"top");
$('#' + videoSpanId)[0].appendChild(audioMutedSpan);
APP.translation.translateElement($('#' + videoSpanId + " > span"));
var mutedIndicator = document.createElement('i');
mutedIndicator.className = 'icon-mic-disabled';
audioMutedSpan.appendChild(mutedIndicator);

View File

@ -37,8 +37,6 @@ function setupWelcomePage()
$("#videoconference_page").hide();
$("#domain_name").text(
window.location.protocol + "//" + window.location.host + "/");
$("span[name='appName']").text(interfaceConfig.APP_NAME);
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
var leftWatermarkDiv
= $("#welcome_page_header div[class='watermark leftwatermark']");

View File

@ -0,0 +1,110 @@
var i18n = require("i18next-client");
var languages = require("../../service/translation/languages");
var DEFAULT_LANG = languages.EN;
var initialized = false;
var waitingForInit = [];
i18n.addPostProcessor("resolveAppName", function(value, key, options) {
return value.replace("__app__", interfaceConfig.APP_NAME);
});
var defaultOptions = {
detectLngQS: "lang",
useCookie: false,
fallbackLng: DEFAULT_LANG,
load: "unspecific",
resGetPath: 'lang/__ns__-__lng__.json',
ns: {
namespaces: ['main', 'languages'],
defaultNs: 'main'
},
lngWhitelist : languages.getLanguages(),
fallbackOnNull: true,
useDataAttrOptions: true,
app: interfaceConfig.APP_NAME,
getAsync: true,
customLoad: function(lng, ns, options, done) {
var resPath = "lang/__ns__-__lng__.json";
if(lng === languages.EN)
resPath = "lang/__ns__.json";
var url = i18n.functions.applyReplacement(resPath, { lng: lng, ns: ns });
initialized = false;
i18n.functions.ajax({
url: url,
success: function(data, status, xhr) {
i18n.functions.log('loaded: ' + url);
done(null, data);
},
error : function(xhr, status, error) {
if ((status && status == 200) ||
(xhr && xhr.status && xhr.status == 200)) {
// file loaded but invalid json, stop waste time !
i18n.functions.error('There is a typo in: ' + url);
} else if ((status && status == 404) ||
(xhr && xhr.status && xhr.status == 404)) {
i18n.functions.log('Does not exist: ' + url);
} else {
var theStatus = status ? status :
((xhr && xhr.status) ? xhr.status : null);
i18n.functions.log(theStatus + ' when loading ' + url);
}
done(error, {});
},
dataType: "json",
async : options.getAsync
});
}
// options for caching
// useLocalStorage: true,
// localStorageExpirationTime: 86400000 // in ms, default 1 week
};
function initCompleted(t)
{
initialized = true;
$("[data-i18n]").i18n();
for(var i = 0; i < waitingForInit.length; i++)
{
var obj = waitingForInit[i];
obj.callback(i18n.t(obj.key));
}
waitingForInit = [];
}
module.exports = {
init: function (lang) {
initialized = false;
var options = defaultOptions;
if(lang)
options.lng = lang;
i18n.init(options, initCompleted);
},
translateString: function (key, cb) {
if(!cb)
return i18n.t(key);
if(initialized)
{
cb(i18n.t(key));
}
else
{
waitingForInit.push({"callback": cb, "key": key});
}
},
setLanguage: function (lang) {
initialized = false;
if(!lang)
lang = DEFAULT_LANG;
i18n.setLng(lang, defaultOptions, initCompleted);
},
getCurrentLanguage: function () {
return i18n.lng();
},
translateElement: function (selector) {
selector.i18n();
}
};

View File

@ -16,7 +16,8 @@
"readmeFilename": "README.md",
"dependencies": {
"events": "*",
"pako": "*"
"pako": "*",
"i18next-client": "*"
},
"devDependencies": {
},

View File

@ -0,0 +1,12 @@
module.exports = {
getLanguages : function () {
var languages = [];
for(var lang in this)
{
if(typeof this[lang] === "string")
languages.push(this[lang]);
}
return languages;
},
EN: "en"
}