diff --git a/resources/i18n/pt-br.json b/resources/i18n/pt-br.json index 238919259..1a0c1ab8a 100644 --- a/resources/i18n/pt-br.json +++ b/resources/i18n/pt-br.json @@ -660,6 +660,11 @@ "options": { "theme": "Tema", "language": "Língua", + "dateFormat": "Formato de data", + "dateFormats": { + "language": "Igual à língua", + "browser": "Igual ao navegador" + }, "defaultView": "Tela inicial", "desktop_notifications": "Notificações", "lastfmScrobbling": "Enviar scrobbles para Last.fm", diff --git a/ui/src/album/AlbumInfo.jsx b/ui/src/album/AlbumInfo.jsx index 075841e43..1de904031 100644 --- a/ui/src/album/AlbumInfo.jsx +++ b/ui/src/album/AlbumInfo.jsx @@ -8,7 +8,6 @@ import { ArrayField, BooleanField, ChipField, - DateField, FunctionField, SingleFieldList, TextField, @@ -17,6 +16,7 @@ import { } from 'react-admin' import { makeStyles } from '@material-ui/core/styles' import { + DateField, ArtistLinkField, MultiLineTextField, ParticipantsInfo, diff --git a/ui/src/album/AlbumTableView.jsx b/ui/src/album/AlbumTableView.jsx index 051d5988e..46a86c6bc 100644 --- a/ui/src/album/AlbumTableView.jsx +++ b/ui/src/album/AlbumTableView.jsx @@ -3,7 +3,6 @@ import { Datagrid, DatagridBody, DatagridRow, - DateField, NumberField, TextField, FunctionField, @@ -13,6 +12,7 @@ import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import { makeStyles } from '@material-ui/core/styles' import { useDrag } from 'react-dnd' import { + DateField, ArtistLinkField, ArtworkAvatar, DurationField, diff --git a/ui/src/common/DateField.jsx b/ui/src/common/DateField.jsx index dac9cff08..8aab34ae1 100644 --- a/ui/src/common/DateField.jsx +++ b/ui/src/common/DateField.jsx @@ -1,12 +1,12 @@ import React from 'react' import { isDateSet } from '../utils/validations' -import { DateField as RADateField } from 'react-admin' +import { DateField as RADateField, useRecordContext } from 'react-admin' import { useDateLocale } from '../i18n/useDateLocale' export const DateField = (props) => { - const { record, source } = props + const record = useRecordContext(props) const locale = useDateLocale() - const value = record?.[source] + const value = record?.[props.source] if (!isDateSet(value)) return null return } diff --git a/ui/src/common/DateField.test.jsx b/ui/src/common/DateField.test.jsx index 47d56756b..7494b7887 100644 --- a/ui/src/common/DateField.test.jsx +++ b/ui/src/common/DateField.test.jsx @@ -1,6 +1,7 @@ import React from 'react' import { render, screen } from '@testing-library/react' import { describe, it, expect, beforeEach, vi } from 'vitest' +import { RecordContextProvider } from 'react-admin' import { DateField } from './DateField' vi.mock('react-admin', async (importOriginal) => ({ @@ -29,4 +30,13 @@ describe('', () => { ) expect(container).toBeEmptyDOMElement() }) + + it('reads the record from the record context', () => { + render( + + + , + ) + expect(screen.getByText('17.9.2026')).toBeInTheDocument() + }) }) diff --git a/ui/src/common/SongInfo.jsx b/ui/src/common/SongInfo.jsx index 1b1a014f1..b9f7bea8c 100644 --- a/ui/src/common/SongInfo.jsx +++ b/ui/src/common/SongInfo.jsx @@ -6,7 +6,6 @@ import TableContainer from '@material-ui/core/TableContainer' import TableRow from '@material-ui/core/TableRow' import { BooleanField, - DateField, TextField, NumberField, FunctionField, @@ -15,6 +14,7 @@ import { } from 'react-admin' import { humanize, underscore } from 'inflection' import { + DateField, ArtistLinkField, BitrateField, ParticipantsInfo, diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json index f694ea75f..8f086f516 100644 --- a/ui/src/i18n/en.json +++ b/ui/src/i18n/en.json @@ -677,6 +677,11 @@ "options": { "theme": "Theme", "language": "Language", + "dateFormat": "Date Format", + "dateFormats": { + "language": "Match language", + "browser": "Match browser" + }, "defaultView": "Default View", "desktop_notifications": "Desktop Notifications", "lastfmNotConfigured": "Last.fm API-Key is not configured", diff --git a/ui/src/i18n/useDateLocale.js b/ui/src/i18n/useDateLocale.js index c9298a473..f02402f29 100644 --- a/ui/src/i18n/useDateLocale.js +++ b/ui/src/i18n/useDateLocale.js @@ -1,5 +1,8 @@ import { useLocale } from 'react-admin' +export const getStoredDateFormat = () => + localStorage.getItem('dateFormat') || 'language' + // Our language codes are mostly region-less ("en"), and Intl reads a bare "en" // as en-US. Borrow the region from the browser when it speaks the same language. const resolveDateLocale = (locale, browserLocales = []) => { @@ -10,5 +13,10 @@ const resolveDateLocale = (locale, browserLocales = []) => { ) } -export const useDateLocale = () => - resolveDateLocale(useLocale(), navigator.languages) +// With the "browser" date format, return undefined so Intl uses the browser's +// own locale, regardless of the selected language. +export const useDateLocale = () => { + const locale = useLocale() + if (getStoredDateFormat() === 'browser') return undefined + return resolveDateLocale(locale, navigator.languages) +} diff --git a/ui/src/i18n/useDateLocale.test.js b/ui/src/i18n/useDateLocale.test.js index c0ea5ab40..16707a084 100644 --- a/ui/src/i18n/useDateLocale.test.js +++ b/ui/src/i18n/useDateLocale.test.js @@ -9,6 +9,7 @@ vi.mock('react-admin', () => ({ describe('useDateLocale', () => { beforeEach(() => { vi.clearAllMocks() + localStorage.removeItem('dateFormat') }) const renderWith = async (locale, browserLocales) => { @@ -42,4 +43,10 @@ describe('useDateLocale', () => { const result = await renderWith(undefined, ['en-GB']) expect(result.current).toBeUndefined() }) + + it('returns undefined to use the browser locale when set to browser', async () => { + localStorage.setItem('dateFormat', 'browser') + const result = await renderWith('en', ['fr-FR']) + expect(result.current).toBeUndefined() + }) }) diff --git a/ui/src/missing/MissingFilesList.jsx b/ui/src/missing/MissingFilesList.jsx index a09f552d9..64ab0487d 100644 --- a/ui/src/missing/MissingFilesList.jsx +++ b/ui/src/missing/MissingFilesList.jsx @@ -1,4 +1,5 @@ import { + DateField, List, Pagination, SizeField, @@ -7,7 +8,6 @@ import { } from '../common/index' import { Datagrid, - DateField, TextField, downloadCSV, Filter, diff --git a/ui/src/personal/Personal.jsx b/ui/src/personal/Personal.jsx index 84f9b63e6..75c4dd45a 100644 --- a/ui/src/personal/Personal.jsx +++ b/ui/src/personal/Personal.jsx @@ -2,6 +2,7 @@ import { SimpleForm, Title, useTranslate } from 'react-admin' import { Card } from '@material-ui/core' import { makeStyles } from '@material-ui/core/styles' import { SelectLanguage } from './SelectLanguage' +import { SelectDateFormat } from './SelectDateFormat' import { SelectTheme } from './SelectTheme' import { SelectDefaultView } from './SelectDefaultView' import { NotificationsToggle } from './NotificationsToggle' @@ -24,6 +25,7 @@ const Personal = () => { + {config.enableReplayGain && } diff --git a/ui/src/personal/SelectDateFormat.jsx b/ui/src/personal/SelectDateFormat.jsx new file mode 100644 index 000000000..fdc9583d2 --- /dev/null +++ b/ui/src/personal/SelectDateFormat.jsx @@ -0,0 +1,22 @@ +import { SelectInput, useTranslate } from 'react-admin' +import { getStoredDateFormat } from '../i18n/useDateLocale' + +export const SelectDateFormat = (props) => { + const translate = useTranslate() + + return ( + { + localStorage.setItem('dateFormat', event.target.value) + }} + /> + ) +} diff --git a/ui/src/playlist/PlaylistList.jsx b/ui/src/playlist/PlaylistList.jsx index e1695e980..be77672a9 100644 --- a/ui/src/playlist/PlaylistList.jsx +++ b/ui/src/playlist/PlaylistList.jsx @@ -1,7 +1,6 @@ import React, { useMemo } from 'react' import { Datagrid, - DateField, EditButton, Filter, NullableBooleanInput, @@ -20,6 +19,7 @@ import Switch from '@material-ui/core/Switch' import { makeStyles } from '@material-ui/core/styles' import { useMediaQuery } from '@material-ui/core' import { + DateField, ArtworkAvatar, DurationField, List, diff --git a/ui/src/radio/RadioList.jsx b/ui/src/radio/RadioList.jsx index 0cfb2cd68..46e59ffcc 100644 --- a/ui/src/radio/RadioList.jsx +++ b/ui/src/radio/RadioList.jsx @@ -3,7 +3,6 @@ import React, { cloneElement } from 'react' import { CreateButton, Datagrid, - DateField, EditButton, Filter, sanitizeListRestProps, @@ -15,6 +14,7 @@ import { useTranslate, } from 'react-admin' import { + DateField, List, defaultRowsPerPageOptions, getStoredPerPage,