Added localization to Activity Table

Fixed Localization on Library Activity Watch TIme Stats
Fixed Search placeholder not localized
This commit is contained in:
CyferShepard
2024-05-19 00:09:03 +02:00
parent 7c885fa5aa
commit 42c94fcd90
9 changed files with 94 additions and 32 deletions

View File

@@ -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",

View File

@@ -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",

View File

@@ -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() {
</div>
<FormControl
type="text"
placeholder="Search"
placeholder= {i18next.t("SEARCH")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="ms-md-3 my-3 w-sm-100 w-md-75"

View File

@@ -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 }) => <span>{cell.getValue() ?? 1}</span>,
@@ -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,

View File

@@ -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) {
</div>
<FormControl
type="text"
placeholder="Search"
placeholder= {i18next.t("SEARCH")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="ms-md-3 my-3 w-sm-100 w-md-75"

View File

@@ -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 = <p className={numberClassName}>{value}</p>;
const formattedLabel = (
<span className={labelClassName}>
{value === 1 ? label : i18next.t(`UNITS.${label.toUpperCase()}S`) }
{value === 1 ? label[0] : label[1] }
</span>
);
result.push(

View File

@@ -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) {
</div>
<FormControl
type="text"
placeholder="Search"
placeholder= {i18next.t("SEARCH")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="ms-md-3 my-3 w-sm-100 w-md-75"

View File

@@ -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) {
<Row className="space-between-end card-row">
<Col className="card-label"><Trans i18nKey="TYPE" /></Col>
<Col className="text-end">{props.data.CollectionType==='tvshows' ? 'Series' : props.data.CollectionType==='movies'? "Movies" : props.data.CollectionType==='music'? "Music" : 'Mixed'}</Col>
<Col className="text-end">{props.data.CollectionType==='tvshows' ? <Trans i18nKey="SERIES" /> : props.data.CollectionType==='movies'? <Trans i18nKey="MOVIES" /> : props.data.CollectionType==='music'? <Trans i18nKey="MUSIC" /> : 'Mixed'}</Col>
</Row>
<Row className="space-between-end card-row">

View File

@@ -431,7 +431,7 @@ function Users() {
</div>
<FormControl
type="text"
placeholder="Search"
placeholder= {i18next.t("SEARCH")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="ms-md-3 my-3 w-sm-100 w-md-75"