mirror of
https://github.com/jech/galene.git
synced 2024-11-23 00:55:58 +01:00
feat: i18n
This commit is contained in:
parent
3bf0f9ef4c
commit
fe7c8cad8f
6 changed files with 319 additions and 73 deletions
|
@ -19,7 +19,7 @@
|
|||
<div class="row full-height">
|
||||
<nav id="left-sidebar">
|
||||
<div class="users-header">
|
||||
<div class="galene-header">Galène</div>
|
||||
<div class="galene-header" data-i18n="app"></div>
|
||||
</div>
|
||||
<div class="header-sep"></div>
|
||||
<div id="users"></div>
|
||||
|
@ -28,7 +28,7 @@
|
|||
<header>
|
||||
<nav class="topnav navbar navbar-expand navbar-light fixed-top">
|
||||
<div id="header">
|
||||
<div class="collapse" title="Collapse left panel" id="sidebarCollapse">
|
||||
<div class="collapse" data-i18n-title="collapse-left-panel" id="sidebarCollapse">
|
||||
<svg class="svg-inline--fa" aria-hidden="true" data-icon="align-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path fill="currentColor" d="M288 44v40c0 8.837-7.163 16-16 16H16c-8.837 0-16-7.163-16-16V44c0-8.837 7.163-16 16-16h256c8.837 0 16 7.163 16 16zM0 172v40c0 8.837 7.163 16 16 16h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16zm16 312h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm256-200H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16h256c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16z"></path>
|
||||
</svg>
|
||||
|
@ -39,36 +39,36 @@
|
|||
<ul class="nav-menu">
|
||||
<li>
|
||||
<button id="presentbutton" class="invisible btn btn-success">
|
||||
<i class="fas fa-play" aria-hidden="true"></i><span class="nav-text"> Ready</span>
|
||||
<i class="fas fa-play" aria-hidden="true"></i><span class="nav-text" data-i18n="ready"> </span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="unpresentbutton" class="invisible btn btn-cancel">
|
||||
<i class="fas fa-stop" aria-hidden="true"></i><span class="nav-text"> Panic</span>
|
||||
<i class="fas fa-stop" aria-hidden="true"></i><span class="nav-text" data-i18n="panic"> </span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<div id="mutebutton" class="nav-link nav-button">
|
||||
<span><i class="fas fa-microphone-slash" aria-hidden="true"></i></span>
|
||||
<label>Mute</label>
|
||||
<label data-i18n="mute"></label>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div id="sharebutton" class="invisible nav-link nav-button">
|
||||
<span><i class="fas fa-share-square" aria-hidden="true"></i></span>
|
||||
<label>Share Screen</label>
|
||||
<label data-i18n="share-screen"></label>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div id="unsharebutton" class="invisible nav-link nav-button nav-cancel">
|
||||
<span><i class="fas fa-window-close" aria-hidden="true"></i></span>
|
||||
<label>Unshare Screen</label>
|
||||
<label data-i18n="unshare-screen"></label>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div id="stopvideobutton" class="invisible nav-link nav-button nav-cancel">
|
||||
<span><i class="fas fa-window-close" aria-hidden="true"></i></span>
|
||||
<label>Stop Video</label>
|
||||
<label data-i18n="stop-video"></label>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -83,7 +83,7 @@
|
|||
<div class="coln-left" id="left">
|
||||
<div id="chat">
|
||||
<div id="chatbox">
|
||||
<div class="close-chat" id="close-chat" title="Hide chat">
|
||||
<div class="close-chat" id="close-chat" data-i18n-title="hide-chat">
|
||||
<span class="close-icon"></span>
|
||||
</div>
|
||||
<div id="box"></div>
|
||||
|
@ -100,7 +100,7 @@
|
|||
<div class="coln-right" id="right">
|
||||
<span class="show-video blink" id="switch-video"><i class="fas fa-exchange" aria-hidden="true"></i></span>
|
||||
<div class="collapse-video" id="collapse-video">
|
||||
<i class="far fa-comment-alt open-chat" title="Open chat"></i>
|
||||
<i class="far fa-comment-alt open-chat" data-i18n-title="open-chat"></i>
|
||||
</div>
|
||||
<div class="video-container no-video" id="video-container">
|
||||
<div id="expand-video" class="expand-video">
|
||||
|
@ -110,30 +110,30 @@
|
|||
<div class="login-container invisible" id="login-container">
|
||||
<div class="login-box">
|
||||
<form id="userform" class="userform">
|
||||
<label for="username">Username</label>
|
||||
<label for="username" data-i18n="username"></label>
|
||||
<input id="username" type="text" name="username"
|
||||
autocomplete="username" class="form-control"/>
|
||||
<label for="password">Password</label>
|
||||
<label for="password" data-i18n="password"></label>
|
||||
<input id="password" type="password" name="password"
|
||||
autocomplete="current-password" class="form-control"/>
|
||||
<label>Auto ready</label>
|
||||
<label data-i18n="auto-ready"></label>
|
||||
<div class="present-switch">
|
||||
<p class="switch-radio">
|
||||
<input id="presentoff" type="radio" name="presentradio" value="" checked/>
|
||||
<label for="presentoff">Disabled</label>
|
||||
<label for="presentoff" data-i18n="disabled"></label>
|
||||
</p>
|
||||
<p class="switch-radio">
|
||||
<input id="presentmike" type="radio" name="presentradio" value="mike"/>
|
||||
<label for="presentmike">Enable microphone</label>
|
||||
<label for="presentmike" data-i18n="enable-micro"></label>
|
||||
</p>
|
||||
<p class="switch-radio">
|
||||
<input id="presentboth" type="radio" name="presentradio" value="both"/>
|
||||
<label for="presentboth">Enable camera and microphone</label>
|
||||
<label for="presentboth" data-i18n="enable-cam-micro"></label>
|
||||
</p>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="connect">
|
||||
<input id="connectbutton" type="submit" class="btn btn-blue" value="Connect"/>
|
||||
<input id="connectbutton" type="submit" class="btn btn-blue" data-i18n-value="connect"/>
|
||||
</div>
|
||||
</form>
|
||||
<div class="clear"></div>
|
||||
|
@ -147,7 +147,7 @@
|
|||
|
||||
<div id="sidebarnav" class="sidenav">
|
||||
<div class="sidenav-header">
|
||||
<h2>Settings</h2>
|
||||
<h2 data-i18n="settings"></h2>
|
||||
<a class="closebtn" id="clodeside"><i class="fas fa-times" aria-hidden="true"></i></a>
|
||||
</div>
|
||||
<div class="sidenav-content" id="optionsdiv">
|
||||
|
@ -163,76 +163,84 @@
|
|||
<div class="user-logout">
|
||||
<a id="disconnectbutton">
|
||||
<span class="logout-icon"><i class="fas fa-sign-out-alt"></i></span>
|
||||
<span class="logout-text">Logout</span>
|
||||
<span class="logout-text" data-i18n="logout"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mediaoptions" class="invisible">
|
||||
<fieldset>
|
||||
<legend>Media Options</legend>
|
||||
<label for="videoselect" class="sidenav-label-first">Camera:</label>
|
||||
<legend data-i18n="media-options"></legend>
|
||||
<label for="videoselect" class="sidenav-label-first" data-i18n="camera"></label>
|
||||
<select id="videoselect" class="select select-inline">
|
||||
<option value="">off</option>
|
||||
</select>
|
||||
|
||||
<label for="audioselect" class="sidenav-label">Microphone:</label>
|
||||
<label for="audioselect" class="sidenav-label" data-i18n="micro"></label>
|
||||
<select id="audioselect" class="select select-inline">
|
||||
<option value="">off</option>
|
||||
<option value="" data-i18n="off"></option>
|
||||
</select>
|
||||
|
||||
<form>
|
||||
<input id="mirrorbox" type="checkbox" checked/>
|
||||
<label for="mirrorbox">Mirror view</label>
|
||||
<label for="mirrorbox" data-i18n="mirror-view"></label>
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<input id="blackboardbox" type="checkbox"/>
|
||||
<label for="blackboardbox">Blackboard mode</label>
|
||||
<label for="blackboardbox" data-i18n="blackboard-mode"></label>
|
||||
</form>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Other Settings</legend>
|
||||
<legend data-i18n="other-settings"></legend>
|
||||
|
||||
<form id="filterform">
|
||||
<label for="filterselect" class="sidenav-label-first">Filter:</label>
|
||||
<label for="filterselect" class="sidenav-label-first" data-i18n="filter"></label>
|
||||
<select id="filterselect" class="select select-inline">
|
||||
<option value="" selected>none</option>
|
||||
<option value="" selected data-i18n="none"></option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<form id="sendform">
|
||||
<label for="sendselect" class="sidenav-label-first">Send:</label>
|
||||
<label for="sendselect" class="sidenav-label-first" data-i18n="send"></label>
|
||||
<select id="sendselect" class="select select-inline">
|
||||
<option value="lowest">lowest</option>
|
||||
<option value="low">low</option>
|
||||
<option value="normal" selected>normal</option>
|
||||
<option value="unlimited">unlimited</option>
|
||||
<option value="lowest" data-i18n="lowest"></option>
|
||||
<option value="low" data-i18n="low"></option>
|
||||
<option value="normal" selected data-i18n="normal"></option>
|
||||
<option value="unlimited" data-i18n="unlimited"></option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<form id="languageform">
|
||||
<label for="languageselect" class="sidenav-label" data-i18n="language"></label>
|
||||
<select id="languageselect" class="select select-inline">
|
||||
<option value="fr" data-i18n="fr">FR</option>
|
||||
<option value="en" data-i18n="en">EN</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<form id="requestform">
|
||||
<label for="requestselect" class="sidenav-label">Receive:</label>
|
||||
<label for="requestselect" class="sidenav-label" data-i18n="receive"></label>
|
||||
<select id="requestselect" class="select select-inline">
|
||||
<option value="">nothing</option>
|
||||
<option value="audio">audio only</option>
|
||||
<option value="screenshare">screen share</option>
|
||||
<option value="everything" selected>everything</option>
|
||||
<option value="" data-i18n="nothing"></option>
|
||||
<option value="audio" data-i18n="audio-only"></option>
|
||||
<option value="screenshare" data-i18n="screen-share"></option>
|
||||
<option value="everything" selected data-i18n="everything"></option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<input id="activitybox" type="checkbox"/>
|
||||
<label for="activitybox">Activity detection</label>
|
||||
<label for="activitybox" data-i18n="activity-detection"></label>
|
||||
</form>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<form id="fileform">
|
||||
<label for="fileinput" class=".sidenav-label-first">Play local file:</label>
|
||||
<label for="fileinput" class=".sidenav-label-first" data-i18n="play-local-file"></label>
|
||||
<input type="file" id="fileinput" accept="audio/*,video/*" multiple/>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -241,19 +249,19 @@
|
|||
<div id="videocontrols-template" class="invisible">
|
||||
<div class="video-controls vc-overlay">
|
||||
<div class="controls-button controls-left">
|
||||
<span class="video-play" title="Play video">
|
||||
<span class="video-play" data-i18n-title="play-video">
|
||||
<i class="fas fa-play"></i>
|
||||
</span>
|
||||
<span class="volume" title="Volume">
|
||||
<span class="volume" data-i18n-title="volume">
|
||||
<i class="fas fa-volume-up volume-mute" aria-hidden="true"></i>
|
||||
<input class="volume-slider" type="range" max="100" value="100" min="0" step="5" >
|
||||
</span>
|
||||
</div>
|
||||
<div class="controls-button controls-right">
|
||||
<span class="pip" title="Picture In Picture">
|
||||
<span class="pip" data-i18n-title="picture-in-picture">
|
||||
<i class="far fa-clone" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="fullscreen" title="Fullscreen">
|
||||
<span class="fullscreen" data-i18n-title="Fullscreen">
|
||||
<i class="fas fa-expand" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -262,6 +270,7 @@
|
|||
|
||||
<script src="/protocol.js" defer></script>
|
||||
<script src="/scripts/toastify.js" defer></script>
|
||||
<script src="/translations/translation.js" defer></script>
|
||||
<script src="/galene.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -39,6 +39,20 @@ let serverConnection;
|
|||
*/
|
||||
let fallbackUserPass = null;
|
||||
|
||||
/** @type {Translation} */
|
||||
let trans = new Translation();
|
||||
|
||||
/**
|
||||
* Change language
|
||||
*/
|
||||
async function changeLanguage(){
|
||||
const element = document.getElementById('languageselect');
|
||||
await element.addEventListener('change', async function() {
|
||||
const language = element.options[element.selectedIndex].value;
|
||||
trans.selectLanguage(language);
|
||||
await trans.analyse();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} username
|
||||
|
@ -308,7 +322,7 @@ function setConnected(connected) {
|
|||
fillLogin();
|
||||
userbox.classList.add('invisible');
|
||||
connectionbox.classList.remove('invisible');
|
||||
displayError('Disconnected', 'error');
|
||||
displayError(trans.get('disconnected'), 'error');
|
||||
hideVideo();
|
||||
closeVideoControls();
|
||||
}
|
||||
|
@ -344,7 +358,7 @@ function gotDownStream(c) {
|
|||
};
|
||||
c.onerror = function(e) {
|
||||
console.error(e);
|
||||
displayError(e);
|
||||
displayError(e.toString());
|
||||
};
|
||||
c.ondowntrack = function(track, transceiver, label, stream) {
|
||||
setMedia(c, false);
|
||||
|
@ -602,7 +616,7 @@ getInputElement('fileinput').onchange = function(e) {
|
|||
for(let i = 0; i < files.length; i++) {
|
||||
addFileMedia(files[i]).catch(e => {
|
||||
console.error(e);
|
||||
displayError(e);
|
||||
displayError(e.toString());
|
||||
});
|
||||
}
|
||||
input.value = '';
|
||||
|
@ -795,7 +809,7 @@ function newUpStream(localId) {
|
|||
};
|
||||
c.onerror = function(e) {
|
||||
console.error(e);
|
||||
displayError(e);
|
||||
displayError(e.toString());
|
||||
};
|
||||
c.onnegotiationcompleted = function() {
|
||||
setMaxVideoThroughput(c, getMaxVideoThroughput());
|
||||
|
@ -1037,7 +1051,7 @@ async function addLocalMedia(localId) {
|
|||
if(settings.filter) {
|
||||
filter = filters[settings.filter];
|
||||
if(!filter) {
|
||||
displayWarning(`Unknown filter ${settings.filter}`);
|
||||
displayWarning(trans.get('unknown-filter') + settings.filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1190,7 +1204,7 @@ async function addFileMedia(file) {
|
|||
/** @ts-ignore */
|
||||
stream = video.mozCaptureStream();
|
||||
else {
|
||||
displayError("This browser doesn't support file playback");
|
||||
displayError(trans.get('playback-unsupported'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1230,7 @@ async function addFileMedia(file) {
|
|||
let muted = getSettings().localMute;
|
||||
if(presenting && !muted) {
|
||||
setLocalMute(true, true);
|
||||
displayWarning('You have been muted');
|
||||
displayWarning(trans.get('you-muted'));
|
||||
}
|
||||
}
|
||||
c.pc.addTrack(t, stream);
|
||||
|
@ -1517,7 +1531,7 @@ function registerControlHandlers(media, container) {
|
|||
/** @ts-ignore */
|
||||
media.requestPictureInPicture();
|
||||
} else {
|
||||
displayWarning('Picture in Picture not supported.');
|
||||
displayWarning(trans.get('pic-in-pic-unsupported'));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
@ -1539,7 +1553,7 @@ function registerControlHandlers(media, container) {
|
|||
/** @ts-ignore */
|
||||
media.webkitRequestFullscreen();
|
||||
} else {
|
||||
displayWarning('Full screen not supported!');
|
||||
displayWarning(trans.get('fullscreen-unsupported'));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
@ -1776,7 +1790,7 @@ async function gotJoined(kind, group, perms, message) {
|
|||
|
||||
switch(kind) {
|
||||
case 'fail':
|
||||
displayError('The server said: ' + message);
|
||||
displayError(trans.get('server-say') + message);
|
||||
this.close();
|
||||
setButtonsVisibility();
|
||||
return;
|
||||
|
@ -1796,18 +1810,18 @@ async function gotJoined(kind, group, perms, message) {
|
|||
return;
|
||||
break;
|
||||
default:
|
||||
displayError('Unknown join message');
|
||||
displayError(trans.get('unknown-join-message'));
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let input = /** @type{HTMLTextAreaElement} */
|
||||
(document.getElementById('input'));
|
||||
input.placeholder = 'Type /help for help';
|
||||
input.placeholder = trans.get('type-help');
|
||||
setTimeout(() => {input.placeholder = '';}, 8000);
|
||||
|
||||
if(typeof RTCPeerConnection === 'undefined')
|
||||
displayWarning("This browser doesn't support WebRTC");
|
||||
displayWarning(trans.get('webrtc-unsupported'));
|
||||
else
|
||||
this.request(getSettings().request);
|
||||
|
||||
|
@ -1827,9 +1841,7 @@ async function gotJoined(kind, group, perms, message) {
|
|||
button.disabled = false;
|
||||
}
|
||||
} else {
|
||||
displayMessage(
|
||||
"Press Ready to enable your camera or microphone"
|
||||
);
|
||||
displayMessage(trans.get('press-ready'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1850,7 +1862,7 @@ function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
|||
case 'info':
|
||||
let from = id ? (username || 'Anonymous') : 'The Server';
|
||||
if(privileged)
|
||||
displayError(`${from} said: ${message}`, kind);
|
||||
displayError(`${from} ${trans.get('said')} ${message}`, kind);
|
||||
else
|
||||
console.error(`Got unprivileged message of kind ${kind}`);
|
||||
break;
|
||||
|
@ -1858,7 +1870,7 @@ function gotUserMessage(id, dest, username, time, privileged, kind, message) {
|
|||
if(privileged) {
|
||||
setLocalMute(true, true);
|
||||
let by = username ? ' by ' + username : '';
|
||||
displayWarning(`You have been muted${by}`);
|
||||
displayWarning(trans.get('you-muted') + by);
|
||||
} else {
|
||||
console.error(`Got unprivileged message of kind ${kind}`);
|
||||
}
|
||||
|
@ -2460,7 +2472,7 @@ function handleInput() {
|
|||
} else {
|
||||
let c = commands[cmd];
|
||||
if(!c) {
|
||||
displayError(`Uknown command /${cmd}, type /help for help`);
|
||||
displayError( trans.get('unknown-command')+ cmd + trans.get('type-help'));
|
||||
return;
|
||||
}
|
||||
if(c.predicate) {
|
||||
|
@ -2484,7 +2496,7 @@ function handleInput() {
|
|||
}
|
||||
|
||||
if(!serverConnection || !serverConnection.socket) {
|
||||
displayError("Not connected.");
|
||||
displayError(trans.get("not-connected"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2547,7 +2559,7 @@ function chatResizer(e) {
|
|||
document.getElementById('resizer').addEventListener('mousedown', chatResizer, false);
|
||||
|
||||
/**
|
||||
* @param {unknown} message
|
||||
* @param {string} message
|
||||
* @param {string} [level]
|
||||
*/
|
||||
function displayError(message, level) {
|
||||
|
@ -2582,14 +2594,14 @@ function displayError(message, level) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} message
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayWarning(message) {
|
||||
return displayError(message, "warning");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} message
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayMessage(message) {
|
||||
return displayError(message, "info");
|
||||
|
@ -2708,11 +2720,14 @@ async function serverConnect() {
|
|||
await serverConnection.connect(url);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
displayError(e.message ? e.message : "Couldn't connect to " + url);
|
||||
displayError(e.message ? e.message : trans.get('can-t-connect') + url);
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
async function start() {
|
||||
await trans.analyse();
|
||||
await changeLanguage();
|
||||
|
||||
group = decodeURIComponent(location.pathname.replace(/^\/[a-z]*\//, ''));
|
||||
let title = group.charAt(0).toUpperCase() + group.slice(1);
|
||||
if(group !== '') {
|
||||
|
|
64
static/translations/i18n.en.json
Normal file
64
static/translations/i18n.en.json
Normal file
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"app": "Galène",
|
||||
"ready": " Ready",
|
||||
"panic": "Panic",
|
||||
"mute": "Mute",
|
||||
"share-screen": "Share Screen",
|
||||
"unshare Screen": "Unshare Screen",
|
||||
"stop-video": "Stop Video",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"connect": "Connect",
|
||||
"auto-ready": "Auto ready",
|
||||
"disabled": "Disabled",
|
||||
"enable-micro": "Enable microphone",
|
||||
"enable-cam-micro": "Enable camera and microphone",
|
||||
"settings": "Settings",
|
||||
"logout": "Logout",
|
||||
"media-options": "Media Options",
|
||||
"camera": "Camera:",
|
||||
"micro": "Microphone:",
|
||||
"off": "off",
|
||||
"mirror-view": "Mirror view",
|
||||
"blackboard-mode": "Blackboard mode",
|
||||
"other-settings": "Other Settings",
|
||||
"language": "Language:",
|
||||
"filter": "Filter:",
|
||||
"none": "none",
|
||||
"send": "Send:",
|
||||
"lowest": "lowest",
|
||||
"low": "low",
|
||||
"normal": "normal",
|
||||
"unlimited": "unlimited",
|
||||
"receive:": "Receive:",
|
||||
"nothing": "nothing",
|
||||
"audio-only": "audio only",
|
||||
"screen-share1": "screen share",
|
||||
"everything": "everything",
|
||||
"activity-detection": "Activity detection",
|
||||
"play-local-file": "Play local file:",
|
||||
|
||||
"collapse-left-panel": "Collapse left panel",
|
||||
"hide-chat": "Hide chat",
|
||||
"open-chat": "Open chat",
|
||||
"play-video": "Play video",
|
||||
"volume": "Volume",
|
||||
"pic-in-pic": "Picture in Picture",
|
||||
"fullscreen": "Fullscreen",
|
||||
|
||||
"not-connected": "Not connected.",
|
||||
"disconnected": "Disconnected",
|
||||
"can-t-connect": "Couldn't connect to ",
|
||||
"unknown-filter": "Unknown filter ",
|
||||
"playback-unsupported": "This browser doesn't support file playback",
|
||||
"you-muted": "You have been muted",
|
||||
"pic-in-pic-unsupported": "Picture in Picture not supported.",
|
||||
"fullscreen-unsupported": "Full screen not supported!",
|
||||
"serve-say": "The server said: ",
|
||||
"unknown-join-message": "Unknown join message",
|
||||
"webrtc-unsupported": "This browser doesn't support WebRTC",
|
||||
"type-help": "Type /help for help",
|
||||
"press-ready": "Press Ready to enable your camera or microphone",
|
||||
"said": "said: ",
|
||||
"unknown-command ": "Unknown command "
|
||||
}
|
65
static/translations/i18n.fr.json
Normal file
65
static/translations/i18n.fr.json
Normal file
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"app": "Galène",
|
||||
"ready": " Prêt",
|
||||
"panic": "Panic",
|
||||
"mute": "Muet",
|
||||
"share-screen": "Partage d'écran",
|
||||
"unshare Screen": "Arrêter le partage d'écran",
|
||||
"stop-video": "Arrêter la video",
|
||||
"username": "Pseudo",
|
||||
"password": "Mot de passe",
|
||||
"connect": "Connexion",
|
||||
"auto-ready": "Préparation automatique",
|
||||
"disabled": "Désactiver",
|
||||
"enable-micro": "Activer le microphone",
|
||||
"enable-cam-micro": "Activer la caméra et microphone",
|
||||
"settings": "Paramètres",
|
||||
"logout": "Déconnexion",
|
||||
"media-options": "Options Media",
|
||||
"camera": "Caméra:",
|
||||
"micro": "Microphone:",
|
||||
"off": "off",
|
||||
"mirror-view": "Vue en miroir",
|
||||
"blackboard-mode": "Mode tableau noir",
|
||||
"other-settings": "Autres paramètres",
|
||||
"language": "Langue:",
|
||||
"filter": "Filtre:",
|
||||
"none": "aucun",
|
||||
"send": "Envoyer:",
|
||||
"lowest": "le plus bas",
|
||||
"low": "faible",
|
||||
"normal": "normal",
|
||||
"unlimited": "illimité",
|
||||
"receive:": "Recevoir:",
|
||||
"nothing": "rien",
|
||||
"audio-only": "audio uniquement",
|
||||
"screen-share1": "partage d'écran",
|
||||
"everything": "tout",
|
||||
"activity-detection": "Détection d'activité",
|
||||
"play-local-file": "Lecture du fichier local :",
|
||||
|
||||
"collapse-left-panel": "Effondrement du panneau de gauche",
|
||||
"hide-chat": "Cacher le chat",
|
||||
"open-chat": "Chat ouvert",
|
||||
"play-video": "Lire la vidéo",
|
||||
"volume": "Volume",
|
||||
"pic-in-pic": "Image dans l'image",
|
||||
"fullscreen": "Plein écran",
|
||||
|
||||
|
||||
"not-connected": "Pas connecté.",
|
||||
"disconnected": "Déconnecté",
|
||||
"can-t-connect": "Pas possible de se connecter à ",
|
||||
"unknown-filter": "Filtre inconnu ",
|
||||
"playback-unsupported": "Ce navigateur ne prend pas en charge la lecture",
|
||||
"you-muted": "Vous avez été mis en sourdine",
|
||||
"pic-in-pic-unsupported": "L'image dans l'image n'est pas prise en charge.",
|
||||
"fullscreen-unsupported": "Le plein écran n'est pas pris en charge !",
|
||||
"serve-say": "Le serveur a dit: ",
|
||||
"unknown-join-message": "Message d'adhésion inconnu",
|
||||
"webrtc-unsupported": "Ce navigateur n'est pas compatible avec WebRTC",
|
||||
"type-help": "Écrire /help pour l'aide",
|
||||
"press-ready": "Appuyez sur Prêt pour activer votre caméra ou votre microphone",
|
||||
"said": "a dit: ",
|
||||
"unknown-command ": "Commande inconnue "
|
||||
}
|
92
static/translations/translation.js
Normal file
92
static/translations/translation.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function Translation() {
|
||||
/**
|
||||
* Default language
|
||||
* @type {string}
|
||||
*/
|
||||
this.default = 'en';
|
||||
/**
|
||||
* Used language
|
||||
* @type {string|string}
|
||||
*/
|
||||
this.language = this.default;
|
||||
/**
|
||||
* Available languages
|
||||
* @type {string[]}
|
||||
*/
|
||||
this.languages = ['en', 'fr'];
|
||||
/**
|
||||
* Json witch contains translations
|
||||
* @type {undefined}
|
||||
*/
|
||||
this.json = undefined;
|
||||
|
||||
this.selectLanguage(navigator.language);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
Translation.prototype.load = async function (){
|
||||
const url = window.location.origin + '/translations/i18n.' + this.language + '.json';
|
||||
return await (await fetch(url)).json();
|
||||
}
|
||||
/**
|
||||
* Analyse all HTML to remplace innerHTML with value linked to the key
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
Translation.prototype.analyse = async function (){
|
||||
this.json = await trans.load();
|
||||
document.querySelectorAll('[data-i18n]').forEach(item => {
|
||||
item.innerHTML = this.get(item.getAttribute('data-i18n'))
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(item => {
|
||||
item.title = this.get(item.getAttribute('data-i18n-title'))
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-value]').forEach(item => {
|
||||
item.value = this.get(item.getAttribute('data-i18n-value'))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key
|
||||
* @returns string
|
||||
*/
|
||||
Translation.prototype.get = function(key) {
|
||||
return key in this.json ? this.json[key] : key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Select an available language
|
||||
* @param language
|
||||
*/
|
||||
Translation.prototype.selectLanguage = function (language){
|
||||
this.language = this.languages.filter(l => l === language).length === 1 ? language : this.default;
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"files": [
|
||||
"protocol.js",
|
||||
"galene.js"
|
||||
"galene.js",
|
||||
"translations/translation.js"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue