Conversation
Code Review SummaryThis PR successfully internationalizes the application using 🚀 Key Improvements
💡 Minor Suggestions
🚨 Critical Issues
|
| error.value = ""; | ||
| const normalizedApiKey = apiKey.trim(); | ||
|
|
||
| if (!normalizedApiKey) { |
There was a problem hiding this comment.
The error message 'API key is required' is still hardcoded while the rest of the app has been internationalized. This should use the $t function or be handled in the view layer with a translation key.
| if (!normalizedApiKey) { | |
| if (!normalizedApiKey) { | |
| error.value = "auth.errors.apiKeyRequired"; | |
| loading.value = false; | |
| return false; | |
| } |
| @@ -951,12 +974,18 @@ const deleteCredential = async () => { | |||
| try { | |||
There was a problem hiding this comment.
When calling templatesApi.refresh(), the response potentially contains a count or message that is being ignored in favor of a static notification. It's better to verify if the count needs to be passed to the translation string.
| try { | |
| const response = await templatesApi.refresh(); | |
| notifications.success( | |
| t("settings.general.quickActions.templatesRefreshedTitle"), | |
| t("settings.general.quickActions.templatesRefreshedDesc", { n: response.data.count || 0 }) | |
| ); |
nfebe
left a comment
There was a problem hiding this comment.
The refactoring in CI is not related. Please update the PR (resolve conflicts and update the video)
This looks quite good! Thank you
cc9d209 to
8a8e623
Compare
| return date.toLocaleString(); | ||
| }; | ||
|
|
||
| const formatBackupStatus = (status: string): string => { |
There was a problem hiding this comment.
The helper function formatBackupStatus attempts to translate status strings but falls back to a manual string replacement. It's safer to ensure all possible statuses are defined in the locale files to avoid inconsistent UI labels.
| const formatBackupStatus = (status: string): string => { | |
| const formatBackupStatus = (status: string): string => { | |
| const key = `deployment.backups.status.${status}`; | |
| return te(key) ? t(key) : t('deployment.backups.status.unknown'); | |
| }; |
| <template> | ||
| <div class="language-dropdown" @keydown.esc="closeMenu"> | ||
| <button class="language-trigger" type="button" :aria-expanded="isOpen" aria-haspopup="menu" @click="toggleMenu"> | ||
| <img class="language-flag" :src="currentLanguage.flagPath" :alt="currentLanguage.code" /> | ||
| <span class="language-label">{{ currentLanguage.label }}</span> | ||
| <i class="pi pi-chevron-down" /> | ||
| </button> | ||
| <div v-if="isOpen" class="language-menu" role="menu"> | ||
| <button |
There was a problem hiding this comment.
The new LanguageSelector component lacks accessibility attributes for the list items. Using role="menu" on the container is good, but the trigger and items should have appropriate ARIA labels and keyboard navigation handling beyond just 'Esc'.
| <template> | |
| <div class="language-dropdown" @keydown.esc="closeMenu"> | |
| <button class="language-trigger" type="button" :aria-expanded="isOpen" aria-haspopup="menu" @click="toggleMenu"> | |
| <img class="language-flag" :src="currentLanguage.flagPath" :alt="currentLanguage.code" /> | |
| <span class="language-label">{{ currentLanguage.label }}</span> | |
| <i class="pi pi-chevron-down" /> | |
| </button> | |
| <div v-if="isOpen" class="language-menu" role="menu"> | |
| <button | |
| <button | |
| v-for="lang in languages" | |
| :key="lang.code" | |
| class="language-item" | |
| type="button" | |
| role="menuitem" | |
| :aria-label="'Switch to ' + lang.label" | |
| @click="selectLanguage(lang)" | |
| > |
8a8e623 to
38c18ad
Compare
| >? | ||
| </p> | ||
| <p v-if="fileToDelete?.is_dir" class="warning-text">This will delete all contents inside the folder.</p> | ||
| <!-- eslint-disable vue/no-v-html --> |
There was a problem hiding this comment.
Using v-html for simple string interpolation is a security risk (XSS) and unnecessary for this use case. Use standard interpolation or the <i18n-t> component if specific formatting is needed.
| <!-- eslint-disable vue/no-v-html --> | |
| <p>{{ $t('deployment.files.modal.delete.confirm', { name: fileToDelete?.name }) }}</p> |
38c18ad to
42a7f47
Compare
| const formatBackupStatus = (status: string): string => { | ||
| const key = `deployment.backups.status.${status}`; | ||
| if (te(key)) return t(key); | ||
| return status.replace(/_/g, " "); |
There was a problem hiding this comment.
When a translation is missing, the fallback logic simply replaces underscores. This is a good safety measure, but it's often better to title-case the result for a cleaner UI.
| return status.replace(/_/g, " "); | |
| return status.replace(/_/g, " ").replace(/\b\w/g, l => l.toUpperCase()); |
| @@ -0,0 +1,148 @@ | |||
| <template> | |||
| <div class="language-dropdown" @keydown.esc="closeMenu"> | |||
There was a problem hiding this comment.
The container uses keydown.esc but lacks a blur/outside click handler. If a user clicks away from the menu, it will stay open. Adding a simple directive or global listener would improve UX.
| <div class="language-dropdown" @keydown.esc="closeMenu"> | |
| <div class="language-dropdown" v-click-outside="closeMenu" @keydown.esc="closeMenu"> |
| return t("security.events.dynamicType", { type: humanized }); | ||
| }; | ||
|
|
||
| const normalizeEventToken = (value?: string) => |
There was a problem hiding this comment.
The manual string manipulation for token normalization is repeated. Consider moving this to a central utility file since it is used in multiple components (Backups, Security, Services).
| const normalizeEventToken = (value?: string) => | |
| const normalizeEventToken = (value?: string) => | |
| normalizeToken(value); |
Changes to CI file removed, conflicts resolved and video updated. |
This PR handles a slight setup documentation modification and handles translation to locale languages.
Screencast.From.2026-03-26.14-43-44.mp4