Add i18n selector

This commit is contained in:
Samuel Ortion 2024-01-17 12:13:08 +01:00
parent 3e28cb35e9
commit f6bfa6bce5
7 changed files with 73 additions and 27 deletions

View File

@ -1 +1,17 @@
{}
{
"Game": "Game",
"About": "About",
"It was a": "It was a",
"Language": "Language",
"Submit": "Submit",
"Geolocalize yourself on the map": "Geolocalize yourself on the map",
"Start the game": "Start the game",
"Start a new quizz": "Start a new quizz",
"Return to the map": "Return to the map",
"Wrong!": "Wrong!",
"About SoundBirder": "About SoundBirder",
"SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.": "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.",
"Author": "Author",
"The project is made with ♥ by Samuel Ortion.": "The project is made with ♥ by Samuel Ortion.",
"Correct!": "Correct!"
}

View File

@ -1,12 +1,18 @@
{
"Game": "Jeu",
"About": "À propos",
"Contact": "Contact",
"Author": "Auteur",
"The project is made with ♥ by Samuel Ortion": "Ce projet est fait avec ♥ par Samuel Ortion",
"Correct!": "Correct !",
"Wrong!": "Incorrect !",
"It was a": "C'était un",
"SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.": "SoundBirder est une application open-source pour apprendre les chant d'oiseaux. Elle est basée sur les enregistrements de Xeno-Canto et sur les données d'eBird",
"The project is made with ♥ by Samuel Ortion.": "Ce projet est fait avec ♥ par Samuel Ortion."
"Game": "Jeu",
"About": "À propos",
"Contact": "Contact",
"Author": "Auteur",
"The project is made with ♥ by Samuel Ortion": "Ce projet est fait avec ♥ par Samuel Ortion",
"Correct!": "Correct !",
"Wrong!": "Incorrect !",
"It was a": "C'était un",
"SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.": "SoundBirder est une application open-source pour apprendre les chant d'oiseaux. Elle est basée sur les enregistrements de Xeno-Canto et sur les données d'eBird",
"The project is made with ♥ by Samuel Ortion.": "Ce projet est fait avec ♥ par Samuel Ortion.",
"Language": "Language",
"Submit": "Submit",
"Geolocalize yourself on the map": "Geolocalize yourself on the map",
"Start the game": "Start the game",
"Start a new quizz": "Start a new quizz",
"Return to the map": "Return to the map"
}

View File

@ -8,7 +8,7 @@ let gameQuizzStep = document.querySelector('.game-quizz-step');
let gameQuizz = document.querySelector('.game-quizz');
let gameResultStep = document.querySelector('.game-results-step');
let gameLoading = document.querySelector('.game-loading');
let audio = document.querySelector('.game-quizz-step audio');
let audio = document.querySelector('.game-audio audio');
let proposals = document.querySelector('.game-quizz-step .proposals');
let result = document.querySelector('.game-results-step .message');
let restartButton = document.querySelector('.game-quizz .restart-button');
@ -45,6 +45,7 @@ function regionCoder() {
function quizzStep() {
gameQuizz.classList.remove("none");
gameLoading.classList.remove("none");
audio.classList.add("none");
client.getQuizz(region)
.then(quizz => {
gameLoading.classList.add("none");
@ -66,6 +67,10 @@ function displayQuizz(quizz) {
audio.play();
proposals.innerHTML = "";
quizz.species.forEach(sp => {
if (sp.speciesCode == undefined) {
quizzStep();
return
}
let proposal = document.createElement('li');
proposal.classList.add('proposal');
let button = document.createElement('button');
@ -81,6 +86,7 @@ function displayQuizz(quizz) {
function verifyAnswer(event) {
let answer = event.target.value;
console.log(answer);
audio.pause();
client.checkAnswer(answer)
.then(data => {
@ -99,7 +105,6 @@ mapButton.addEventListener('click', returnToMap);
function displayResult(message_class, message, species) {
gameQuizzStep.classList.toggle('none');
audio.classList.add("none");
result.classList.remove('success', 'error');
result.classList.add(message_class);
result.innerText = message;

6
public/javascripts/lang.js Executable file
View File

@ -0,0 +1,6 @@
let languageSelectorButton = document.getElementById('language-selector-button');
let languageSelector = document.getElementById('language-selector');
languageSelectorButton.addEventListener('click', function () {
let code = languageSelector.value;
window.location = '/' + code;
});

View File

@ -1,27 +1,28 @@
.game
.game-map-step
#map.h-100
button.button.geolocation-button
button.button.geolocation-button(aria-label=__('Geolocalize yourself on the map'))
i(data-feather="map-pin")
button.button.start-button
button.button.start-button(aria-label=__('Start the game'))
i(data-feather="play")
.game-quizz.none
.game-loading
<svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg>
.game-quizz-step.none
ul.proposals
.game-audio
.game-quizz-step.none
ul.proposals
.game-results-step.none
p.message
.species.answer
p #{ __('It was a') }
span.com
span.sci
audio(controls).none
.game-results-step.none
p.message
.species.answer
p #{ __('It was a') }
span.com
span.sci
button.button.restart-button
button.button.restart-button(aria-label=__('Start a new quizz'))
i(data-feather="repeat")
button.button.map-button
button.button.map-button(aria-label=__('Return to the map'))
i(data-feather="map")
link(rel="stylesheet" href="/dist/leaflet/leaflet.css")
link(rel="stylesheet" href="/stylesheets/spinner.css")

10
views/lang.pug Normal file
View File

@ -0,0 +1,10 @@
- var languages = {en: "English", fr: "Français"} // , de: "Deutsch", es: "Español"}
.flex.flex-row
.relative.h-10.w-10(class='min-w-[100px]')
select#language-selector.peer.h-full.w-full.border.border-blue-gray-200.border-t-transparent.bg-transparent.px-3.font-sans.text-sm.font-normal.text-blue-gray-700.outline.outline-0.transition-all(class='rounded-[7px] py-2.5 placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 empty:!bg-gray-900 focus:border-2 focus:border-gray-900 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50')
each language, code in languages
option.pointer-events-none.absolute.left-0.flex.h-full.w-full.select-none.font-normal.leading-tight.text-blue-gray-400.transition-all(class="before:content[' '] after:content[' '] -top-1.5 text-[11px] before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-gray-900 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-gray-900 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-gray-900 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500" value=code) #{ language }
label.pointer-events-none.absolute.left-0.flex.h-full.w-full.select-none.font-normal.leading-tight.text-blue-gray-400.transition-all(class="before:content[' '] after:content[' '] -top-1.5 text-[11px] before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-md after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-gray-900 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:border-gray-900 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:border-gray-900 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500")
| #{ __('Language') }
button#language-selector-button.p-1.btn #{ __('Submit') }

View File

@ -10,13 +10,14 @@ html
body
.flex.flex-col.min-h-screen
.flex-1.p-5
nav
nav(role="navigation")
- var i18n_prefix = locale ? '/' + locale : ''
ul.flex.flex-row.text-center.justify-evenly
li
a(href=`${i18n_prefix}/`) #{ __('Game') }
li
a(href=`${i18n_prefix}/about`) #{ __('About') }
include lang.pug
header
h1= title
main
@ -29,4 +30,5 @@ html
span.author
a(href="https://samuel.ortion.fr" class="link") Samuel Ortion
script(src="/javascripts/app.js" type="module")
script(src="/dist/feather/feather.min.js")
script(src="/dist/feather/feather.min.js")
script(src="/javascripts/lang.js")