feat: translation result

This commit is contained in:
2026-01-14 15:51:15 +01:00
parent 9170157dbb
commit d8b6f1ad25
8 changed files with 370 additions and 175 deletions

View File

@@ -1,12 +1,14 @@
import i18next from 'https://cdn.jsdelivr.net/npm/i18next@25.7.4/+esm';
import i18nextHttpBackend from 'https://cdn.jsdelivr.net/npm/i18next-http-backend@3.0.2/+esm'
import i18nextBrowserLanguagedetector from 'https://cdn.jsdelivr.net/npm/i18next-browser-languagedetector@8.2.0/+esm'
let apiUrlRoot = "";
const rootDiv = document.getElementById("safca_api_data");
const header = `<h4>Résultat de la compétition :</h4>`
const backButton = `<a onclick="setSubPage('home')" href="javascript:void(0);">Retour</a>`
const cupImg = `<img decoding="async" loading="lazy" width="16" height="16" class="wp-image-1635"
style="width: 16px;" src="https://intra.ffsaf.fr/img/171891.png"
alt="">`
const voidFunction = () => {
}
let lastRf = 0;
@@ -41,18 +43,23 @@ function setSubPage(name) {
}
function homePage() {
rootDiv.innerHTML = header;
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4>`;
let content = document.createElement('div');
content.innerHTML = `
<ul>
<li><a onclick="setSubPage('poule')" href="javascript:void(0);">Par catégorie</a></li>
<li><a onclick="setSubPage('comb')" href="javascript:void(0);">Par combattant</a></li>
<li><a onclick="setSubPage('club')" href="javascript:void(0);">Par club</a></li>
<li><a onclick="setSubPage('all')" href="javascript:void(0);">Tous les combattants</a></li>
<li><a id="pouleLink" href="javascript:void(0);">${i18next.t('parCatégorie')}</a></li>
<li><a id="combLink" href="javascript:void(0);">${i18next.t('parCombattant')}</a></li>
<li><a id="clubLink" href="javascript:void(0);">${i18next.t('parClub')}</a></li>
<li><a id="allLink" href="javascript:void(0);">${i18next.t('tousLesCombattants')}</a></li>
</ul>
`
rootDiv.append(content)
document.getElementById('pouleLink').addEventListener('click', () => setSubPage('poule'));
document.getElementById('combLink').addEventListener('click', () => setSubPage('comb'));
document.getElementById('clubLink').addEventListener('click', () => setSubPage('club'));
document.getElementById('allLink').addEventListener('click', () => setSubPage('all'));
}
let loadingAnimationStep = 0;
@@ -63,7 +70,7 @@ function startLoading(root) {
element.id = id;
const anim = setInterval(() => {
let str = "Chargement";
let str = i18next.t('chargement');
for (let i = 0; i < loadingAnimationStep; i++) {
str += ".";
}
@@ -84,11 +91,11 @@ function scoreToString(score) {
const scorePrint = (s1) => {
switch (s1) {
case -997:
return "disc.";
return i18next.t('disc.');
case -998:
return "abs.";
return i18next.t('abs.');
case -999:
return "for.";
return i18next.t('for.');
case -1000:
return "";
default:
@@ -111,11 +118,11 @@ function dateToString(date) {
let d = Math.floor((current - date_2) / (1000 * 60 * 60 * 24));
if (d === 0)
return "Aujourd'hui à " + date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});
return i18next.t('aujourdhuià', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else if (d === 1)
return "Hier à " + date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});
return i18next.t('hierà', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else if (d === 2)
return "Avant-hier à " + date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});
return i18next.t('avanthierà', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else
return date_.toLocaleDateString();
}
@@ -175,11 +182,11 @@ function buildPouleMenu(isPoule, change_view) {
return li;
}
const li1 = createTab('Poule', isPoule, function () {
const li1 = createTab(i18next.t('poule'), isPoule, function () {
change_view(true);
});
ul.appendChild(li1);
const li2 = createTab('Tournois', !isPoule, function () {
const li2 = createTab(i18next.t('tournois'), !isPoule, function () {
change_view(false);
});
ul.appendChild(li2);
@@ -195,12 +202,12 @@ function buildMatchArray(matchs) {
<figure class="wp-block-table is-style-stripes" style="font-size: 16px">
<table style="width: 800px;overflow: auto"><thead>
<tr>
<th class="has-text-align-center" data-align="center">Rouge</th>
<th class="has-text-align-center" data-align="center">${i18next.t('rouge')}</th>
<th class="has-text-align-center" data-align="center"></th>
<th class="has-text-align-center" data-align="center">Scores</th>
<th class="has-text-align-center" data-align="center">${i18next.t('scores')}</th>
<th class="has-text-align-center" data-align="center"></th>
<th class="has-text-align-center" data-align="center">Bleu</th>
<th class="has-text-align-center" data-align="center">Date</th>
<th class="has-text-align-center" data-align="center">${i18next.t('bleu')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('date')}</th>
</tr>
</thead><tbody>`
for (const match of matchs) {
@@ -225,12 +232,12 @@ function buildRankArray(rankArray) {
<table style="width: 600px;overflow: auto">
<thead>
<tr>
<th class="has-text-align-center" data-align="center">Place</th>
<th class="has-text-align-center" data-align="center">Nom</th>
<th class="has-text-align-center" data-align="center">Victoire</th>
<th class="has-text-align-center" data-align="center">Ratio</th>
<th class="has-text-align-center" data-align="center">Points marqués</th>
<th class="has-text-align-center" data-align="center">Points reçus</th>
<th class="has-text-align-center" data-align="center">${i18next.t('place')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('nom')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('victoire')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratio')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsMarqués')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsReçus')}</th>
</tr>
</thead><tbody>`
for (const row of rankArray) {
@@ -255,10 +262,12 @@ function buildTree(treeData) {
}
function poulePage(location) {
rootDiv.innerHTML = header + backButton;
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4><a id="homeLink" href="javascript:void(0);">${i18next.t('back')}</a>`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = '<h4>Recherche par catégorie</h4>';
content.innerHTML = `<h4>${i18next.t('rechercheParCatégorie')}</h4>`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
@@ -286,7 +295,7 @@ function poulePage(location) {
for (const g in poule.matchs) {
if (Object.keys(poule.matchs).length > 1) {
const text = document.createElement('h4');
text.textContent = `Groupe ${g}`;
text.textContent = `${i18next.t('poule')} ${g}`;
text.style.marginTop = '2em';
dataContainer.append(text);
}
@@ -303,7 +312,7 @@ function poulePage(location) {
for (const g in poule.matchs) {
if (Object.keys(poule.matchs).length > 1) {
const text = document.createElement('h4');
text.textContent = `Groupe ${g}`;
text.textContent = `${i18next.t('poule')} ${g}`;
text.style.marginTop = '2em';
dataContainer.append(text);
}
@@ -323,7 +332,7 @@ function poulePage(location) {
})
.catch(e => {
console.error(e);
dataContainer.replaceChildren(new Text("Erreur de chargement de la poule"));
dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaPoule')));
})
.finally(() => stopLoading(loading));
}
@@ -334,7 +343,7 @@ function poulePage(location) {
.then(poule => {
const select = document.createElement('select');
select.setAttribute('id', poule.id);
select.innerHTML = `<option value="0">--Sélectionner une catégorie--</option>`;
select.innerHTML = `<option value="0">${i18next.t('--sélectionnerUneCatégorie--')}</option>`;
for (const pouleKey of Object.keys(poule).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))) {
select.innerHTML += `<option value="${poule[pouleKey]}">${pouleKey}</option>`;
}
@@ -356,7 +365,7 @@ function poulePage(location) {
loadPoule();
}
})
.catch(() => rootDiv.append(new Text("Erreur de chargement des catégories")))
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesCatégories'))))
.finally(() => stopLoading(loading));
rfFonction = () => {
@@ -372,28 +381,33 @@ function buildCombView(comb) {
let arrayContent = `
<h3>Info :</h3>
<ul>
<li>Nom Pr&eacute;nom : ${comb.name}</li>
<li>Club : ${comb.club}</li>
<li>Cat&eacute;gorie : ${comb.cat}</li>
<li>${i18next.t('nomPrénom')} : ${comb.name}</li>
<li>${i18next.t('club')} : ${comb.club}</li>
<li>${i18next.t('catégorie')} : ${comb.cat}</li>
</ul>
<h3>Statistique :</h3>
<h3>${i18next.t('statistique')} :</h3>
<ul>
<li>Taux de victoire : ${comb.matchs.length === 0 ? "---" : (comb.totalWin / comb.matchs.length * 100).toFixed(0)}% (${comb.totalWin} sur ${comb.matchs.length})</li>
<li>Points marqués : ${comb.pointMake}</li>
<li>Points reçus : ${comb.pointTake}</li>
<li>Ratio du score (point marqu&eacute; / point re&ccedil;u): ${comb.pointRatio.toFixed(3)}</li>
<li>${i18next.t('tauxDeVictoire2', {
nb: comb.matchs.length === 0 ? "---" : (comb.totalWin / comb.matchs.length * 100).toFixed(0),
victoires: comb.totalWin,
matchs: comb.matchs.length
})}
</li>
<li>${i18next.t('pointsMarqués2', {nb: comb.pointMake})}</li>
<li>${i18next.t('pointsReçus2', {nb: comb.pointTake})}</li>
<li>${i18next.t('ratioDuScore2', {nb: comb.pointRatio.toFixed(3)})}</li>
</ul>
<h3>Liste des matchs:</h3>
<h3>${i18next.t('listeDesMatchs')}:</h3>
<figure class="wp-block-table is-style-stripes" style="font-size: 16px">
<table style="width: 700px;overflow: auto"><thead>
<tr>
<th class="has-text-align-center" data-align="center">Date</th>
<th class="has-text-align-center" data-align="center">Poule</th>
<th class="has-text-align-center" data-align="center">Adversaire</th>
<th class="has-text-align-center" data-align="center">Scores</th>
<th class="has-text-align-center" data-align="center">Ratio</th>
<th class="has-text-align-center" data-align="center">${i18next.t('date')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('poule')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('adversaire')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('scores')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratio')}</th>
<th class="has-text-align-center" data-align="center"></th>
</tr>
</thead><tbody>`
@@ -414,10 +428,12 @@ function buildCombView(comb) {
}
function combPage(location) {
rootDiv.innerHTML = header + backButton;
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4><a id="homeLink" href="javascript:void(0);">${i18next.t('back')}</a>`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = '<h4>Recherche par combattant</h4>';
content.innerHTML = `<h4>${i18next.t('rechercheParCombattant')}</h4>`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
@@ -430,7 +446,7 @@ function combPage(location) {
console.log(comb);
dataContainer.replaceChildren(buildCombView(comb));
})
.catch(() => dataContainer.replaceChildren(new Text("Erreur de chargement du combattant")))
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDuCombattant'))))
.finally(() => stopLoading(loading));
}
@@ -439,7 +455,7 @@ function combPage(location) {
.then(response => response.json())
.then(combs => {
const select = document.createElement('select');
select.innerHTML = `<option value="0">--Sélectionner un combattant--</option>`;
select.innerHTML = `<option value="0">${i18next.t('--sélectionnerUnCombattant--')}</option>`;
for (const comb of Object.keys(combs).sort()) {
select.innerHTML += `<option value="${combs[comb]}">${comb}</option>`;
}
@@ -458,7 +474,7 @@ function combPage(location) {
loadComb(tmp);
}
})
.catch(() => rootDiv.append(new Text("Erreur de chargement des combattants")))
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesCombattants'))))
.finally(() => stopLoading(loading));
rootDiv.append(content)
@@ -467,34 +483,34 @@ function combPage(location) {
function buildClubView(club) {
const pouleDiv = document.createElement('div');
let arrayContent = `
<h3>Info :</h3>
<h3>${i18next.t('info')} :</h3>
<ul>
<li>Nom : ${club.name}</li>
<li>Nombre d'inscris : ${club.nb_insc}</li>
<li>${i18next.t('nom')} : ${club.name}</li>
<li>${i18next.t('nombreDinscris')} : ${club.nb_insc}</li>
</ul>
<h3>Statistique :</h3>
<h3>${i18next.t('statistique')} :</h3>
<ul>
<li>Nombre de match disput&eacute; : ${club.nb_match}</li>
<li>Nombre de victoires : ${club.match_w}</li>
<li>Ratio de victoires moyen : ${club.ratioVictoire.toFixed(3)}</li>
<li>Points marqués : ${club.pointMake}</li>
<li>Points reçus : ${club.pointTake}</li>
<li>Ratio de points moyen : ${club.ratioPoint.toFixed(3)}</li>
<li>${i18next.t('nombreDeMatchDisputé2', {nb: club.nb_match})}</li>
<li>${i18next.t('nombreDeVictoires2', {nb: club.match_w})}</li>
<li>${i18next.t('ratioDeVictoiresMoyen2', {nb: club.ratioVictoire.toFixed(3)})}</li>
<li>${i18next.t('pointsMarqués2', {nb: club.pointMake})}</li>
<li>${i18next.t('pointsReçus2', {nb: club.pointTake})}</li>
<li>${i18next.t('ratioDePointsMoyen2', {nb: club.ratioPoint.toFixed(3)})}</li>
</ul>
<h3>Liste des menbres :</h3>
<h3>${i18next.t('listeDesMenbres')} :</h3>
<figure class="wp-block-table is-style-stripes" style="font-size: 16px">
<table style="width: 800px;overflow: auto"><thead>
<tr>
<th class="has-text-align-center" data-align="center">Cat&eacute;gorie</th>
<th class="has-text-align-center" data-align="center">Nom</th>
<th class="has-text-align-center" data-align="center">Victoires</th>
<th class="has-text-align-center" data-align="center">D&eacute;faites</th>
<th class="has-text-align-center" data-align="center">Ratio victoires</th>
<th class="has-text-align-center" data-align="center">Points marqués</th>
<th class="has-text-align-center" data-align="center">Points reçus</th>
<th class="has-text-align-center" data-align="center">Ratio points</th>
<th class="has-text-align-center" data-align="center">${i18next.t('catégorie')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('nom')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('victoires')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('défaites')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratioVictoires')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsMarqués')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsReçus')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratioPoints')}</th>
</tr>
</thead><tbody>`
for (const comb of club.combs) {
@@ -516,10 +532,12 @@ function buildClubView(club) {
}
function clubPage(location) {
rootDiv.innerHTML = header + backButton;
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4><a id="homeLink" href="javascript:void(0);">${i18next.t('back')}</a>`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = '<h4>Recherche par club</h4>';
content.innerHTML = `<h4>${i18next.t('rechercheParClub')}</h4>`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
@@ -532,7 +550,7 @@ function clubPage(location) {
console.log(club);
dataContainer.replaceChildren(buildClubView(club));
})
.catch(() => dataContainer.replaceChildren(new Text("Erreur de chargement du club")))
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDuClub'))))
.finally(() => stopLoading(loading));
}
@@ -541,7 +559,7 @@ function clubPage(location) {
.then(response => response.json())
.then(clubs => {
const select = document.createElement('select');
select.innerHTML = `<option value="0">--Sélectionner un club--</option>`;
select.innerHTML = `<option value="0">${i18next.t('--sélectionnerUnClub--')}</option>`;
for (const club of Object.keys(clubs).sort()) {
select.innerHTML += `<option value="${clubs[club]}">${club}</option>`;
}
@@ -562,7 +580,7 @@ function clubPage(location) {
loadComb(tmp);
}
})
.catch(() => rootDiv.append(new Text("Erreur de chargement des clubs")))
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesClubs'))))
.finally(() => stopLoading(loading));
rootDiv.append(content)
@@ -571,27 +589,27 @@ function clubPage(location) {
function buildCombsView(combs) {
const pouleDiv = document.createElement('div');
let arrayContent = `
<h3>Statistique :</h3>
<h3>${i18next.t('statistique')} :</h3>
<ul>
<li>Nombre d'inscris : ${combs.nb_insc}</li>
<li>Nombre de match disput&eacute; : ${combs.tt_match}</li>
<li>Points marqués : ${combs.point}</li>
<li>${i18next.t('nombreDinscris2', {nb: combs.nb_insc})}</li>
<li>${i18next.t('nombreDeMatchDisputé2', {nb: combs.tt_match})}</li>
<li>${i18next.t('pointsMarqués2', {nb: combs.point})}</li>
</ul>
<h3>Liste des combattants :</h3>
<h3>${i18next.t('listeDesCombattants')} :</h3>
<figure class="wp-block-table is-style-stripes" style="font-size: 16px">
<table style="width: 1200px;overflow: auto"><thead>
<tr>
<th class="has-text-align-center" data-align="center">Cat&eacute;gorie</th>
<th class="has-text-align-center" data-align="center">Club</th>
<th class="has-text-align-center" data-align="center">Nom</th>
<th class="has-text-align-center" data-align="center">Victoires</th>
<th class="has-text-align-center" data-align="center">D&eacute;faites</th>
<th class="has-text-align-center" data-align="center">Ratio victoires</th>
<th class="has-text-align-center" data-align="center">Points marqués</th>
<th class="has-text-align-center" data-align="center">Points reçus</th>
<th class="has-text-align-center" data-align="center">Ratios points</th>
<th class="has-text-align-center" data-align="center">${i18next.t('catégorie')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('club')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('nom')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('victoires')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('défaites')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratioVictoires')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsMarqués')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('pointsReçus')}</th>
<th class="has-text-align-center" data-align="center">${i18next.t('ratiosPoints')}</th>
</tr>
</thead><tbody>`
for (const comb of combs.combs) {
@@ -614,7 +632,9 @@ function buildCombsView(combs) {
}
function combsPage() {
rootDiv.innerHTML = header + backButton;
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4><a id="homeLink" href="javascript:void(0);">${i18next.t('back')}</a>`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
@@ -628,26 +648,44 @@ function combsPage() {
console.log(combs);
dataContainer.replaceChildren(buildCombsView(combs));
})
.catch(() => dataContainer.replaceChildren(new Text("Erreur de chargement de la liste")))
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaListe'))))
.finally(() => stopLoading(loading));
content.append(dataContainer);
rootDiv.append(content)
}
window.addEventListener("load", () => {
let path = document.getElementById('safca_api_script').src;
const urlParams = new URLSearchParams(new URL(path).search);
apiUrlRoot = path.substring(0, path.lastIndexOf('/')) + "/api/public/result/" + urlParams.get("id");
export async function initCompetitionApi(apiUrlRoot_) {
apiUrlRoot = apiUrlRoot_;
const options = {
order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
caches: [],
}
await i18next
.use(i18nextHttpBackend)
.use(i18nextBrowserLanguagedetector)
.init({
fallbackLng: 'fr',
debug: true,
interpolation: {
escapeValue: true,
},
detection: options,
ns: ['result'],
defaultNS: 'result',
})
console.log("apiUrlRoot:", apiUrlRoot)
console.log("FFSAF Competition API initialized.")
let hash = window.location.hash.substring(1);
if (hash.length === 0)
setSubPage('home');
else
setSubPage(hash);
});
}
class TreeNode {
constructor(data) {
@@ -671,7 +709,7 @@ class TreeNode {
}
function initTree(data_in) {
out = [];
let out = [];
for (const din of data_in) {
out.push(parseTree(din));
}
@@ -818,7 +856,7 @@ function drawGraph(root = []) {
ctx.textBaseline = 'top';
for (let i = 0; i < scores.length; i++) {
const score = scores[i].s1+"-"+scores[i].s2;
const score = scores[i].s1 + "-" + scores[i].s2;
const div = (scores.length <= 2) ? 2 : (scores.length >= 4) ? 4 : 3;
const text = ctx.measureText(score);
let dx = (size * 2 - text.width) / 2;