feat: add Dutch (nl) locale and fix missing translation keys across all locales

Add complete Dutch translation (2054 keys) with Netherlands flag in the
language selector. Also synchronize all existing locales so every language
has the same set of keys: added 29 missing keys to EN/RU/PT and 95 missing
keys to DE (moderation, analytics, CSS templates, backup, events).
This commit is contained in:
Paul Nothaft
2026-03-25 22:35:57 +01:00
parent 2ac6c51fe5
commit b54a80d251
7 changed files with 2835 additions and 615 deletions
@@ -38,11 +38,20 @@ const PTBRFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) =
</svg>
);
const NLFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) => (
<svg className={className} viewBox="0 0 640 480" xmlns="http://www.w3.org/2000/svg">
<path fill="#AE1C28" d="M0 0h640v160H0z"/>
<path fill="#FFF" d="M0 160h640v160H0z"/>
<path fill="#21468B" d="M0 320h640v160H0z"/>
</svg>
);
const languages = [
{ code: 'en', name: 'English', Flag: GBFlag },
{ code: 'de', name: 'Deutsch', Flag: DEFlag },
{ code: 'ru', name: 'Русский', Flag: RUFlag },
{ code: 'pt', name: 'Português', Flag: PTBRFlag },
{ code: 'nl', name: 'Nederlands', Flag: NLFlag },
];
export const LanguageSelector: React.FC = () => {
+5 -1
View File
@@ -7,6 +7,7 @@ import enTranslations from './locales/en.json';
import deTranslations from './locales/de.json';
import ruTranslations from './locales/ru.json';
import ptTranslations from './locales/pt.json';
import nlTranslations from './locales/nl.json';
i18n
.use(HttpBackend)
@@ -15,7 +16,7 @@ i18n
.init({
fallbackLng: 'en',
debug: false,
resources: {
en: {
translation: enTranslations,
@@ -29,6 +30,9 @@ i18n
pt: {
translation: ptTranslations,
},
nl: {
translation: nlTranslations,
},
},
interpolation: {
File diff suppressed because it is too large Load Diff
+72 -66
View File
@@ -224,10 +224,10 @@
"dayRemaining": "1 day remaining",
"hoursRemaining": "{{hours}} hours remaining",
"expiredMessage": "This gallery expired on {{date}}",
"contactOrganizer": "Please contact the event organizer if you need access to these photos",
"contactOrganizer": "Please contact the event organizer if you need access to these photos.",
"searchPhotos": "Search photos by filename...",
"sortByDate": "Sort by Date",
"sortByName": "Sort by Name",
"sortByName": "Sort by Name",
"sortBySize": "Sort by Size",
"allPhotos": "All Photos",
"filter": "Filter",
@@ -236,7 +236,7 @@
"liked": "Liked",
"favorited": "Favorited",
"favorites": "Favorites",
"downloadSelected": "Download Selected",
"downloadSelected": "Download {{count}} Selected",
"shareGallery": "Share Gallery",
"needHelp": "Need help? Contact us at",
"noPhotosFound": "No photos found",
@@ -244,7 +244,6 @@
"tryAgain": "Try Again",
"loading": "Loading gallery...",
"expiredOn": "This gallery expired on {{date}}.",
"contactOrganizer": "Please contact the event organizer if you need access to these photos.",
"expiresIn": "Gallery expires in {{count}} day",
"expiresIn_plural": "Gallery expires in {{count}} days",
"downloadBefore": "Download your photos before they're no longer available.",
@@ -262,7 +261,6 @@
"photosSelected": "{{count}} selected",
"selectAll": "Select All",
"deselectAll": "Deselect All",
"downloadSelected": "Download {{count}} Selected",
"deleteSelected": "Delete Selected",
"photosCount": "{{count}} photo",
"photosCount_plural": "{{count}} photos",
@@ -286,10 +284,7 @@
"noMedia": "No media uploaded yet",
"searchPlaceholder": "Search photos...",
"sortBy": "Sort By",
"sortByDate": "Sort by Date",
"sortByCaptureDate": "Sort by Capture Date",
"sortByName": "Sort by Name",
"sortBySize": "Sort by Size",
"sortByRating": "Sort by Rating",
"photoGallery": "Photo Gallery",
"photos": "Photos",
@@ -308,7 +303,9 @@
"writeLovelyNote": "Write a lovely note...",
"postComment": "Post Comment",
"anonymous": "Anonymous"
}
},
"rated": "Rated",
"commented": "Commented"
},
"categories": {
"title": "Photo Categories",
@@ -429,7 +426,6 @@
"statistics": "Statistics",
"views": "Views",
"downloads": "Downloads",
"uniqueVisitors": "Unique Visitors",
"noStatistics": "No statistics available yet",
"archiveStatus": "Archive Status",
"archivedOn": "Archived On",
@@ -437,7 +433,7 @@
"loadingPhotos": "Loading photos...",
"photoCategories": "Photo Categories",
"organizeCategoriesInfo": "Organize your photos into categories. Categories help guests navigate and find specific types of photos.",
"categoriesTip": "Tip: Categories are specific to each event. You can also create global categories in Settings.",
"categoriesTip": "Tip: Categories are specific to each event. You can create custom categories like \"Ceremony\", \"Reception\", \"Portraits\", etc.",
"contactInformation": "Contact Information",
"hostEmailHelp": "Customer will receive gallery creation and expiration notifications",
"adminEmailHelp": "Will receive system notifications and archive confirmations",
@@ -534,8 +530,8 @@
"days365": "1 year",
"inactive": "Inactive",
"expired": "Expired",
"daysLeft": "{{count}}d left",
"daysLeft_plural": "{{count}}d left",
"daysLeft": "({{count}} day left)",
"daysLeft_plural": "({{count}} days left)",
"subtitle": "Manage your photo galleries and events",
"loadingEvents": "Loading events...",
"failedToLoadEvents": "Failed to load events",
@@ -556,8 +552,6 @@
"type": "Type",
"date": "Date",
"status": "Status",
"expires": "Expires",
"actions": "Actions",
"noEventsFound": "No events found",
"stats": {
"totalEvents": "Total Events",
@@ -571,31 +565,17 @@
"deleteEvent": "Delete Event",
"deleteEventConfirm": "Are you sure you want to delete this event?",
"downloadArchiveSoon": "Download archive coming soon",
"failedToLoadEvents": "Failed to load events",
"tryAgain": "Try Again",
"eventExpiredMessage": "This event has expired",
"guestsCannotAccessGallery": "Guests can no longer access the gallery. Consider archiving this event.",
"warningEmailsHaveBeenSent": "Warning emails have been sent to the customer.",
"extendSevenDays": "Extend 7 Days",
"overview": "Overview",
"eventInformation": "Event Information",
"welcomeMessageLabel": "Welcome Message",
"noWelcomeMessageSet": "No welcome message set",
"hostEmail": "Customer Email",
"adminEmail": "Admin Email",
"createdOn": "Created",
"expires": "Expires",
"daysLeft": "({{count}} day left)",
"daysLeft_plural": "({{count}} days left)",
"shareLink": "Share Link",
"copy": "Copy",
"copied": "Copied!",
"uploadPhotos": "Upload Photos",
"photoCategories": "Photo Categories",
"organizingPhotosInfo": "Organize your photos into categories. Categories help guests navigate and find specific types of photos.",
"categoriesTip": "Tip: Categories are specific to each event. You can create custom categories like \"Ceremony\", \"Reception\", \"Portraits\", etc.",
"archiveStatusTitle": "Archive Status",
"archivedOn": "Archived On",
"downloadingArchive": "Downloading {{name}} archive...",
"downloadStarted": "Download started",
"failedToDownloadArchive": "Failed to download archive",
@@ -622,7 +602,24 @@
"failed": "Rename failed",
"filesRenamed": "{{count}} files updated",
"confirm": "Rename Event"
}
},
"activeFilter": "Active",
"archivedFilter": "Archived",
"sortByName": "By Name",
"sortByDate": "By Date",
"sortByExpiration": "By Expiration",
"photosCount": "Photos",
"moreActions": "More Actions",
"copyLinkTooltip": "Copy Link",
"viewGalleryTooltip": "View Gallery",
"uploadPhotosTooltip": "Upload Photos",
"editTooltip": "Edit",
"archiveTooltip": "Archive",
"noEvents": "No events found",
"noEventsDescription": "Create your first event to get started.",
"bulkArchive": "Archive",
"confirmBulkArchive": "Are you sure you want to archive {{count}} event(s)?",
"confirmBulkArchiveDescription": "This action cannot be undone. Archived events will no longer be publicly accessible."
},
"settings": {
"title": "System Settings",
@@ -1005,7 +1002,7 @@
"fullDashboard": "Full Dashboard",
"refresh": "Refresh",
"last7Days": "Last 7 days",
"last30Days": "Last 30 days",
"last30Days": "Last 30 days",
"last90Days": "Last 90 days",
"pageViews": "Page Views",
"uniqueVisitors": "Unique Visitors",
@@ -1049,7 +1046,7 @@
"uploadLogo": "Upload Logo",
"removeLogo": "Remove Logo",
"logoHelp": "Recommended size: 200x60px, PNG or JPEG",
"favicon": "Favicon",
"favicon": "Favicon",
"currentFavicon": "Current favicon",
"uploadFavicon": "Upload Favicon",
"removeFavicon": "Remove Favicon",
@@ -1671,8 +1668,6 @@
"username": "Username",
"password": "Password",
"enterPassword": "Enter password",
"fromEmail": "From Email",
"fromName": "From Name",
"required": "required",
"ignoreSslErrors": "Ignore SSL/TLS certificate errors",
"ignoreSslWarning": "Warning: Disabling certificate verification makes the connection vulnerable to man-in-the-middle attacks. Only enable this if you trust the SMTP server and understand the security implications.",
@@ -1823,7 +1818,9 @@
"upToDate": "Backup is up to date",
"recent": "Backup is recent",
"gettingOld": "Backup is getting old",
"outdated": "Backup is outdated"
"outdated": "Backup is outdated",
"failed": "Last backup failed",
"old": "Backup is getting old"
},
"stats": {
"totalBackups": "Total Backups",
@@ -2000,7 +1997,15 @@
"duration": "Duration",
"actions": "Actions"
},
"details": "Details",
"details": {
"backupDetails": "Backup Details",
"destination": "Destination",
"started": "Started",
"completed": "Completed",
"contentBackedUp": "Content Backed Up",
"errorDetails": "Error Details",
"manifest": "Manifest"
},
"statistics": "Statistics",
"errors": "Errors",
"backupDetails": {
@@ -2030,15 +2035,6 @@
"backupsWillAppear": "Backups will appear here once created",
"messages": {
"deleteSuccess": "Backup deleted successfully"
},
"details": {
"backupDetails": "Backup Details",
"destination": "Destination",
"started": "Started",
"completed": "Completed",
"contentBackedUp": "Content Backed Up",
"errorDetails": "Error Details",
"manifest": "Manifest"
}
},
"restore": {
@@ -2083,26 +2079,26 @@
"at": "at"
},
"restoreTypes": {
"full": {
"name": "Full Restore",
"description": "Restore everything including database, photos, and archives",
"warning": "This will replace all current data"
},
"database": {
"name": "Database Only",
"description": "Restore only the database (settings, events, users)",
"warning": "Current database will be replaced"
},
"files": {
"name": "Files Only",
"description": "Restore only photos and archives",
"warning": "Existing files may be overwritten"
},
"selective": {
"name": "Selective Restore",
"description": "Choose specific items to restore",
"warning": "Only selected items will be restored"
}
"full": {
"name": "Full Restore",
"description": "Restore everything including database, photos, and archives",
"warning": "This will replace all current data"
},
"database": {
"name": "Database Only",
"description": "Restore only the database (settings, events, users)",
"warning": "Current database will be replaced"
},
"files": {
"name": "Files Only",
"description": "Restore only photos and archives",
"warning": "Existing files may be overwritten"
},
"selective": {
"name": "Selective Restore",
"description": "Choose specific items to restore",
"warning": "Only selected items will be restored"
}
},
"options": {
"title": "Restore Options",
@@ -2328,7 +2324,9 @@
"fiveStarsOnly": "5 Stars Only",
"hasLikes": "Has likes",
"hasFavorites": "Has favorites",
"hasComments": "Has comments"
"hasComments": "Has comments",
"showingPhotos": "Total photos",
"withRatings": "With ratings"
},
"adminLogin": {
"title": "Admin Login",
@@ -2378,5 +2376,13 @@
"linkLabel": "Client Access Link",
"regenerateToken": "Regenerate link",
"tokenRegenerated": "Client access link regenerated"
},
"export": {
"button": "Export",
"success": "Export downloaded successfully",
"error": "Export failed: ",
"exportSelected": "Export {{count}} selected",
"exportFiltered": "Export filtered photos",
"hint": "Select photos or apply filters to export"
}
}
File diff suppressed because it is too large Load Diff
+35 -4
View File
@@ -303,7 +303,9 @@
"writeLovelyNote": "Escreva uma mensagem carinhosa...",
"postComment": "Publicar Comentário",
"anonymous": "Anônimo"
}
},
"rated": "Avaliado",
"commented": "Comentado"
},
"categories": {
"title": "Categorias de Fotos",
@@ -600,7 +602,24 @@
"failed": "Falha ao renomear",
"filesRenamed": "{{count}} arquivos atualizados",
"confirm": "Renomear Evento"
}
},
"activeFilter": "Ativos",
"archivedFilter": "Arquivados",
"sortByName": "Por nome",
"sortByDate": "Por data",
"sortByExpiration": "Por expiração",
"photosCount": "Fotos",
"moreActions": "Mais ações",
"copyLinkTooltip": "Copiar link",
"viewGalleryTooltip": "Ver galeria",
"uploadPhotosTooltip": "Enviar fotos",
"editTooltip": "Editar",
"archiveTooltip": "Arquivar",
"noEvents": "Nenhum evento encontrado",
"noEventsDescription": "Crie seu primeiro evento para começar.",
"bulkArchive": "Arquivar",
"confirmBulkArchive": "Tem certeza de que deseja arquivar {{count}} evento(s)?",
"confirmBulkArchiveDescription": "Esta ação não pode ser desfeita. Eventos arquivados não serão mais acessíveis publicamente."
},
"settings": {
"title": "Configurações do Sistema",
@@ -1799,7 +1818,9 @@
"upToDate": "Backup está atualizado",
"recent": "Backup é recente",
"gettingOld": "Backup está ficando antigo",
"outdated": "Backup está desatualizado"
"outdated": "Backup está desatualizado",
"failed": "Último backup falhou",
"old": "Backup está ficando antigo"
},
"stats": {
"totalBackups": "Total de Backups",
@@ -2303,7 +2324,9 @@
"fiveStarsOnly": "Apenas 5 Estrelas",
"hasLikes": "Tem curtidas",
"hasFavorites": "Tem favoritos",
"hasComments": "Tem comentários"
"hasComments": "Tem comentários",
"showingPhotos": "Total de fotos",
"withRatings": "Com avaliações"
},
"adminLogin": {
"title": "Login de Administrador",
@@ -2353,5 +2376,13 @@
"linkLabel": "Link de Acesso do Cliente",
"regenerateToken": "Regenerar link",
"tokenRegenerated": "Link de acesso do cliente regenerado"
},
"export": {
"button": "Exportar",
"success": "Exportação baixada com sucesso",
"error": "Falha na exportação: ",
"exportSelected": "Exportar {{count}} selecionados",
"exportFiltered": "Exportar fotos filtradas",
"hint": "Selecione fotos ou aplique filtros para exportar"
}
}
+36 -5
View File
@@ -303,7 +303,9 @@
"writeLovelyNote": "Напишите тёплые слова...",
"postComment": "Оставить комментарий",
"anonymous": "Аноним"
}
},
"rated": "Оценено",
"commented": "Прокомментировано"
},
"categories": {
"title": "Категории фото",
@@ -600,7 +602,24 @@
"failed": "Ошибка переименования",
"filesRenamed": "Обновлено {{count}} файлов",
"confirm": "Переименовать событие"
}
},
"activeFilter": "Активные",
"archivedFilter": "Архивированные",
"sortByName": "По имени",
"sortByDate": "По дате",
"sortByExpiration": "По сроку",
"photosCount": "Фото",
"moreActions": "Ещё",
"copyLinkTooltip": "Копировать ссылку",
"viewGalleryTooltip": "Просмотр галереи",
"uploadPhotosTooltip": "Загрузить фото",
"editTooltip": "Редактировать",
"archiveTooltip": "Архивировать",
"noEvents": "События не найдены",
"noEventsDescription": "Создайте своё первое событие, чтобы начать.",
"bulkArchive": "Архивировать",
"confirmBulkArchive": "Вы уверены, что хотите архивировать {{count}} событие(й)?",
"confirmBulkArchiveDescription": "Это действие нельзя отменить. Архивированные события больше не будут доступны публично."
},
"settings": {
"title": "Системные настройки",
@@ -1799,7 +1818,9 @@
"upToDate": "Резервная копия актуальна",
"recent": "Резервная копия свежая",
"gettingOld": "Резервная копия устаревает",
"outdated": "Резервная копия устарела"
"outdated": "Резервная копия устарела",
"failed": "Последнее резервное копирование не удалось",
"old": "Резервная копия устаревает"
},
"stats": {
"totalBackups": "Всего копий",
@@ -2303,7 +2324,9 @@
"fiveStarsOnly": "Только 5 звёзд",
"hasLikes": "Есть лайки",
"hasFavorites": "Есть в избранном",
"hasComments": "Есть комментарии"
"hasComments": "Есть комментарии",
"showingPhotos": "Всего фото",
"withRatings": "С оценками"
},
"adminLogin": {
"title": "Вход для администратора",
@@ -2353,5 +2376,13 @@
"linkLabel": "Ссылка для клиента",
"regenerateToken": "Сгенерировать новую ссылку",
"tokenRegenerated": "Ссылка для клиента обновлена"
},
"export": {
"button": "Экспорт",
"success": "Экспорт успешно загружен",
"error": "Ошибка экспорта: ",
"exportSelected": "Экспортировать {{count}} выбранных",
"exportFiltered": "Экспортировать отфильтрованные фото",
"hint": "Выберите фото или примените фильтры для экспорта"
}
}
}