feat: Localize UI and enhance user experience with French translations
- Update aim-login component with French labels and placeholders - Modify chat-room component to display French error messages and status texts - Update desktop-icons with French alt texts and labels - Add French translations to loading components - Implement language-specific UI improvements
- commit
- 96cccd78a1a0c453a6f2d8f7706d9cc5d5211d04
- author
- Kit · · 2025-02-16 08:10 UTC
- parent
- 944de91
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
6 files · +41 -44
M .cursor/rules/global.mdc +5 -2
| @@ -3,5 +3,8 @@ description: | |||
| 3 | 3 | globs: | |
| 4 | 4 | --- | |
| 5 | 5 | # Debugging | |
| 6 | - | - Always add console.debug for client sided code | |
| 7 | - | - Always add either console.log or follow the logger implementation logic is there is one for server/backend code⏎̸ | |
| 6 | + | - Always add console.debug for client sided code . | |
| 7 | + | - Always add either console.log or follow the logger implementation if any server/backend code. | |
| 8 | + | ||
| 9 | + | # Language | |
| 10 | + | - The content of the app is in French but the code comments must be in English.⏎̸ | |
| @@ -3,5 +3,8 @@ description: | |||||
| 3 | globs: | 3 | globs: | ||
| 4 | --- | 4 | --- | ||
| 5 | # Debugging | 5 | # Debugging | ||
| 6 | - | - Always add console.debug for client sided code | 6 | + | - Always add console.debug for client sided code . |
| 7 | - | - Always add either console.log or follow the logger implementation logic is there is one for server/backend code⏎̸ | 7 | + | - Always add either console.log or follow the logger implementation if any server/backend code. |
| 8 | + | ||||
| 9 | + | # Language | |||
| 10 | + | - The content of the app is in French but the code comments must be in English.⏎̸ | |||
M src/lib/components/aim-login.svelte +17 -17
| @@ -179,7 +179,7 @@ | |||
| 179 | 179 | <div class="title-bar"> | |
| 180 | 180 | <div class="title-bar-text"> | |
| 181 | 181 | <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" /> | |
| 182 | - | {activeTab === 'signin' ? 'Sign In' : 'Sign Up'} | |
| 182 | + | {activeTab === 'signin' ? 'Connexion' : 'Inscription'} | |
| 183 | 183 | </div> | |
| 184 | 184 | <div class="title-bar-controls"> | |
| 185 | 185 | <button aria-label="Close" onclick={handleClose}>×</button> | |
| @@ -187,20 +187,20 @@ | |||
| 187 | 187 | </div> | |
| 188 | 188 | ||
| 189 | 189 | <div class="tabs"> | |
| 190 | - | <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Sign In</button> | |
| 191 | - | <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Sign Up</button> | |
| 190 | + | <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Connexion</button> | |
| 191 | + | <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Inscription</button> | |
| 192 | 192 | </div> | |
| 193 | 193 | ||
| 194 | 194 | <div class="window-content"> | |
| 195 | 195 | {#if activeTab === 'signin'} | |
| 196 | 196 | <form onsubmit={handleSigninSubmit}> | |
| 197 | 197 | <div class="form-group"> | |
| 198 | - | <label for="si-username">Username</label> | |
| 199 | - | <input type="text" id="si-username" bind:value={siUsername} placeholder="Username" /> | |
| 198 | + | <label for="si-username">Nom d'utilisateur</label> | |
| 199 | + | <input type="text" id="si-username" bind:value={siUsername} placeholder="Nom d'utilisateur" /> | |
| 200 | 200 | </div> | |
| 201 | 201 | <div class="form-group"> | |
| 202 | - | <label for="si-password">Password</label> | |
| 203 | - | <input type="password" id="si-password" bind:value={siPassword} placeholder="Password" /> | |
| 202 | + | <label for="si-password">Mot de passe</label> | |
| 203 | + | <input type="password" id="si-password" bind:value={siPassword} placeholder="Mot de passe" /> | |
| 204 | 204 | </div> | |
| 205 | 205 | {#if error} | |
| 206 | 206 | <div class="error">{error}</div> | |
| @@ -229,27 +229,27 @@ | |||
| 229 | 229 | <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/> | |
| 230 | 230 | </svg> | |
| 231 | 231 | {/if} | |
| 232 | - | Sign In | |
| 232 | + | Se connecter | |
| 233 | 233 | </button> | |
| 234 | 234 | </div> | |
| 235 | 235 | </form> | |
| 236 | 236 | {:else} | |
| 237 | 237 | <form onsubmit={handleSignupSubmit}> | |
| 238 | 238 | <div class="form-group"> | |
| 239 | - | <label for="su-username">Username</label> | |
| 240 | - | <input type="text" id="su-username" bind:value={suUsername} placeholder="Choose a username" /> | |
| 239 | + | <label for="su-username">Nom d'utilisateur</label> | |
| 240 | + | <input type="text" id="su-username" bind:value={suUsername} placeholder="Choisissez un nom d'utilisateur" /> | |
| 241 | 241 | </div> | |
| 242 | 242 | <div class="form-group"> | |
| 243 | - | <label for="su-password">Password</label> | |
| 244 | - | <input type="password" id="su-password" bind:value={suPassword} placeholder="Password" /> | |
| 243 | + | <label for="su-password">Mot de passe</label> | |
| 244 | + | <input type="password" id="su-password" bind:value={suPassword} placeholder="Mot de passe" /> | |
| 245 | 245 | </div> | |
| 246 | 246 | <div class="form-group"> | |
| 247 | - | <label for="su-confirm-password">Confirm Password</label> | |
| 248 | - | <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirm Password" /> | |
| 247 | + | <label for="su-confirm-password">Confirmer le mot de passe</label> | |
| 248 | + | <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirmer le mot de passe" /> | |
| 249 | 249 | </div> | |
| 250 | 250 | <div class="form-group"> | |
| 251 | - | <label for="captcha">What's the meaning of PDR?</label> | |
| 252 | - | <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Enter your answer" /> | |
| 251 | + | <label for="captcha">Quelle est la signification de PDR ?</label> | |
| 252 | + | <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Entrez votre réponse" /> | |
| 253 | 253 | </div> | |
| 254 | 254 | {#if signupError} | |
| 255 | 255 | <div class="error">{signupError}</div> | |
| @@ -278,7 +278,7 @@ | |||
| 278 | 278 | <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path> | |
| 279 | 279 | </svg> | |
| 280 | 280 | {/if} | |
| 281 | - | Sign Up | |
| 281 | + | S'inscrire | |
| 282 | 282 | </button> | |
| 283 | 283 | </div> | |
| 284 | 284 | </form> | |
| @@ -179,7 +179,7 @@ | |||||
| 179 | <div class="title-bar"> | 179 | <div class="title-bar"> | ||
| 180 | <div class="title-bar-text"> | 180 | <div class="title-bar-text"> | ||
| 181 | <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" /> | 181 | <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" /> | ||
| 182 | - | {activeTab === 'signin' ? 'Sign In' : 'Sign Up'} | 182 | + | {activeTab === 'signin' ? 'Connexion' : 'Inscription'} |
| 183 | </div> | 183 | </div> | ||
| 184 | <div class="title-bar-controls"> | 184 | <div class="title-bar-controls"> | ||
| 185 | <button aria-label="Close" onclick={handleClose}>×</button> | 185 | <button aria-label="Close" onclick={handleClose}>×</button> | ||
| @@ -187,20 +187,20 @@ | |||||
| 187 | </div> | 187 | </div> | ||
| 188 | 188 | ||||
| 189 | <div class="tabs"> | 189 | <div class="tabs"> | ||
| 190 | - | <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Sign In</button> | 190 | + | <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Connexion</button> |
| 191 | - | <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Sign Up</button> | 191 | + | <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Inscription</button> |
| 192 | </div> | 192 | </div> | ||
| 193 | 193 | ||||
| 194 | <div class="window-content"> | 194 | <div class="window-content"> | ||
| 195 | {#if activeTab === 'signin'} | 195 | {#if activeTab === 'signin'} | ||
| 196 | <form onsubmit={handleSigninSubmit}> | 196 | <form onsubmit={handleSigninSubmit}> | ||
| 197 | <div class="form-group"> | 197 | <div class="form-group"> | ||
| 198 | - | <label for="si-username">Username</label> | 198 | + | <label for="si-username">Nom d'utilisateur</label> |
| 199 | - | <input type="text" id="si-username" bind:value={siUsername} placeholder="Username" /> | 199 | + | <input type="text" id="si-username" bind:value={siUsername} placeholder="Nom d'utilisateur" /> |
| 200 | </div> | 200 | </div> | ||
| 201 | <div class="form-group"> | 201 | <div class="form-group"> | ||
| 202 | - | <label for="si-password">Password</label> | 202 | + | <label for="si-password">Mot de passe</label> |
| 203 | - | <input type="password" id="si-password" bind:value={siPassword} placeholder="Password" /> | 203 | + | <input type="password" id="si-password" bind:value={siPassword} placeholder="Mot de passe" /> |
| 204 | </div> | 204 | </div> | ||
| 205 | {#if error} | 205 | {#if error} | ||
| 206 | <div class="error">{error}</div> | 206 | <div class="error">{error}</div> | ||
| @@ -229,27 +229,27 @@ | |||||
| 229 | <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/> | 229 | <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/> | ||
| 230 | </svg> | 230 | </svg> | ||
| 231 | {/if} | 231 | {/if} | ||
| 232 | - | Sign In | 232 | + | Se connecter |
| 233 | </button> | 233 | </button> | ||
| 234 | </div> | 234 | </div> | ||
| 235 | </form> | 235 | </form> | ||
| 236 | {:else} | 236 | {:else} | ||
| 237 | <form onsubmit={handleSignupSubmit}> | 237 | <form onsubmit={handleSignupSubmit}> | ||
| 238 | <div class="form-group"> | 238 | <div class="form-group"> | ||
| 239 | - | <label for="su-username">Username</label> | 239 | + | <label for="su-username">Nom d'utilisateur</label> |
| 240 | - | <input type="text" id="su-username" bind:value={suUsername} placeholder="Choose a username" /> | 240 | + | <input type="text" id="su-username" bind:value={suUsername} placeholder="Choisissez un nom d'utilisateur" /> |
| 241 | </div> | 241 | </div> | ||
| 242 | <div class="form-group"> | 242 | <div class="form-group"> | ||
| 243 | - | <label for="su-password">Password</label> | 243 | + | <label for="su-password">Mot de passe</label> |
| 244 | - | <input type="password" id="su-password" bind:value={suPassword} placeholder="Password" /> | 244 | + | <input type="password" id="su-password" bind:value={suPassword} placeholder="Mot de passe" /> |
| 245 | </div> | 245 | </div> | ||
| 246 | <div class="form-group"> | 246 | <div class="form-group"> | ||
| 247 | - | <label for="su-confirm-password">Confirm Password</label> | 247 | + | <label for="su-confirm-password">Confirmer le mot de passe</label> |
| 248 | - | <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirm Password" /> | 248 | + | <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirmer le mot de passe" /> |
| 249 | </div> | 249 | </div> | ||
| 250 | <div class="form-group"> | 250 | <div class="form-group"> | ||
| 251 | - | <label for="captcha">What's the meaning of PDR?</label> | 251 | + | <label for="captcha">Quelle est la signification de PDR ?</label> |
| 252 | - | <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Enter your answer" /> | 252 | + | <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Entrez votre réponse" /> |
| 253 | </div> | 253 | </div> | ||
| 254 | {#if signupError} | 254 | {#if signupError} | ||
| 255 | <div class="error">{signupError}</div> | 255 | <div class="error">{signupError}</div> | ||
| @@ -278,7 +278,7 @@ | |||||
| 278 | <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path> | 278 | <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path> | ||
| 279 | </svg> | 279 | </svg> | ||
| 280 | {/if} | 280 | {/if} | ||
| 281 | - | Sign Up | 281 | + | S'inscrire |
| 282 | </button> | 282 | </button> | ||
| 283 | </div> | 283 | </div> | ||
| 284 | </form> | 284 | </form> | ||
M src/lib/components/chat-room.svelte +13 -19
| @@ -369,6 +369,9 @@ $effect(() => { | |||
| 369 | 369 | isInitialLoading = false; | |
| 370 | 370 | } | |
| 371 | 371 | }); | |
| 372 | + | ||
| 373 | + | // Add derived state for total users count | |
| 374 | + | let totalUsers = $derived(onlineUsers.length); | |
| 372 | 375 | </script> | |
| 373 | 376 | ||
| 374 | 377 | {#if showChatRoom} | |
| @@ -393,9 +396,9 @@ $effect(() => { | |||
| 393 | 396 | > | |
| 394 | 397 | <div class="title-bar"> | |
| 395 | 398 | <div class="title-bar-text"> | |
| 396 | - | Pdr Aim - {currentUser?.nickname} | |
| 399 | + | Pdr Aim {#if currentUser} - {currentUser.nickname}{:else} - {totalUsers} personne{totalUsers > 1 ? 's' : ''} présente{totalUsers > 1 ? 's' : ''}{/if} | |
| 397 | 400 | {#if sseError} | |
| 398 | - | <span class="connection-error">⚠️ Connection Error</span> | |
| 401 | + | <span class="connection-error">⚠️ Erreur de connexion</span> | |
| 399 | 402 | {/if} | |
| 400 | 403 | </div> | |
| 401 | 404 | <div class="title-bar-controls"> | |
| @@ -409,7 +412,7 @@ $effect(() => { | |||
| 409 | 412 | <button onclick={handleClose} aria-label="Close"></button> | |
| 410 | 413 | {:else} | |
| 411 | 414 | <button | |
| 412 | - | aria-label="Toggle Buddy List" | |
| 415 | + | aria-label="Afficher/Masquer la liste des contacts" | |
| 413 | 416 | onclick={() => showUserList = !showUserList} | |
| 414 | 417 | >👥</button> | |
| 415 | 418 | {/if} | |
| @@ -417,25 +420,22 @@ $effect(() => { | |||
| 417 | 420 | </div> | |
| 418 | 421 | ||
| 419 | 422 | <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;"> | |
| 420 | - | <!-- Add error message display --> | |
| 421 | 423 | {#if sseError} | |
| 422 | 424 | <div class="error-banner"> | |
| 423 | 425 | {sseError} | |
| 424 | 426 | {#if sseRetryAfter} | |
| 425 | 427 | <br> | |
| 426 | - | <small>Retrying in {Math.ceil(sseRetryAfter)}s...</small> | |
| 428 | + | <small>Nouvelle tentative dans {Math.ceil(sseRetryAfter)}s...</small> | |
| 427 | 429 | {/if} | |
| 428 | 430 | </div> | |
| 429 | 431 | {/if} | |
| 430 | 432 | ||
| 431 | - | <!-- Chat content area --> | |
| 432 | 433 | <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;"> | |
| 433 | - | <!-- Add rate limit warning --> | |
| 434 | 434 | {#if rateLimitWarning} | |
| 435 | 435 | <div class="rate-limit-warning" class:with-progress={cooldownEndTime}> | |
| 436 | 436 | <span>{rateLimitWarning}</span> | |
| 437 | 437 | {#if cooldownEndTime} | |
| 438 | - | <small>You can send another message in {cooldownProgress.toFixed(1)}s</small> | |
| 438 | + | <small>Vous pourrez envoyer un autre message dans {cooldownProgress.toFixed(1)}s</small> | |
| 439 | 439 | {/if} | |
| 440 | 440 | </div> | |
| 441 | 441 | {/if} | |
| @@ -479,14 +479,14 @@ $effect(() => { | |||
| 479 | 479 | bind:value={currentMessage} | |
| 480 | 480 | style="flex: 1;" | |
| 481 | 481 | onkeydown={(e) => e.key === 'Enter' && handleSubmit()} | |
| 482 | - | placeholder={cooldownEndTime ? `Wait ${cooldownProgress.toFixed(1)}s...` : "Type a message..."} | |
| 482 | + | placeholder={cooldownEndTime ? `Patientez ${cooldownProgress.toFixed(1)}s...` : "Écrivez un message..."} | |
| 483 | 483 | disabled={!currentUser || Boolean(cooldownEndTime)} | |
| 484 | 484 | /> | |
| 485 | 485 | <LoadingButton | |
| 486 | 486 | onclick={handleSubmit} | |
| 487 | 487 | disabled={!currentUser || Boolean(cooldownEndTime)} | |
| 488 | 488 | loading={isSendingMessage} | |
| 489 | - | text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Send"} | |
| 489 | + | text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Envoyer"} | |
| 490 | 490 | /> | |
| 491 | 491 | </div> | |
| 492 | 492 | {#if cooldownEndTime} | |
| @@ -501,7 +501,7 @@ $effect(() => { | |||
| 501 | 501 | class:hidden={isMobile && !showUserList} | |
| 502 | 502 | style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;" | |
| 503 | 503 | > | |
| 504 | - | <p style="margin: 0 0 0.3rem 0;"><strong>Buddy List</strong></p> | |
| 504 | + | <p style="margin: 0 0 0.3rem 0;"><strong>Liste d'amis</strong></p> | |
| 505 | 505 | {#each onlineUsers as user} | |
| 506 | 506 | <div class="user" class:offline={user.status === 'offline'}> | |
| 507 | 507 | <span class="status-icon">{getStatusIcon(user.status)}</span> | |
| @@ -513,11 +513,6 @@ $effect(() => { | |||
| 513 | 513 | {:else} | |
| 514 | 514 | <div class="nickname select-none">{user.nickname}</div> | |
| 515 | 515 | {/if} | |
| 516 | - | {#if user.status === 'away'} | |
| 517 | - | <div class="status-message">Away</div> | |
| 518 | - | {:else if user.status === 'busy'} | |
| 519 | - | <div class="status-message">Busy</div> | |
| 520 | - | {/if} | |
| 521 | 516 | </div> | |
| 522 | 517 | </div> | |
| 523 | 518 | {/each} | |
| @@ -527,7 +522,6 @@ $effect(() => { | |||
| 527 | 522 | {/if} | |
| 528 | 523 | ||
| 529 | 524 | <style> | |
| 530 | - | ||
| 531 | 525 | .chat-area, .users-list, input { | |
| 532 | 526 | font-size: 1rem; | |
| 533 | 527 | font-family: Arial, Verdana, Tahoma, sans-serif; | |
| @@ -608,14 +602,14 @@ $effect(() => { | |||
| 608 | 602 | color: #666; | |
| 609 | 603 | } | |
| 610 | 604 | ||
| 611 | - | .user .status-message { | |
| 605 | + | /* .user .status-message { | |
| 612 | 606 | font-size: 0.875rem; | |
| 613 | 607 | color: #666; | |
| 614 | 608 | font-style: italic; | |
| 615 | 609 | overflow: hidden; | |
| 616 | 610 | text-overflow: ellipsis; | |
| 617 | 611 | white-space: nowrap; | |
| 618 | - | } | |
| 612 | + | } */ | |
| 619 | 613 | ||
| 620 | 614 | .sunken-panel { | |
| 621 | 615 | background: white; | |
| @@ -369,6 +369,9 @@ $effect(() => { | |||||
| 369 | isInitialLoading = false; | 369 | isInitialLoading = false; | ||
| 370 | } | 370 | } | ||
| 371 | }); | 371 | }); | ||
| 372 | + | ||||
| 373 | + | // Add derived state for total users count | |||
| 374 | + | let totalUsers = $derived(onlineUsers.length); | |||
| 372 | </script> | 375 | </script> | ||
| 373 | 376 | ||||
| 374 | {#if showChatRoom} | 377 | {#if showChatRoom} | ||
| @@ -393,9 +396,9 @@ $effect(() => { | |||||
| 393 | > | 396 | > | ||
| 394 | <div class="title-bar"> | 397 | <div class="title-bar"> | ||
| 395 | <div class="title-bar-text"> | 398 | <div class="title-bar-text"> | ||
| 396 | - | Pdr Aim - {currentUser?.nickname} | 399 | + | Pdr Aim {#if currentUser} - {currentUser.nickname}{:else} - {totalUsers} personne{totalUsers > 1 ? 's' : ''} présente{totalUsers > 1 ? 's' : ''}{/if} |
| 397 | {#if sseError} | 400 | {#if sseError} | ||
| 398 | - | <span class="connection-error">⚠️ Connection Error</span> | 401 | + | <span class="connection-error">⚠️ Erreur de connexion</span> |
| 399 | {/if} | 402 | {/if} | ||
| 400 | </div> | 403 | </div> | ||
| 401 | <div class="title-bar-controls"> | 404 | <div class="title-bar-controls"> | ||
| @@ -409,7 +412,7 @@ $effect(() => { | |||||
| 409 | <button onclick={handleClose} aria-label="Close"></button> | 412 | <button onclick={handleClose} aria-label="Close"></button> | ||
| 410 | {:else} | 413 | {:else} | ||
| 411 | <button | 414 | <button | ||
| 412 | - | aria-label="Toggle Buddy List" | 415 | + | aria-label="Afficher/Masquer la liste des contacts" |
| 413 | onclick={() => showUserList = !showUserList} | 416 | onclick={() => showUserList = !showUserList} | ||
| 414 | >👥</button> | 417 | >👥</button> | ||
| 415 | {/if} | 418 | {/if} | ||
| @@ -417,25 +420,22 @@ $effect(() => { | |||||
| 417 | </div> | 420 | </div> | ||
| 418 | 421 | ||||
| 419 | <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;"> | 422 | <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;"> | ||
| 420 | - | <!-- Add error message display --> | |||
| 421 | {#if sseError} | 423 | {#if sseError} | ||
| 422 | <div class="error-banner"> | 424 | <div class="error-banner"> | ||
| 423 | {sseError} | 425 | {sseError} | ||
| 424 | {#if sseRetryAfter} | 426 | {#if sseRetryAfter} | ||
| 425 | <br> | 427 | <br> | ||
| 426 | - | <small>Retrying in {Math.ceil(sseRetryAfter)}s...</small> | 428 | + | <small>Nouvelle tentative dans {Math.ceil(sseRetryAfter)}s...</small> |
| 427 | {/if} | 429 | {/if} | ||
| 428 | </div> | 430 | </div> | ||
| 429 | {/if} | 431 | {/if} | ||
| 430 | 432 | ||||
| 431 | - | <!-- Chat content area --> | |||
| 432 | <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;"> | 433 | <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;"> | ||
| 433 | - | <!-- Add rate limit warning --> | |||
| 434 | {#if rateLimitWarning} | 434 | {#if rateLimitWarning} | ||
| 435 | <div class="rate-limit-warning" class:with-progress={cooldownEndTime}> | 435 | <div class="rate-limit-warning" class:with-progress={cooldownEndTime}> | ||
| 436 | <span>{rateLimitWarning}</span> | 436 | <span>{rateLimitWarning}</span> | ||
| 437 | {#if cooldownEndTime} | 437 | {#if cooldownEndTime} | ||
| 438 | - | <small>You can send another message in {cooldownProgress.toFixed(1)}s</small> | 438 | + | <small>Vous pourrez envoyer un autre message dans {cooldownProgress.toFixed(1)}s</small> |
| 439 | {/if} | 439 | {/if} | ||
| 440 | </div> | 440 | </div> | ||
| 441 | {/if} | 441 | {/if} | ||
| @@ -479,14 +479,14 @@ $effect(() => { | |||||
| 479 | bind:value={currentMessage} | 479 | bind:value={currentMessage} | ||
| 480 | style="flex: 1;" | 480 | style="flex: 1;" | ||
| 481 | onkeydown={(e) => e.key === 'Enter' && handleSubmit()} | 481 | onkeydown={(e) => e.key === 'Enter' && handleSubmit()} | ||
| 482 | - | placeholder={cooldownEndTime ? `Wait ${cooldownProgress.toFixed(1)}s...` : "Type a message..."} | 482 | + | placeholder={cooldownEndTime ? `Patientez ${cooldownProgress.toFixed(1)}s...` : "Écrivez un message..."} |
| 483 | disabled={!currentUser || Boolean(cooldownEndTime)} | 483 | disabled={!currentUser || Boolean(cooldownEndTime)} | ||
| 484 | /> | 484 | /> | ||
| 485 | <LoadingButton | 485 | <LoadingButton | ||
| 486 | onclick={handleSubmit} | 486 | onclick={handleSubmit} | ||
| 487 | disabled={!currentUser || Boolean(cooldownEndTime)} | 487 | disabled={!currentUser || Boolean(cooldownEndTime)} | ||
| 488 | loading={isSendingMessage} | 488 | loading={isSendingMessage} | ||
| 489 | - | text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Send"} | 489 | + | text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Envoyer"} |
| 490 | /> | 490 | /> | ||
| 491 | </div> | 491 | </div> | ||
| 492 | {#if cooldownEndTime} | 492 | {#if cooldownEndTime} | ||
| @@ -501,7 +501,7 @@ $effect(() => { | |||||
| 501 | class:hidden={isMobile && !showUserList} | 501 | class:hidden={isMobile && !showUserList} | ||
| 502 | style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;" | 502 | style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;" | ||
| 503 | > | 503 | > | ||
| 504 | - | <p style="margin: 0 0 0.3rem 0;"><strong>Buddy List</strong></p> | 504 | + | <p style="margin: 0 0 0.3rem 0;"><strong>Liste d'amis</strong></p> |
| 505 | {#each onlineUsers as user} | 505 | {#each onlineUsers as user} | ||
| 506 | <div class="user" class:offline={user.status === 'offline'}> | 506 | <div class="user" class:offline={user.status === 'offline'}> | ||
| 507 | <span class="status-icon">{getStatusIcon(user.status)}</span> | 507 | <span class="status-icon">{getStatusIcon(user.status)}</span> | ||
| @@ -513,11 +513,6 @@ $effect(() => { | |||||
| 513 | {:else} | 513 | {:else} | ||
| 514 | <div class="nickname select-none">{user.nickname}</div> | 514 | <div class="nickname select-none">{user.nickname}</div> | ||
| 515 | {/if} | 515 | {/if} | ||
| 516 | - | {#if user.status === 'away'} | |||
| 517 | - | <div class="status-message">Away</div> | |||
| 518 | - | {:else if user.status === 'busy'} | |||
| 519 | - | <div class="status-message">Busy</div> | |||
| 520 | - | {/if} | |||
| 521 | </div> | 516 | </div> | ||
| 522 | </div> | 517 | </div> | ||
| 523 | {/each} | 518 | {/each} | ||
| @@ -527,7 +522,6 @@ $effect(() => { | |||||
| 527 | {/if} | 522 | {/if} | ||
| 528 | 523 | ||||
| 529 | <style> | 524 | <style> | ||
| 530 | - | ||||
| 531 | .chat-area, .users-list, input { | 525 | .chat-area, .users-list, input { | ||
| 532 | font-size: 1rem; | 526 | font-size: 1rem; | ||
| 533 | font-family: Arial, Verdana, Tahoma, sans-serif; | 527 | font-family: Arial, Verdana, Tahoma, sans-serif; | ||
| @@ -608,14 +602,14 @@ $effect(() => { | |||||
| 608 | color: #666; | 602 | color: #666; | ||
| 609 | } | 603 | } | ||
| 610 | 604 | ||||
| 611 | - | .user .status-message { | 605 | + | /* .user .status-message { |
| 612 | font-size: 0.875rem; | 606 | font-size: 0.875rem; | ||
| 613 | color: #666; | 607 | color: #666; | ||
| 614 | font-style: italic; | 608 | font-style: italic; | ||
| 615 | overflow: hidden; | 609 | overflow: hidden; | ||
| 616 | text-overflow: ellipsis; | 610 | text-overflow: ellipsis; | ||
| 617 | white-space: nowrap; | 611 | white-space: nowrap; | ||
| 618 | - | } | 612 | + | } */ |
| 619 | 613 | ||||
| 620 | .sunken-panel { | 614 | .sunken-panel { | ||
| 621 | background: white; | 615 | background: white; | ||
M src/lib/components/desktop-icons.svelte +3 -3
| @@ -25,7 +25,7 @@ | |||
| 25 | 25 | role="button" | |
| 26 | 26 | tabindex="0" | |
| 27 | 27 | > | |
| 28 | - | <img src="/desktop/aim-desktop-icon.png" alt="Chat Room" /> | |
| 28 | + | <img src="/desktop/aim-desktop-icon.png" alt="Salon de discussion" /> | |
| 29 | 29 | <span class="icon-label">Pdr Aim</span> | |
| 30 | 30 | </div> | |
| 31 | 31 | ||
| @@ -36,8 +36,8 @@ | |||
| 36 | 36 | role="button" | |
| 37 | 37 | tabindex="0" | |
| 38 | 38 | > | |
| 39 | - | <img src="/desktop/application-x-shellscript.png" alt="Sign In" /> | |
| 40 | - | <span class="icon-label">login</span> | |
| 39 | + | <img src="/desktop/application-x-shellscript.png" alt="Connexion" /> | |
| 40 | + | <span class="icon-label">Connexion</span> | |
| 41 | 41 | </div> | |
| 42 | 42 | </div> | |
| 43 | 43 | ||
| @@ -25,7 +25,7 @@ | |||||
| 25 | role="button" | 25 | role="button" | ||
| 26 | tabindex="0" | 26 | tabindex="0" | ||
| 27 | > | 27 | > | ||
| 28 | - | <img src="/desktop/aim-desktop-icon.png" alt="Chat Room" /> | 28 | + | <img src="/desktop/aim-desktop-icon.png" alt="Salon de discussion" /> |
| 29 | <span class="icon-label">Pdr Aim</span> | 29 | <span class="icon-label">Pdr Aim</span> | ||
| 30 | </div> | 30 | </div> | ||
| 31 | 31 | ||||
| @@ -36,8 +36,8 @@ | |||||
| 36 | role="button" | 36 | role="button" | ||
| 37 | tabindex="0" | 37 | tabindex="0" | ||
| 38 | > | 38 | > | ||
| 39 | - | <img src="/desktop/application-x-shellscript.png" alt="Sign In" /> | 39 | + | <img src="/desktop/application-x-shellscript.png" alt="Connexion" /> |
| 40 | - | <span class="icon-label">login</span> | 40 | + | <span class="icon-label">Connexion</span> |
| 41 | </div> | 41 | </div> | ||
| 42 | </div> | 42 | </div> | ||
| 43 | 43 | ||||
M src/lib/components/ui/button-loading.svelte +2 -2
| @@ -2,10 +2,10 @@ | |||
| 2 | 2 | import { onDestroy } from 'svelte'; | |
| 3 | 3 | ||
| 4 | 4 | // Props with defaults: | |
| 5 | - | // - text: button label when not loading (default "Send") | |
| 5 | + | // - text: button label when not loading (default "Envoyer") | |
| 6 | 6 | // - loading: indicates if the button should show a spinner (default false) | |
| 7 | 7 | // - spinnerChar: the character to use for the spinner animation (default '.') | |
| 8 | - | let { onclick, disabled = $bindable(false), text, loading = $bindable(false), spinnerChar = $bindable('.') } = $props(); | |
| 8 | + | let { onclick, disabled = $bindable(false), text = 'Envoyer', loading = $bindable(false), spinnerChar = $bindable('.') } = $props(); | |
| 9 | 9 | ||
| 10 | 10 | // Using $state for internal reactive variables. | |
| 11 | 11 | let animatedDots = $state(''); // will hold the animated spinner text | |
| @@ -2,10 +2,10 @@ | |||||
| 2 | import { onDestroy } from 'svelte'; | 2 | import { onDestroy } from 'svelte'; | ||
| 3 | 3 | ||||
| 4 | // Props with defaults: | 4 | // Props with defaults: | ||
| 5 | - | // - text: button label when not loading (default "Send") | 5 | + | // - text: button label when not loading (default "Envoyer") |
| 6 | // - loading: indicates if the button should show a spinner (default false) | 6 | // - loading: indicates if the button should show a spinner (default false) | ||
| 7 | // - spinnerChar: the character to use for the spinner animation (default '.') | 7 | // - spinnerChar: the character to use for the spinner animation (default '.') | ||
| 8 | - | let { onclick, disabled = $bindable(false), text, loading = $bindable(false), spinnerChar = $bindable('.') } = $props(); | 8 | + | let { onclick, disabled = $bindable(false), text = 'Envoyer', loading = $bindable(false), spinnerChar = $bindable('.') } = $props(); |
| 9 | 9 | ||||
| 10 | // Using $state for internal reactive variables. | 10 | // Using $state for internal reactive variables. | ||
| 11 | let animatedDots = $state(''); // will hold the animated spinner text | 11 | let animatedDots = $state(''); // will hold the animated spinner text | ||
M src/lib/components/ui/loading-dots.svelte +1 -1
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | <!-- A reusable loading dots component with animated dots --> | |
| 2 | 2 | <script lang="ts"> | |
| 3 | - | let { text = 'Loading', color = 'inherit' } = $props(); | |
| 3 | + | let { text = 'Chargement', color = 'inherit' } = $props(); | |
| 4 | 4 | </script> | |
| 5 | 5 | ||
| 6 | 6 | <div class="loading-text" style="color: {color}"> | |
| @@ -1,6 +1,6 @@ | |||||
| 1 | <!-- A reusable loading dots component with animated dots --> | 1 | <!-- A reusable loading dots component with animated dots --> | ||
| 2 | <script lang="ts"> | 2 | <script lang="ts"> | ||
| 3 | - | let { text = 'Loading', color = 'inherit' } = $props(); | 3 | + | let { text = 'Chargement', color = 'inherit' } = $props(); |
| 4 | </script> | 4 | </script> | ||
| 5 | 5 | ||||
| 6 | <div class="loading-text" style="color: {color}"> | 6 | <div class="loading-text" style="color: {color}"> | ||