added translation support

This commit is contained in:
Faraphel 2023-12-29 20:13:09 +01:00
parent 4616b632c5
commit b26a399e8a
9 changed files with 130 additions and 76 deletions

View file

@ -3,6 +3,7 @@ import sys
from PyQt6.QtCore import QTranslator, QLocale from PyQt6.QtCore import QTranslator, QLocale
from PyQt6.QtWidgets import QApplication from PyQt6.QtWidgets import QApplication
import translate
from source import assets_path from source import assets_path
from source.widget import MyMainWindow from source.widget import MyMainWindow
@ -13,7 +14,10 @@ if __name__ == "__main__":
# get the user language # get the user language
local = QLocale() local = QLocale()
language_code: str = local.languageToCode(local.language()) language_code: str = local.languageToCode(local.language(), QLocale.LanguageCodeType.ISO639)
# apply the language on the translator
translate.set_language(language_code)
# load the translator to support multiple languages # load the translator to support multiple languages
translator = QTranslator() translator = QTranslator()

View file

@ -4,13 +4,14 @@ from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QSpinBox from PyQt6.QtWidgets import QVBoxLayout, QLabel, QSpinBox
import translate
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
class IntegerQuestion(BaseSurvey): class IntegerQuestion(BaseSurvey):
def __init__( def __init__(
self, self,
title: str, title: translate.Translatable,
default: Optional[int] = None, default: Optional[int] = None,
minimum: Optional[int] = None, minimum: Optional[int] = None,
maximum: Optional[int] = None, maximum: Optional[int] = None,
@ -30,7 +31,7 @@ class IntegerQuestion(BaseSurvey):
# question title # question title
self.label_question = QLabel() self.label_question = QLabel()
self._layout.addWidget(self.label_question) self._layout.addWidget(self.label_question)
self.label_question.setText(title) self.label_question.setText(translate.translate(title))
self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_question.font() font_title = self.label_question.font()

View file

@ -4,17 +4,18 @@ from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QCheckBox, QLineEdit from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QCheckBox, QLineEdit
import translate
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
class MultipleChoiceQuestion(BaseSurvey): class MultipleChoiceQuestion(BaseSurvey):
def __init__( def __init__(
self, self,
title: str, title: translate.Translatable,
choices: dict[Any, str], choices: dict[Any, translate.Translatable],
details_choice_enabled: bool = None, details_choice_enabled: bool = None,
details_choice_id: str = None, details_choice_id: str = None,
details_choice_text: str = None, details_choice_text: translate.Translatable = None,
signals: dict[str, pyqtSignal] = None signals: dict[str, pyqtSignal] = None
): ):
super().__init__() super().__init__()
@ -31,7 +32,7 @@ class MultipleChoiceQuestion(BaseSurvey):
# question title # question title
self.label_question = QLabel() self.label_question = QLabel()
self._layout.addWidget(self.label_question) self._layout.addWidget(self.label_question)
self.label_question.setText(title) self.label_question.setText(translate.translate(title))
self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_question.font() font_title = self.label_question.font()
@ -51,7 +52,7 @@ class MultipleChoiceQuestion(BaseSurvey):
for choice_id, choice_text in choices.items(): for choice_id, choice_text in choices.items():
# create a radio button for that choice # create a radio button for that choice
button = QCheckBox() button = QCheckBox()
button.setText(choice_text) button.setText(translate.translate(choice_text))
# add the button to the frame # add the button to the frame
self._layout_responses.addWidget(button) self._layout_responses.addWidget(button)
@ -62,7 +63,7 @@ class MultipleChoiceQuestion(BaseSurvey):
if self.details_choice_enabled: if self.details_choice_enabled:
self.button_response_other = QCheckBox() self.button_response_other = QCheckBox()
self._layout_responses.addWidget(self.button_response_other) self._layout_responses.addWidget(self.button_response_other)
self.button_response_other.setText(self.details_choice_text) self.button_response_other.setText(translate.translate(self.details_choice_text))
self.button_response_other.toggled.connect(self._on_response_other_check) # NOQA: connect exist self.button_response_other.toggled.connect(self._on_response_other_check) # NOQA: connect exist
self.entry_response_other = QLineEdit() self.entry_response_other = QLineEdit()

View file

@ -4,17 +4,18 @@ from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QRadioButton, QButtonGroup, QLineEdit, QAbstractButton from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QRadioButton, QButtonGroup, QLineEdit, QAbstractButton
import translate
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
class SingleChoiceQuestion(BaseSurvey): class SingleChoiceQuestion(BaseSurvey):
def __init__( def __init__(
self, self,
title: str, title: translate.Translatable,
details_choice_enabled: bool = None, details_choice_enabled: bool = None,
details_choice_id: str = None, details_choice_id: str = None,
details_choice_text: str = None, details_choice_text: translate.Translatable = None,
choices: dict[Any, str] = None, choices: dict[Any, translate.Translatable] = None,
signals: dict[str, pyqtSignal] = None signals: dict[str, pyqtSignal] = None
): ):
super().__init__() super().__init__()
@ -33,7 +34,7 @@ class SingleChoiceQuestion(BaseSurvey):
# question title # question title
self.label_question = QLabel() self.label_question = QLabel()
self._layout.addWidget(self.label_question) self._layout.addWidget(self.label_question)
self.label_question.setText(title) self.label_question.setText(translate.translate(title))
self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_question.font() font_title = self.label_question.font()
@ -59,7 +60,7 @@ class SingleChoiceQuestion(BaseSurvey):
for choice_id, choice_text in choices.items(): for choice_id, choice_text in choices.items():
# create a radio button for that choice # create a radio button for that choice
button = QRadioButton() button = QRadioButton()
button.setText(choice_text) button.setText(translate.translate(choice_text))
# add the button to the frame # add the button to the frame
self._layout_responses.addWidget(button) self._layout_responses.addWidget(button)
@ -73,7 +74,7 @@ class SingleChoiceQuestion(BaseSurvey):
self._layout_responses.addWidget(self.button_response_other) self._layout_responses.addWidget(self.button_response_other)
self.button_responses_id[self.button_response_other] = self.details_choice_id self.button_responses_id[self.button_response_other] = self.details_choice_id
self.button_response_other.setText(self.details_choice_text) self.button_response_other.setText(translate.translate(self.details_choice_text))
self.group_responses.addButton(self.button_response_other) self.group_responses.addButton(self.button_response_other)
self.button_response_other.toggled.connect(self._on_response_other_check) # NOQA: connect exist self.button_response_other.toggled.connect(self._on_response_other_check) # NOQA: connect exist

View file

@ -4,14 +4,15 @@ from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QVBoxLayout, QLabel from PyQt6.QtWidgets import QVBoxLayout, QLabel
import translate
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
class Text(BaseSurvey): class Text(BaseSurvey):
def __init__( def __init__(
self, self,
title: str, title: translate.Translatable,
description: Optional[str], description: Optional[translate.Translatable],
abandonable: bool = None, abandonable: bool = None,
signals: dict[str, pyqtSignal] = None signals: dict[str, pyqtSignal] = None
): ):
@ -27,7 +28,7 @@ class Text(BaseSurvey):
# prepare the title # prepare the title
self.label_title = QLabel() self.label_title = QLabel()
self._layout.addWidget(self.label_title) self._layout.addWidget(self.label_title)
self.label_title.setText(title) self.label_title.setText(translate.translate(title))
self.label_title.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_title.font() font_title = self.label_title.font()
@ -39,7 +40,7 @@ class Text(BaseSurvey):
# prepare the description # prepare the description
self.label_description = QLabel() self.label_description = QLabel()
self._layout.addWidget(self.label_description) self._layout.addWidget(self.label_description)
self.label_description.setText(description) self.label_description.setText(translate.translate(description))
self.label_description.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_description.setAlignment(Qt.AlignmentFlag.AlignCenter)
@classmethod @classmethod

View file

@ -4,11 +4,12 @@ from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QTextEdit from PyQt6.QtWidgets import QVBoxLayout, QLabel, QTextEdit
import translate
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
class TextQuestion(BaseSurvey): class TextQuestion(BaseSurvey):
def __init__(self, title: str, signals: dict[str, pyqtSignal] = None): def __init__(self, title: translate.Translatable, signals: dict[str, pyqtSignal] = None):
super().__init__() super().__init__()
self.signals = signals if signals is not None else {} self.signals = signals if signals is not None else {}
@ -20,7 +21,7 @@ class TextQuestion(BaseSurvey):
# question title # question title
self.label_question = QLabel() self.label_question = QLabel()
self._layout.addWidget(self.label_question) self._layout.addWidget(self.label_question)
self.label_question.setText(title) self.label_question.setText(translate.translate(title))
self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_question.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_question.font() font_title = self.label_question.font()

View file

@ -6,6 +6,7 @@ from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QUrl, QEvent, QObject
from PyQt6.QtGui import QFont, QMouseEvent, QResizeEvent, QKeyEvent from PyQt6.QtGui import QFont, QMouseEvent, QResizeEvent, QKeyEvent
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QSizePolicy from PyQt6.QtWidgets import QLabel, QVBoxLayout, QSizePolicy
import translate
from source import assets_path from source import assets_path
from source.survey.base import BaseSurvey from source.survey.base import BaseSurvey
from source.widget import Browser from source.widget import Browser
@ -16,7 +17,7 @@ page_success_path: Path = assets_path / "web/success.html"
class WebMission(BaseSurvey): class WebMission(BaseSurvey):
def __init__( def __init__(
self, self,
title: str, title: translate.Translatable,
url: str, url: str,
check_condition: Optional[str] = None, check_condition: Optional[str] = None,
skip_time: Optional[float] = None, skip_time: Optional[float] = None,
@ -42,7 +43,7 @@ class WebMission(BaseSurvey):
# mission title # mission title
self.label_title = QLabel() self.label_title = QLabel()
self._layout.addWidget(self.label_title) self._layout.addWidget(self.label_title)
self.label_title.setText(title) self.label_title.setText(translate.translate(title))
self.label_title.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
font_title = self.label_title.font() font_title = self.label_title.font()

45
source/translate.py Normal file
View file

@ -0,0 +1,45 @@
import warnings
from typing import Optional
Translatable = dict[str, str]
current_language_code: str = "en"
def set_language(language_code: str) -> None:
"""
Set the current language of the application
:param language_code: the new language code
"""
global current_language_code
current_language_code = language_code
def get_language() -> str:
"""
Get the current language of the application
:return: the current language of the application
"""
return current_language_code
def translate(language_data: Translatable) -> str:
"""
Get the translation of a text to the current set language
:param language_data: a dictionary with every translation associated to the language
:return: the translation for the current language
"""
translation: Optional[str] = language_data.get(current_language_code)
if translation is None:
warnings.warn(f"No translation for language {current_language_code!r} for text {language_data!r}")
translation = language_data.get("en")
if translation is None:
raise Exception(f"No translation available for text {language_data!r}")
return translation

View file

@ -3,20 +3,20 @@
"text-welcome": { "text-welcome": {
"type": "text", "type": "text",
"title": "Bienvenue dans nôtre enquête !", "title": {"fr": "Bienvenue dans nôtre enquête !"},
"description": "Nous somme des étudiants en Informatique à l'UPJV.\nNous réalisons une étude sur l'ergonomie des interfaces graphique.\nCette étude concerne la plateforme de jeu vidéo \"Steam\"." "description": {"fr": "Nous somme des étudiants en Informatique à l'UPJV.\nNous réalisons une étude sur l'ergonomie des interfaces graphique.\nCette étude concerne la plateforme de jeu vidéo \"Steam\"."}
}, },
"text-data-collection": { "text-data-collection": {
"type": "text", "type": "text",
"title": "Collection des données", "title": {"fr": "Collection des données"},
"description": "Notre étude demande de collecter des données comme vos mouvements de souris et votre clavier lorsque vous naviguerez sur cette application.\nAucune autre donnée ne sera collecté en dehors de cette application.\nCes données sont anonyme et ne seront destiné exclusivement qu'à notre recherche.\nSi vous ne souhaitez pas continuer ce questionnaire, appuyez sur \"Abandonner\"", "description": {"fr": "Notre étude demande de collecter des données comme vos mouvements de souris et votre clavier lorsque vous naviguerez sur cette application.\nAucune autre donnée ne sera collecté en dehors de cette application.\nCes données sont anonyme et ne seront destiné exclusivement qu'à notre recherche.\nSi vous ne souhaitez pas continuer ce questionnaire, appuyez sur \"Abandonner\""},
"abandonable": true "abandonable": true
}, },
"question-age": { "question-age": {
"type": "question-integer", "type": "question-integer",
"title": "Quel est votre âge ?", "title": {"fr": "Quel est votre âge ?"},
"default": 30, "default": 30,
"minimum": 13, "minimum": 13,
"maximum": 150 "maximum": 150
@ -24,52 +24,52 @@
"question-usage-steam": { "question-usage-steam": {
"type": "question-single-choice", "type": "question-single-choice",
"title": "Utilisez-vous Steam ?", "title": {"fr": "Utilisez-vous Steam ?"},
"choices": { "choices": {
"always": "Tout le temps", "always": {"fr": "Tout le temps"},
"often": "Souvent", "often": {"fr": "Souvent"},
"sometime": "De temps en temps", "sometime": {"fr": "De temps en temps"},
"rarely": "Rarement", "rarely": {"fr": "Rarement"},
"never": "Jamais" "never": {"fr": "Jamais"}
} }
}, },
"question-usage-concurrent": { "question-usage-concurrent": {
"type": "question-multiple-choice", "type": "question-multiple-choice",
"title": "Quel autre plateforme de jeu en ligne utilisez-vous ?", "title": {"fr": "Quel autre plateforme de jeu en ligne utilisez-vous ?"},
"choices": { "choices": {
"epic": "Epic Games Store", "epic": {"fr": "Epic Games Store"},
"gog": "GOG", "gog": {"fr": "GOG"},
"origin": "Origin", "origin": {"fr": "Origin"},
"uplay": "Uplay", "uplay": {"fr": "Uplay"},
"battle": "Battle.net" "battle": {"fr": "Battle.net"}
}, },
"details_choice_enabled": true, "details_choice_enabled": true,
"details_choice_id": "other", "details_choice_id": "other",
"details_choice_text": "Autre" "details_choice_text": {"fr": "Autre"}
}, },
"question-difficulty-before": { "question-difficulty-before": {
"type": "question-single-choice", "type": "question-single-choice",
"title": "Avez-vous déjà rencontré des difficultés particulières lors de votre utilisation de Steam ?", "title": {"fr": "Avez-vous déjà rencontré des difficultés particulières lors de votre utilisation de Steam ?"},
"choices": { "choices": {
"no": "Non" "no": {"fr": "Non"}
}, },
"details_choice_enabled": true, "details_choice_enabled": true,
"details_choice_id": "yes", "details_choice_id": "yes",
"details_choice_text": "Oui" "details_choice_text": {"fr": "Oui"}
}, },
"mission-explanation": { "mission-explanation": {
"type": "text", "type": "text",
"title": "Explication de l'Evaluation", "title": {"fr": "Explication de l'Evaluation"},
"description": "Nous allons vous demander de naviguer dans Steam.\nUn message en haut vous indiquera une tâche à réaliser dans la plateforme.\n\nSi la tâche vous semble trop difficile, un bouton pour passer la tâche\ns'affichera au bout d'un certain temps en bas à droite.\n\nRappel : Votre clavier et votre souris sont enregistré.\nNe tapez aucune information personnel dans l'écran qui va suivre !" "description": {"fr": "Nous allons vous demander de naviguer dans Steam.\nUn message en haut vous indiquera une tâche à réaliser dans la plateforme.\n\nSi la tâche vous semble trop difficile, un bouton pour passer la tâche\ns'affichera au bout d'un certain temps en bas à droite.\n\nRappel : Votre clavier et votre souris sont enregistré.\nNe tapez aucune information personnel dans l'écran qui va suivre !"}
}, },
"mission-language": { "mission-language": {
"type": "mission-web", "type": "mission-web",
"title": "Changer la langue en français.", "title": {"fr": "Changer la langue en français."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.getElementsByTagName('html')[0].lang == 'fr'", "check": "document.getElementsByTagName('html')[0].lang == 'fr'",
"skip_time": 60 "skip_time": 60
@ -77,7 +77,7 @@
"mission-price": { "mission-price": {
"type": "mission-web", "type": "mission-web",
"title": "Afficher tous les jeux coutant moins de 20€.", "title": {"fr": "Afficher tous les jeux coutant moins de 20€."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.getElementById('maxprice_input').value == '20'", "check": "document.getElementById('maxprice_input').value == '20'",
"skip_time": 90 "skip_time": 90
@ -85,7 +85,7 @@
"mission-community-hub": { "mission-community-hub": {
"type": "mission-web", "type": "mission-web",
"title": "Rendez-vous sur le Hub de la Communauté du jeu \"Stray\".", "title": {"fr": "Rendez-vous sur le Hub de la Communauté du jeu \"Stray\"."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://steamcommunity.com/app/1332010'", "check": "document.documentURI == 'https://steamcommunity.com/app/1332010'",
"skip_time": 90 "skip_time": 90
@ -93,7 +93,7 @@
"mission-game-page": { "mission-game-page": {
"type": "mission-web", "type": "mission-web",
"title": "Rendez-vous sur la page du jeu \"Outer Wilds\".", "title": {"fr": "Rendez-vous sur la page du jeu \"Outer Wilds\"."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://store.steampowered.com/app/753640/Outer_Wilds/'", "check": "document.documentURI == 'https://store.steampowered.com/app/753640/Outer_Wilds/'",
"skip_time": 90 "skip_time": 90
@ -101,7 +101,7 @@
"mission-game-dlc": { "mission-game-dlc": {
"type": "mission-web", "type": "mission-web",
"title": "Rendez-vous sur la page du contenu additionnel \"Echoes of the Eye\" du jeu \"Outer Wilds\".", "title": {"fr": "Rendez-vous sur la page du contenu additionnel \"Echoes of the Eye\" du jeu \"Outer Wilds\"."},
"url": "https://store.steampowered.com/app/753640/Outer_Wilds/", "url": "https://store.steampowered.com/app/753640/Outer_Wilds/",
"check": "document.documentURI == 'https://store.steampowered.com/app/1622100/Outer_Wilds__Echoes_of_the_Eye/'", "check": "document.documentURI == 'https://store.steampowered.com/app/1622100/Outer_Wilds__Echoes_of_the_Eye/'",
"skip_time": 90 "skip_time": 90
@ -109,7 +109,7 @@
"mission-actuality-new": { "mission-actuality-new": {
"type": "mission-web", "type": "mission-web",
"title": "Aller sur la page des Actualités \"À la une\".", "title": {"fr": "Aller sur la page des Actualités \"À la une\"."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://store.steampowered.com/news/collection/featured/'", "check": "document.documentURI == 'https://store.steampowered.com/news/collection/featured/'",
"skip_time": 120 "skip_time": 120
@ -117,7 +117,7 @@
"mission-profile": { "mission-profile": {
"type": "mission-web", "type": "mission-web",
"title": "Trouver la page de profil de \"Faraphel\".", "title": {"fr": "Trouver la page de profil de \"Faraphel\"."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://steamcommunity.com/id/Faraphel'", "check": "document.documentURI == 'https://steamcommunity.com/id/Faraphel'",
"skip_time": 240 "skip_time": 240
@ -125,7 +125,7 @@
"mission-game-discussion": { "mission-game-discussion": {
"type": "mission-web", "type": "mission-web",
"title": "Trouver la page de discussions du jeu \"Sid Meier's Civilization V\".", "title": {"fr": "Trouver la page de discussions du jeu \"Sid Meier's Civilization V\"."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://steamcommunity.com/app/8930/discussions/'", "check": "document.documentURI == 'https://steamcommunity.com/app/8930/discussions/'",
"skip_time": 180 "skip_time": 180
@ -133,7 +133,7 @@
"mission-gift-card": { "mission-gift-card": {
"type": "mission-web", "type": "mission-web",
"title": "Trouver la page des cartes-cadeaux.", "title": {"fr": "Trouver la page des cartes-cadeaux."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "document.documentURI == 'https://store.steampowered.com/digitalgiftcards'", "check": "document.documentURI == 'https://store.steampowered.com/digitalgiftcards'",
"skip_time": 240 "skip_time": 240
@ -141,7 +141,7 @@
"mission-workshop": { "mission-workshop": {
"type": "mission-web", "type": "mission-web",
"title": "Trouver la page de communauté de la modification \"Animated Hair\" du jeu \"Terraria\" sur le Workshop.", "title": {"fr": "Trouver la page de communauté de la modification \"Animated Hair\" du jeu \"Terraria\" sur le Workshop."},
"url": "https://store.steampowered.com/", "url": "https://store.steampowered.com/",
"check": "publishedfileid == '2871109853'", "check": "publishedfileid == '2871109853'",
"skip_time": 240 "skip_time": 240
@ -150,46 +150,45 @@
"question-hardest-mission": { "question-hardest-mission": {
"type": "question-single-choice", "type": "question-single-choice",
"title": "Parmi les tâches, laquelle avez-vous trouvée la plus difficile ?", "title": {"fr": "Parmi les tâches, laquelle avez-vous trouvée la plus difficile ?"},
"choices": { "choices": {
"mission-language": "Changer la langue", "mission-language": {"fr": "Changer la langue"},
"mission-price": "Filtrer les jeux par leur prix", "mission-price": {"fr": "Filtrer les jeux par leur prix"},
"mission-community-hub": "Se rendre sur le hub de la communauté", "mission-community-hub": {"fr": "Se rendre sur le hub de la communauté"},
"mission-game-page": "Se rendre sur la page d'un jeu", "mission-game-page": {"fr": "Se rendre sur la page d'un jeu"},
"mission-game-dlc": "Se rendre sur la page du contenu additionnel (DLC) d'un jeu", "mission-game-dlc": {"fr": "Se rendre sur la page du contenu additionnel (DLC) d'un jeu"},
"mission-actuality-new": "Se rendre sur la page des actualités", "mission-actuality-new": {"fr": "Se rendre sur la page des actualités"},
"mission-profile": "Se rendre sur le profil d'un utilisateur", "mission-profile": {"fr": "Se rendre sur le profil d'un utilisateur"},
"mission-game-discussion": "Se rendre sur la page de discussion d'un jeu", "mission-game-discussion": {"fr": "Se rendre sur la page de discussion d'un jeu"},
"mission-gift-card": "Se rendre sur la page des cartes cadeaux", "mission-gift-card": {"fr": "Se rendre sur la page des cartes cadeaux"},
"mission-workshop": "Se rendre sur la page de la modification d'un jeu" "mission-workshop": {"fr": "Se rendre sur la page de la modification d'un jeu"}
} }
}, },
"question-experience": { "question-experience": {
"type": "question-single-choice", "type": "question-single-choice",
"title": "Avez-vous trouvé l'interface de Steam ergonomique ?", "title": {"fr": "Avez-vous trouvé l'interface de Steam ergonomique ?"},
"choices": { "choices": {
"yes": "Oui", "yes": {"fr": "Oui"},
"mixed": "Mitigé", "mixed": {"fr": "Mitigé"},
"no": "Non" "no": {"fr": "Non"}
} }
}, },
"question-experience-details": { "question-experience-details": {
"type": "question-text", "type": "question-text",
"title": "Qu'avez-vous pensé de l'ergonomie de Steam ?" "title": {"fr": "Qu'avez-vous pensé de l'ergonomie de Steam ?"}
}, },
"question-comment": { "question-comment": {
"type": "question-text", "type": "question-text",
"title": "Vous pouvez laisser un commentaire sur votre ressenti général (optionnel)" "title": {"fr": "Vous pouvez laisser un commentaire sur votre ressenti général (optionnel)"}
}, },
"text-thanking": { "text-thanking": {
"type": "text", "type": "text",
"title": "Remerciement", "title": {"fr": "Remerciement"},
"description": "Nous vous remercions grandement pour votre contribution à notre questionnaire et pour votre temps.\n\nVos données collectées sont situés dans le dossier \"result\".", "description": {"fr": "Nous vous remercions grandement pour votre contribution à notre questionnaire et pour votre temps.\n\nVos données collectées sont situés dans le dossier \"result\"."}
"abandonable": true
} }
} }