Add i18n support
continuous-integration/drone/push Build is passing Details

main
Clément FRÉVILLE 4 months ago
parent 24b38a2eac
commit bc3cac2460

@ -9,13 +9,13 @@
</head>
<body>
<div id="automaton-selector">
<h1>Select an automaton</h1>
<h1 data-i18n>Select an automaton</h1>
<div id="automaton-collection"></div>
</div>
<div id="app" hidden="hidden">
<div class="actions">
<button id="back-button">Back</button>
<a id="controls-button" class="button open-modal" href="#controls">Controls</a>
<button id="back-button" data-i18n>Back</button>
<a id="controls-button" class="button open-modal" href="#controls" data-i18n>Controls</a>
</div>
<div class="input">
<input type="text" id="word-input" autocapitalize="off" spellcheck="false" />
@ -23,52 +23,52 @@
</div>
<div class="input">
<div id="input-buttons"></div>
<button id="clear-button">Clear</button>
<button id="clear-button" data-i18n>Clear</button>
</div>
<div id="state-graph"></div>
</div>
<div id="controls" class="modal" hidden="hidden">
<div class="modal-content">
<h3>Controls</h3>
<h3 data-i18n>Controls</h3>
<table>
<thead>
<tr>
<th>Action</th>
<th>Desktop</th>
<th data-i18n>Action</th>
<th data-i18n>Desktop</th>
</tr>
</thead>
<tbody>
<tr>
<td>Pan</td>
<td>Left-click and drag</td>
<td data-i18n>Pan</td>
<td data-i18n>Left-click and drag</td>
</tr>
<tr>
<td>Zoom in/out</td>
<td>Mouse wheel</td>
<td data-i18n>Zoom in/out</td>
<td data-i18n>Mouse wheel</td>
</tr>
<tr>
<td>Create state</td>
<td>Double-click</td>
<td data-i18n>Create state</td>
<td data-i18n>Double-click</td>
</tr>
<tr>
<td>Move state</td>
<td>Middle-click and drag</td>
<td data-i18n>Move state</td>
<td data-i18n>Middle-click and drag</td>
</tr>
<tr>
<td>Edit/Delete state</td>
<td>Right-click</td>
<td data-i18n>Edit/Delete state</td>
<td data-i18n>Right-click</td>
</tr>
<tr>
<td>Create transition</td>
<td>Left-click and drag</td>
<td data-i18n>Create transition</td>
<td data-i18n>Left-click and drag</td>
</tr>
<tr>
<td>Edit transition</td>
<td>Right-click</td>
<td data-i18n>Edit transition</td>
<td data-i18n>Right-click</td>
</tr>
<tr>
<td>Delete transition</td>
<td>Right-click</td>
<td data-i18n>Delete transition</td>
<td data-i18n>Right-click</td>
</tr>
</table>
</div>

@ -1,5 +1,6 @@
import * as d3 from 'd3';
import { D3DragEvent, D3ZoomEvent } from 'd3';
import { tr } from '../i18n.ts';
import { Canvas, createCanvas, createSimulation, createZoom, GraphConfiguration, Simulation } from './d3/canvas.ts';
import { Graph, Link, Node } from './d3/graph.ts';
import { initMarkers } from './d3/markers.ts';
@ -235,17 +236,17 @@ export class GraphEditor extends HTMLElement {
if (!this.readonly) {
nodeGroup.on('contextmenu', (event: MouseEvent, d: Node) => {
this.moveMenu(event, [
new MenuAction('Start', () => d.start, () => {
new MenuAction(tr('Start'), () => d.start, () => {
d.start = !d.start;
this.fireOnChange();
this.restart();
}),
new MenuAction('Accepting', () => d.accepting, () => {
new MenuAction(tr('Accepting'), () => d.accepting, () => {
d.accepting = !d.accepting;
this.fireOnChange();
this.restart();
}),
new MenuAction('Delete', () => false, () => {
new MenuAction(tr('Delete'), () => false, () => {
this.graph.removeNode(d);
this.fireOnChange();
this.resetDraggableLink();

@ -0,0 +1,51 @@
const fr = {
'Select an automaton': 'Sélectionner un automate',
'Back': 'Retour',
'Clear': 'Effacer',
'Delete': 'Supprimer',
'Controls': 'Contrôles',
'Action': 'Action',
'Desktop': 'Bureau',
'Pan': 'Déplacer',
'Zoom in/out': 'Zoomer',
'Create state': 'Créer un état',
'Move state': 'Déplacer un état',
'Edit/Delete state': 'Éditer/Supprimer un état',
'Create transition': 'Créer une transition',
'Edit transition': 'Éditer une transition',
'Delete transition': 'Supprimer une transition',
'Left-click and drag': 'Clic gauche et déplacer',
'Mouse wheel': 'Molette de la souris',
'Double-click': 'Double-clic',
'Middle-click and drag': 'Clic molette et déplacer',
'Right-click': 'Clic droit',
'Starts and ends with a': 'Commence et finit par a',
'Starts with bb': 'Commence par bb',
'Exactly one b': 'Exactement un b',
'Odd number of a': 'Nombre impair de a',
'Ends with two b': 'Finit par deux b',
'states': 'états',
'Create New Automaton': 'Créer un nouvel automate',
'Create a new automaton from scratch.': 'Créer un nouvel automate à partir de zéro.',
'Start': 'Début',
'Accepting': 'Acceptant',
};
type TranslationKey = keyof typeof fr;
let lang = 'en-US';
if (navigator.languages) {
lang = navigator.languages.find((l) => l !== lang) || lang;
}
const messages: Record<string, string> = lang.startsWith('fr') ? fr : {};
document.querySelectorAll('[data-i18n]').forEach((element) => {
element.textContent = trUserContent(element.textContent!);
});
export function tr(key: TranslationKey): string {
return messages[key] || key;
}
export function trUserContent(key: string): string {
return messages[key] || key;
}

@ -1,5 +1,6 @@
import { AUTOMATONS } from './examples.js';
import { openAutomaton } from './fsm.js';
import { tr, trUserContent } from './i18n.ts';
import './modal.ts';
const automatonSelector = /** @type {HTMLDivElement} */ (document.getElementById('automaton-selector'));
@ -14,8 +15,8 @@ for (const [displayName, automaton] of Object.entries(AUTOMATONS)) {
card.classList.add('card');
card.innerHTML = `
<div class="card-body">
<h5 class="card-title">${displayName}</h5>
<p class="card-text">${automaton.length} states</p>
<h5 class="card-title">${trUserContent(displayName)}</h5>
<p class="card-text">${automaton.length} ${tr('states')}</p>
</div>
`;
const handleEvent = () => {
@ -35,8 +36,8 @@ const create = document.createElement('div');
create.classList.add('card');
create.innerHTML = `
<div class="card-body">
<h5 class="card-title">Create New Automaton</h5>
<p class="card-text">Create a new automaton from scratch.</p>
<h5 class="card-title">${tr('Create New Automaton')}</h5>
<p class="card-text">${tr('Create a new automaton from scratch.')}</p>
</div>
`;
automatonCollection.appendChild(create);

Loading…
Cancel
Save