patchMatch({ _execRconCommandsInit: true }),
],
[
- t('Execute Rcon Knife Commands'),
+ t('Execute RCON Knife Commands'),
() => patchMatch({ _execRconCommandsKnife: true }),
],
[
- t('Execute Rcon Match Commands'),
+ t('Execute RCON Match Commands'),
() => patchMatch({ _execRconCommandsMatch: true }),
],
[
- t('Execute Rcon End Commands'),
+ t('Execute RCON End Commands'),
() => patchMatch({ _execRconCommandsEnd: true }),
],
- [t('Share Match with Token'), () => modalRef?.showModal()],
+ [t('Share Match With Token'), () => modalRef?.showModal()],
[t('Edit Match'), goToEditPage],
]
: [
- [t('Share Match with Token'), () => modalRef?.showModal()],
+ [t('Share Match With Token'), () => modalRef?.showModal()],
[t('Edit Match'), goToEditPage],
]
}
diff --git a/frontend/src/components/Rcon.tsx b/frontend/src/components/Rcon.tsx
index 58a556c..76f3d83 100644
--- a/frontend/src/components/Rcon.tsx
+++ b/frontend/src/components/Rcon.tsx
@@ -50,7 +50,7 @@ const RconCard: Component<{
return (
- {t('Rcon')}
+ {t('RCON')}
{output()}
diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json
new file mode 100644
index 0000000..b37c340
--- /dev/null
+++ b/frontend/src/locales/fr.json
@@ -0,0 +1,183 @@
+{
+ "Create": "Créer",
+ "Matches": "Matchs",
+ "Game Servers": "Serveurs de jeu",
+ "Login": "Connexion",
+ "Logout": "Déconnexion",
+ "Chat": "Tchat",
+ "Send chat message...": "Envoyer un message dans le tchat...",
+ "Select Preset": "Sélectionner Préréglage",
+ "Delete preset": "Supprimer préréglage",
+ "Preset Name": "Nom Préréglage",
+ "Can be used by": "Utilisable par",
+ "Everybody": "Tout le monde",
+ "Only logged in users": "Utilisateurs connectés seulement",
+ "Add new preset": "Ajouter nouveau préréglage",
+ "Update preset": "Enregistrer préréglage",
+ "The map pool has too few maps.": "La liste de cartes est trop courte.",
+ "Teams": "Équipes",
+ "Team A Name": "Nom Équipe A",
+ "Team B Name": "Nom Équipe B",
+ "Game Server": "Serveur de jeu",
+ "Use Own Game Server": "Utiliser son propre serveur de jeu",
+ "Game Server IP": "IP Serveur",
+ "Game Server Port": "Port Serveur",
+ "Game Server RCON Password": "Mot de passe RCON Serveur",
+ "Map Pool": "Liste Cartes",
+ "Election Steps": "Étapes Sélection",
+ "Quick Load:": "Chargement Rapide :",
+ "Best of 1": "Best of 1",
+ "Best of 3": "Best of 3",
+ "Empty": "Vide",
+ "Add new step": "Ajouter une étape",
+ "RCON Commands": "Commandes RCON",
+ "Init": "Initialisation",
+ "Executed only once: when the match is created": "Exécuté une seule fois : lors de la création du match",
+ "Knife": "Knife",
+ "Executed at the start of a knife round": "Exécuté au début d'un knife round",
+ "Match": "Match",
+ "Executed at the start of each match map": "Exécuté au début de chaque carte du match",
+ "End": "Fin",
+ "Executed only once: after the end of the last map": "Exécuté une seule fois : à la fin de la dernière carte",
+ "Advanced Settings": "Paramètres Avancés",
+ "Hide": "Cacher",
+ "Show": "Montrer",
+ "Mode": "Mode",
+ "Single match (stops when match is finished)": "Match unique (s'arrête quand le match est fini)",
+ "Loop mode (starts again after match is finished)": "Mode boucle (recommence après la fin du match)",
+ "Match End Action": "Action Fin Match",
+ "None": "Aucune",
+ "Kick all players after match end": "Kick tous les joueurs à la fin du match",
+ "Quit server via RCON after match end": "Quitter le serveur via RCON à la fin du match",
+ "TMT Log Address": "Adresse Logs TMT",
+ "HTTP log receiver from the perspective of the game server": "Destinataire de logs HTTP de la perspective du serveur de jeu",
+ "Webhook URL": "URL Webhook",
+ "HTTP address to receive TMT's webhooks": "Adresse HTTP pour recevoir les webhooks de TMT",
+ "Webhook Headers": "En-têtes du webhook",
+ "Additional headers that will be added to each webhook request": "En-têtes supplémentaires ajoutés à chaque requête de webhook",
+ "Match Passthrough": "Passerelle Match",
+ "Custom value to identify the match in 3rd party tools": "Valeur personnalisée pour identifier le match dans des outils tiers",
+ "Team A Advantage": "Avantage Équipe A",
+ "Team A Passthrough": "Passerelle Équipe A",
+ "Custom value to identify this team in 3rd party tools": "Valeur personnalisée pour identifier cette équipe dans des outils tiers",
+ "Team A Player Steam IDs 64": "Steam IDs 64 Joueurs Équipe A",
+ "Force players by their steam id 64 into team A": "Forcer les joueurs par leur Steam ID 64 dans l'équipe A",
+ "Team B Advantage": "Avantage Équipe B",
+ "Team B Passthrough": "Passerelle Équipe B",
+ "Team B Player Steam IDs 64": "Steam IDs 64 Joueurs Équipe B",
+ "Force players by their steam id 64 into team B": "Forcer les joueurs par leur Steam ID 64 dans l'équipe B",
+ "Can Clinch": "Clinch Possible",
+ "Ends match series after a map if a winner is determined": "Termine la série de matchs après une carte si un gagnant est déterminé",
+ "Match State": "État du Match",
+ "Overrides the match state (does not execute anything)": "Remplace l'état du match (n'exécute rien)",
+ "Election": "Sélection",
+ "Match Map": "Carte Match",
+ "Finished": "Terminé",
+ "JSON Payload": "Instructions JSON",
+ "This will be used to create the match": "Ceci sera utilisé pour créer le match",
+ "This will be used to update the match": "Ceci sera utilisé pour modifier le match",
+ "JSON parse error: Invalid JSON": "Erreur de traitement JSON : JSON invalide",
+ "Create Match": "Créer Match",
+ "Update Match": "Modifier Match",
+ "Map Mode": "Mode Carte",
+ "Map Who": "Équipe Carte",
+ "Map Fixed": "Carte Déterminée",
+ "Side Mode": "Mode Camp",
+ "Side Who": "Équipe Camp",
+ "Side Fixed": "Camp Déterminé",
+ "Add": "Ajouter",
+ "Map ban by": "Bannissement de carte par",
+ "Map pick by": "Choix de carte par",
+ "Fixed map": "Carte déterminée",
+ "Map must be agreed on": "La carte doit être un accord mutuel",
+ "Random map ban": "Bannissement de carte aléatoire",
+ "Random map pick": "Choix de carte aléatoire",
+ "Team A starts as CT": "L'équipe A commence comme CT",
+ "Team A starts as T": "L'équipe A commence comme T",
+ "Team B starts as CT": "L'équipe B commence comme CT",
+ "Team B starts as T": "L'équipe B commence comme T",
+ "Team X starts as CT": "L'équipe X commence comme CT",
+ "Team X starts as T": "L'équipe X commence comme T",
+ "Team Y starts as CT": "L'équipe Y commence comme CT",
+ "Team Y starts as T": "L'équipe Y commence comme T",
+ "Team A": "Équipe A",
+ "Team B": "Équipe B",
+ "Team X": "Équipe X",
+ "Team Y": "Équipe Y",
+ "Side pick by": "Choix de camp par",
+ "Knife for side": "Knife pour camp",
+ "Random sides": "Camps aléatoires",
+ "Logs": "Logs",
+ "#": "#",
+ "IP": "IP",
+ "Port": "Port",
+ "RCON Password": "Mot de passe RCON",
+ "Can Be Used?": "Utilisable ?",
+ "Used By": "Utilisé Par",
+ "Actions": "Actions",
+ "RCON": "RCON",
+ "Stop Match": "Arrêter Match",
+ "Restart Match": "Recommencer Match",
+ "Execute RCON Init Commands": "Exécuter Commandes RCON Initialisation",
+ "Execute RCON Knife Commands": "Exécuter Commandes RCON Knife",
+ "Execute RCON Match Commands": "Exécuter Commandes RCON Match",
+ "Execute RCON End Commands": "Exécuter Commandes RCON Fin",
+ "Share Match With Token": "Partager Match Avec Token",
+ "Edit Match": "Modifier Match",
+ "Map Wins": "Victoires Carte",
+ "draws": "égalités",
+ "ELECTION": "SÉLECTION",
+ "MATCH_MAP": "CARTE_MATCH",
+ "FINISHED": "TERMINÉ",
+ "Copy & share the link below.": "Copiez & partagez le lien ci-dessous.",
+ "Warning: The link gives full admin access to (only) this match.": "Attention : Le lien donne un accès administrateur complet à (seulement) ce match.",
+ "Online Players": "Joueurs En Ligne",
+ "Age": "Âge",
+ "Best of": "Best of",
+ "Current Map": "Carte Actuelle",
+ "Map State": "État Carte",
+ "Map Score": "Score Carte",
+ "Details": "Détails",
+ "enter map name": "entrer nom de carte",
+ "enter map state": "entrer état de carte",
+ "change map name": "changer nom de carte",
+ "change map state": "changer état de carte",
+ "load round backup": "charger sauvegarde de round",
+ "switch to this map": "passer à cette carte",
+ "switch team internals": "échanger propriétés d'équipes",
+ "Load Round Backup": "Charger Sauvegarde de Round",
+ "close": "fermer",
+ "Match is not live": "Le match n'est pas en cours",
+ "This match is not being supervised.": "Ce match n'est pas supervisé.",
+ "Start Supervising": "Commencer Supervision",
+ "This match was not stopped properly.": "Ce match n'a pas été arrêté correctement.",
+ "The next time TMT starts, it will try to supervise it again." : "La prochaine fois que TMT démarrera, il réessaiera de le superviser.",
+ "Stop it to prevent supervising retries.": "Arrêtez le pour empêcher d'essayer la supervision.",
+ "Stop Supervising": "Arrêter Supervision",
+ "Players": "Joueurs",
+ "Not Assigned": "Non Assigné",
+ "Online": "En Ligne",
+ "Offline": "Hors-Ligne",
+ "Execute RCON command...": "Lancer une commande RCON...",
+ "Select Round Backup File": "Choisir Fichier de Sauvegarde de Round",
+ "ip: port invalid": "ip: port invalide",
+ "Game servers managed by TMT can be used to automatically assign them to new matches.": "Les serveurs de jeu gérés par TMT peuvent automatiquement être assignés aux nouveaux matchs.",
+ "Anonymous (not logged in) users can then also use them for their games.": "Les utilisateurs anonymes (non connectés) peuvent alors aussi les utiliser pour leurs parties.",
+ "Add Game Server": "Ajouter Serveur de Jeu",
+ "Can be used for new matches?": "Utilisable par des nouveaux matchs ?",
+ "Login Failed": "Échec Connexion",
+ "Password/Token": "Mot de Passe / Token",
+ "Log Out": "Déconnnexion",
+ "Back to the Match": "Retour au Match",
+ "Live Matches": "Matchs en Cours",
+ "Only show matches that are currently being supervised": "Afficher seulement les matchs actuellement supervisés",
+ "Not Live": "Pas en Cours",
+ "Only show offline matches (not supervised)": "Afficher seulement les matchs hors-ligne (non supervisés)",
+ "Dangling Matches": "Matchs Suspendus",
+ "Only show offline matches which have not been properly stopped": "Afficher seulement les matchs hors-ligne qui n'ont pas été arrêtés correctement",
+ "A custom filter from the URL is used": "Un filtre personnalisé dans l'URL est utilisé",
+ "Custom Filter": "Filtre Personnalisé",
+ "Column Settings": "Paramètres de Colonnes",
+ "Error": "Erreur",
+ "caution, please confirm": "attention, veuillez confirmer"
+}
diff --git a/frontend/src/pages/gameServer.tsx b/frontend/src/pages/gameServer.tsx
index 8328390..84a3682 100644
--- a/frontend/src/pages/gameServer.tsx
+++ b/frontend/src/pages/gameServer.tsx
@@ -27,7 +27,7 @@ export const GameServerPage: Component = () => {
});
return Number.isNaN(port) ? (
-
+
) : (
diff --git a/frontend/src/pages/gameServers.tsx b/frontend/src/pages/gameServers.tsx
index 92ba8dd..80b201d 100644
--- a/frontend/src/pages/gameServers.tsx
+++ b/frontend/src/pages/gameServers.tsx
@@ -108,7 +108,7 @@ export const GameServersPage: Component = () => {
/>
setRconPassword(e.currentTarget.value)}
/>
diff --git a/frontend/src/pages/matches.tsx b/frontend/src/pages/matches.tsx
index bef1211..759d270 100644
--- a/frontend/src/pages/matches.tsx
+++ b/frontend/src/pages/matches.tsx
@@ -52,7 +52,7 @@ const filterOptions: FilterOption[] = [
{
title: t('Dangling Matches'),
search: '?isLive=false&isStopped=false&state=ELECTION&state=MATCH_MAP',
- label: t('Only show offline matches, which have not been properly stopped'),
+ label: t('Only show offline matches which have not been properly stopped'),
includeNewMatches: false,
},
];
diff --git a/frontend/src/utils/locale.ts b/frontend/src/utils/locale.ts
index f7efaad..51f6b67 100644
--- a/frontend/src/utils/locale.ts
+++ b/frontend/src/utils/locale.ts
@@ -1,6 +1,7 @@
import { createSignal } from 'solid-js';
import de from '../locales/de.json';
import en from '../locales/en.json';
+import fr from '../locales/fr.json';
export const t = (key: string) => {
const l = getCurrentLocale() as { [key: string]: string };
@@ -11,41 +12,41 @@ export const t = (key: string) => {
return value ?? key;
};
-const fromBrowserLanguage = () => {
- const nl = navigator.language?.substring(0, 2);
- if (['de', 'en'].includes(nl)) {
+type Locale = 'de' | 'en' | 'fr';
+
+const locales: Locale[] = ['de', 'en', 'fr'];
+
+const fromBrowserLanguage = (): Locale => {
+ const nl = navigator.language?.substring(0, 2) as Locale;
+ if (locales.includes(nl)) {
return nl;
}
return 'en';
};
-const [currentLocale, setCurrentLocale] = createSignal<'de' | 'en'>(
- (localStorage.getItem('locale') as 'de' | 'en') ?? fromBrowserLanguage()
+const [currentLocale, setCurrentLocale] = createSignal(
+ (localStorage.getItem('locale') as Locale) ?? fromBrowserLanguage()
);
export { currentLocale };
export const cycleLocale = () => {
const cl = currentLocale();
- let next: 'de' | 'en';
- if (cl === 'de') {
- next = 'en';
- } else if (cl === 'en') {
- next = 'de';
- } else {
- next = 'en';
- }
+ let next: Locale = locales[(locales.indexOf(cl) + 1)%locales.length];
setCurrentLocale(next);
localStorage.setItem('locale', next);
};
const getCurrentLocale = () => {
const cl = currentLocale();
- if (cl === 'de') {
- return de;
- } else if (cl === 'en') {
- return en;
- } else {
- console.warn(`Locale ${cl} is not available, use "en" instead`);
- return en;
+ switch (cl) {
+ case 'de':
+ return de;
+ case 'en':
+ return en;
+ case 'fr':
+ return fr;
+ default:
+ console.warn(`Locale ${cl} is not available, use "en" instead`);
+ return en;
}
};