From 42c94fcd906b770ec999ad8a328a98a11b6de492 Mon Sep 17 00:00:00 2001 From: CyferShepard Date: Sun, 19 May 2024 00:09:03 +0200 Subject: [PATCH] Added localization to Activity Table Fixed Localization on Library Activity Watch TIme Stats Fixed Search placeholder not localized --- public/locales/en-US/translation.json | 20 ++++++++++- public/locales/fr-FR/translation.json | 22 ++++++++++-- src/pages/activity.jsx | 3 +- .../components/activity/activity-table.jsx | 30 ++++++++++------ .../components/item-info/item-activity.jsx | 3 +- .../library/globalstats/watchtimestats.jsx | 8 ++--- .../components/library/library-activity.jsx | 3 +- src/pages/components/library/library-card.jsx | 35 +++++++++++++------ src/pages/users.jsx | 2 +- 9 files changed, 94 insertions(+), 32 deletions(-) diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json index 532f48f..afc0c07 100644 --- a/public/locales/en-US/translation.json +++ b/public/locales/en-US/translation.json @@ -81,8 +81,22 @@ }, "IP_ADDRESS": "IP Address", "CLIENT": "Client", + "DEVICE": "Device", "PLAYBACK_DURATION": "Playback Duration", - "TOTAL_PLAYBACK": "Total Playback" + "TOTAL_PLAYBACK": "Total Playback", + "EXPAND": "Expand", + "COLLAPSE": "Collapse", + "SORT_BY": "Sort by", + "ASCENDING": "Ascending", + "DESCENDING": "Descending", + "CLEAR_SORT": "Clear Sort", + "CLEAR_FILTER": "Clear Filter", + "FILTER_BY": "Filter by", + "COLUMN_ACTIONS": "Column Actions", + "TOGGLE_SELECT_ROW": "Toggle Select Row", + "TOGGLE_SELECT_ALL": "Toggle Select All", + "MIN": "Min", + "MAX": "Max" }, "TABLE_NAV_BUTTONS": { "FIRST": "First", @@ -118,6 +132,8 @@ "SHOW_ARCHIVED_LIBRARIES": "Show Archived Libraries", "HIDE_ARCHIVED_LIBRARIES": "Hide Archived Libraries", "UNITS": { + "MONTH": "Month", + "MONTHS": "Months", "DAY": "Day", "DAYS": "Days", "HOUR": "Hour", @@ -204,6 +220,7 @@ "GITHUB": "Github", "Backup": "Backup Jellystat" }, + "SEARCH": "Search", "TOTAL": "Total", "LAST": "Last", "SERIES": "Series", @@ -212,6 +229,7 @@ "EPISODE": "Episode", "EPISODES": "Episodes", "MOVIES": "Movies", + "MUSIC": "Music", "SONGS": "Songs", "FILES": "Files", "LIBRARIES": "Libraries", diff --git a/public/locales/fr-FR/translation.json b/public/locales/fr-FR/translation.json index eb24432..437c31c 100644 --- a/public/locales/fr-FR/translation.json +++ b/public/locales/fr-FR/translation.json @@ -80,9 +80,23 @@ "HEADER": "Stream Info" }, "IP_ADDRESS": "Adresse IP", - "CLIENT": "Appareil", + "CLIENT": "Client", + "DEVICE": "Appareil", "PLAYBACK_DURATION": "Durée de la lecture", - "TOTAL_PLAYBACK": "Lecture totale" + "TOTAL_PLAYBACK": "Lecture totale", + "EXPAND": "Développer", + "COLLAPSE": "Réduire", + "SORT_BY": "Trier par", + "ASCENDING": "Croissant", + "DESCENDING": "Décroissant", + "CLEAR_SORT": "Annuler le tri", + "CLEAR_FILTER": "Annuler le filtre", + "FILTER_BY": "Filtrer par", + "COLUMN_ACTIONS": "Actions supplémentaires", + "TOGGLE_SELECT_ROW": "Inverser la sélection", + "TOGGLE_SELECT_ALL": "Inverser toutes les sélections", + "MIN": "Min", + "MAX": "Max" }, "TABLE_NAV_BUTTONS": { "FIRST": "Première", @@ -118,6 +132,8 @@ "SHOW_ARCHIVED_LIBRARIES": "Afficher les médiathèques archivées", "HIDE_ARCHIVED_LIBRARIES": "Cacher les médiathèques archivées", "UNITS": { + "MONTH": "Mois", + "MONTHS": "Mois", "DAY": "Jour", "DAYS": "Jours", "HOUR": "Heure", @@ -202,6 +218,7 @@ "GITHUB": "Github", "Backup": "Sauvegarde de Jellystat" }, + "SEARCH": "Recherche", "TOTAL": "Total", "LAST": "Last", "SERIES": "Séries", @@ -210,6 +227,7 @@ "EPISODE": "Épisode", "EPISODES": "Épisodes", "MOVIES": "Films", + "MUSIC": "Musiques", "SONGS": "Chansons", "FILES": "Fichiers", "LIBRARIES": "Médiathèques", diff --git a/src/pages/activity.jsx b/src/pages/activity.jsx index 8ddcb3d..40f4d97 100644 --- a/src/pages/activity.jsx +++ b/src/pages/activity.jsx @@ -9,6 +9,7 @@ import ActivityTable from "./components/activity/activity-table"; import Loading from "./components/general/loading"; import { Trans } from "react-i18next"; import { FormControl, FormSelect } from "react-bootstrap"; +import i18next from "i18next"; function Activity() { const [data, setData] = useState(); @@ -115,7 +116,7 @@ function Activity() { setSearchQuery(e.target.value)} className="ms-md-3 my-3 w-sm-100 w-md-75" diff --git a/src/pages/components/activity/activity-table.jsx b/src/pages/components/activity/activity-table.jsx index fa8c901..5e20065 100644 --- a/src/pages/components/activity/activity-table.jsx +++ b/src/pages/components/activity/activity-table.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from "react"; import axios from "axios"; import { enUS } from "@mui/material/locale"; -import { MRT_Localization_EN } from "material-react-table/locales/en"; + import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; @@ -135,7 +135,7 @@ export default function ActivityTable(props) { const columns = [ { accessorKey: "UserName", - header: "User", + header: i18next.t("USER"), filterVariant: "select", filterSelectOptions: uniqueUserNames, Cell: ({ row }) => { @@ -149,7 +149,7 @@ export default function ActivityTable(props) { }, { accessorKey: "RemoteEndPoint", - header: "IP Address", + header: i18next.t("ACTIVITY_TABLE.IP_ADDRESS"), Cell: ({ row }) => { row = row.original; @@ -175,7 +175,7 @@ export default function ActivityTable(props) { ? row.NowPlayingItemName : row.SeriesName + " : S" + row.SeasonNumber + "E" + row.EpisodeNumber + " - " + row.NowPlayingItemName }`, - header: "Title", + header: i18next.t("TITLE"), minSize: 300, Cell: ({ row }) => { row = row.original; @@ -190,7 +190,7 @@ export default function ActivityTable(props) { }, { accessorKey: "Client", - header: "Client", + header: i18next.t("ACTIVITY_TABLE.CLIENT"), filterVariant: "select", filterSelectOptions: uniqueClients, Cell: ({ row }) => { @@ -204,11 +204,11 @@ export default function ActivityTable(props) { }, { accessorKey: "DeviceName", - header: "Device", + header: i18next.t("ACTIVITY_TABLE.DEVICE"), }, { accessorFn: (row) => new Date(row.ActivityDateInserted), - header: "Date", + header: i18next.t("DATE"), size: 110, filterVariant: "date-range", Cell: ({ row }) => { @@ -227,7 +227,7 @@ export default function ActivityTable(props) { }, { accessorKey: "PlaybackDuration", - header: "Total Playback", + header: i18next.t("ACTIVITY_TABLE.TOTAL_PLAYBACK"), minSize: 200, filterFn: (row, id, filterValue) => formatTotalWatchTime(row.getValue(id)).startsWith(filterValue), @@ -235,7 +235,7 @@ export default function ActivityTable(props) { }, { accessorFn: (row) => Number(row.TotalPlays ?? 1), - header: "Total Plays", + header: i18next.t("TOTAL_PLAYS"), filterFn: "betweenInclusive", Cell: ({ cell }) => {cell.getValue() ?? 1}, @@ -249,6 +249,17 @@ export default function ActivityTable(props) { const table = useMaterialReactTable({ columns, data, + localization: { + expand: i18next.t("ACTIVITY_TABLE.EXPAND"), + collapse: i18next.t("ACTIVITY_TABLE.COLLAPSE"), + sortByColumnAsc: `${i18next.t("ACTIVITY_TABLE.SORT_BY")} {column} - ${i18next.t("ACTIVITY_TABLE.ASCENDING")}`, + sortByColumnDesc: `${i18next.t("ACTIVITY_TABLE.SORT_BY")} {column} - ${i18next.t("ACTIVITY_TABLE.DESCENDING")}`, + clearFilter: i18next.t("ACTIVITY_TABLE.CLEAR_FILTER"), + clearSort: i18next.t("ACTIVITY_TABLE.CLEAR_SORT"), + filterByColumn: `${i18next.t("ACTIVITY_TABLE.FILTER_BY")} {column}`, + toggleSelectAll: i18next.t("ACTIVITY_TABLE.TOGGLE_SELECT_ALL"), + toggleSelectRow: i18next.t("ACTIVITY_TABLE.TOGGLE_SELECT_ROW"), + }, columnFilterDisplayMode: "popover", layoutMode: "grid", enableExpandAll: false, @@ -266,7 +277,6 @@ export default function ActivityTable(props) { }, ], }, - localization: { MRT_Localization_EN }, showAlertBanner: false, enableHiding: false, enableFullScreenToggle: false, diff --git a/src/pages/components/item-info/item-activity.jsx b/src/pages/components/item-info/item-activity.jsx index ebebc17..623799f 100644 --- a/src/pages/components/item-info/item-activity.jsx +++ b/src/pages/components/item-info/item-activity.jsx @@ -3,6 +3,7 @@ import axios from "axios"; import ActivityTable from "../activity/activity-table"; import { Trans } from "react-i18next"; import { FormControl, FormSelect } from "react-bootstrap"; +import i18next from "i18next"; function ItemActivity(props) { const [data, setData] = useState(); @@ -81,7 +82,7 @@ function ItemActivity(props) { setSearchQuery(e.target.value)} className="ms-md-3 my-3 w-sm-100 w-md-75" diff --git a/src/pages/components/library/globalstats/watchtimestats.jsx b/src/pages/components/library/globalstats/watchtimestats.jsx index b484505..075e509 100644 --- a/src/pages/components/library/globalstats/watchtimestats.jsx +++ b/src/pages/components/library/globalstats/watchtimestats.jsx @@ -6,9 +6,9 @@ function WatchTimeStats(props) { function formatTime(totalSeconds, numberClassName, labelClassName) { const units = [ - { label: i18next.t("UNITS.DAY"), seconds: 86400 }, - { label: i18next.t("UNITS.HOUR"), seconds: 3600 }, - { label: i18next.t("UNITS.MINUTE"), seconds: 60 }, + { label: [i18next.t("UNITS.DAY"),i18next.t("UNITS.DAYS")], seconds: 86400 }, + { label: [i18next.t("UNITS.HOUR"),i18next.t("UNITS.HOURS")], seconds: 3600 }, + { label: [i18next.t("UNITS.MINUTE"),i18next.t("UNITS.MINUTES")], seconds: 60 }, ]; const parts = units.reduce((result, { label, seconds }) => { @@ -17,7 +17,7 @@ function WatchTimeStats(props) { const formattedValue =

{value}

; const formattedLabel = ( - {value === 1 ? label : i18next.t(`UNITS.${label.toUpperCase()}S`) } + {value === 1 ? label[0] : label[1] } ); result.push( diff --git a/src/pages/components/library/library-activity.jsx b/src/pages/components/library/library-activity.jsx index 8555390..39e3ebe 100644 --- a/src/pages/components/library/library-activity.jsx +++ b/src/pages/components/library/library-activity.jsx @@ -4,6 +4,7 @@ import axios from "axios"; import ActivityTable from "../activity/activity-table"; import { Trans } from "react-i18next"; import { FormControl, FormSelect } from "react-bootstrap"; +import i18next from "i18next"; function LibraryActivity(props) { const [data, setData] = useState(); @@ -81,7 +82,7 @@ function LibraryActivity(props) { setSearchQuery(e.target.value)} className="ms-md-3 my-3 w-sm-100 w-md-75" diff --git a/src/pages/components/library/library-card.jsx b/src/pages/components/library/library-card.jsx index 37cf775..918acb5 100644 --- a/src/pages/components/library/library-card.jsx +++ b/src/pages/components/library/library-card.jsx @@ -53,22 +53,28 @@ function LibraryCard(props) { const days = Math.floor(seconds / 86400); // 1 day = 86400 seconds const hours = Math.floor((seconds % 86400) / 3600); // 1 hour = 3600 seconds const minutes = Math.floor(((seconds % 86400) % 3600) / 60); // 1 minute = 60 seconds + + const units = { + months: [i18next.t("UNITS.MONTH"), i18next.t("UNITS.MONTHS")], + days: [i18next.t("UNITS.DAY"), i18next.t("UNITS.DAYS")], + hours: [i18next.t("UNITS.HOUR"), i18next.t("UNITS.HOURS")], + minutes: [i18next.t("UNITS.MINUTE"), i18next.t("UNITS.MINUTES")] + }; let formattedTime = ''; if (days) { - formattedTime += `${days} day${days > 1 ? 's' : ''}`; + formattedTime += `${days} ${days > 1 ? units.days[1] : units.days[0]}`; } if (hours) { - formattedTime += ` ${hours} hour${hours > 1 ? 's' : ''}`; + formattedTime += ` ${hours} ${hours > 1 ? units.hours[1] : units.hours[0]}`; } - if (minutes) { - formattedTime += ` ${minutes} minute${minutes > 1 ? 's' : ''}`; + if (minutes) { formattedTime += ` ${minutes} ${minutes > 1 ? units.minutes[1] : units.minutes[0]}`; } if (!days && !hours && !minutes) { - formattedTime = '0 minutes'; + formattedTime =`0 ${units.minutes[1]}`; } return formattedTime; @@ -82,24 +88,31 @@ function LibraryCard(props) { const minutes = Math.floor((seconds % 3600) / 60); // 1 minute = 60 seconds const timeComponents = []; + + const units = { + months: [i18next.t("UNITS.MONTH"), i18next.t("UNITS.MONTHS")], + days: [i18next.t("UNITS.DAY"), i18next.t("UNITS.DAYS")], + hours: [i18next.t("UNITS.HOUR"), i18next.t("UNITS.HOURS")], + minutes: [i18next.t("UNITS.MINUTE"), i18next.t("UNITS.MINUTES")] + }; if (months) { - timeComponents.push(`${months} Month${months > 1 ? 's' : ''}`); + timeComponents.push(`${months} ${months > 1 ? units.months[1] : units.months[0] }`); } if (days) { - timeComponents.push(`${days} day${days > 1 ? 's' : ''}`); + timeComponents.push(`${days} ${days > 1 ? units.days[1] : units.days[0]}`); } if (hours) { - timeComponents.push(`${hours} hour${hours > 1 ? 's' : ''}`); + timeComponents.push(`${hours} ${hours > 1 ? units.hours[1] : units.hours[0]}`); } if (!months && minutes) { - timeComponents.push(`${minutes} minute${minutes > 1 ? 's' : ''}`); + timeComponents.push(`${minutes} ${minutes > 1 ? units.minutes[1] : units.minutes[0]}`); } - const formattedTime = timeComponents.length > 0 ? timeComponents.join(' ') : '0 minutes'; + const formattedTime = timeComponents.length > 0 ? timeComponents.join(' ') : `0 ${units.minutes[1]}`; return formattedTime; } @@ -151,7 +164,7 @@ function LibraryCard(props) { - {props.data.CollectionType==='tvshows' ? 'Series' : props.data.CollectionType==='movies'? "Movies" : props.data.CollectionType==='music'? "Music" : 'Mixed'} + {props.data.CollectionType==='tvshows' ? : props.data.CollectionType==='movies'? : props.data.CollectionType==='music'? : 'Mixed'} diff --git a/src/pages/users.jsx b/src/pages/users.jsx index 796db9f..6a9d272 100644 --- a/src/pages/users.jsx +++ b/src/pages/users.jsx @@ -431,7 +431,7 @@ function Users() { setSearchQuery(e.target.value)} className="ms-md-3 my-3 w-sm-100 w-md-75"