From 91984f405c9bb52cbf766dc4018fedc41aeae465 Mon Sep 17 00:00:00 2001 From: Carsten Milling Date: Sun, 5 Jul 2026 20:22:37 +0200 Subject: [PATCH 1/2] Display translators alongside authors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consume the new `editors` field on plays (dracor-api#368) and surface entries with role "translator" next to authors. - Play detail header: render a translator AuthorInfo card with a vertical "Translator" label overlaying the image's left edge; text side is muted. Sticky heading includes the translator name (with a "tr." prefix, italic) in the collapsed state only. - Corpus play list: append a muted "trans. …" line under the author names in the Authors column, and fold translator names/refs/aliases into the column's search filter. Closes #391 --- src/components/AuthorInfo.jsx | 7 ++-- src/components/AuthorInfo.module.scss | 26 +++++++++++++++ src/components/Corpus.jsx | 4 +++ src/components/CorpusIndex.jsx | 34 +++++++++++++------- src/components/CorpusIndex.scss | 5 +++ src/components/PlayDetailsHeader.jsx | 18 +++++++++-- src/components/PlayDetailsHeader.module.scss | 5 +++ src/types.ts | 2 ++ 8 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/components/AuthorInfo.jsx b/src/components/AuthorInfo.jsx index 7d50be5a..44910476 100644 --- a/src/components/AuthorInfo.jsx +++ b/src/components/AuthorInfo.jsx @@ -7,7 +7,7 @@ import style from './AuthorInfo.module.scss'; const cx = classnames.bind(style); -const AuthorInfo = ({author: {fullname, refs = []}}) => { +const AuthorInfo = ({author: {fullname, refs = [], role}}) => { const [info, setInfo] = useState(null); const wikidataRef = refs.find((r) => r.type === 'wikidata'); @@ -64,8 +64,10 @@ const AuthorInfo = ({author: {fullname, refs = []}}) => { const {name, imageUrl, commonsPage, birth = [], death = []} = info || {}; + const isTranslator = role === 'translator'; + return ( -
+
{imageUrl && } {commonsPage && ( @@ -77,6 +79,7 @@ const AuthorInfo = ({author: {fullname, refs = []}}) => { /> )} + {isTranslator && Translator}

{fullname}

diff --git a/src/components/AuthorInfo.module.scss b/src/components/AuthorInfo.module.scss index dc8c0004..b77e1d20 100644 --- a/src/components/AuthorInfo.module.scss +++ b/src/components/AuthorInfo.module.scss @@ -16,6 +16,32 @@ } } +.is-translator > span { + opacity: 0.8; +} + +.role-badge { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 1.3em; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.65); + color: var(--background-light); + writing-mode: vertical-rl; + transform: rotate(180deg); + font-size: 0.63em; + font-weight: 500; + letter-spacing: 0.15em; + text-transform: uppercase; + opacity: 0.7; + padding: 1px; +} + .image { position: relative; height: 6em; diff --git a/src/components/Corpus.jsx b/src/components/Corpus.jsx index 7e173fad..97e699ab 100644 --- a/src/components/Corpus.jsx +++ b/src/components/Corpus.jsx @@ -32,6 +32,10 @@ const Corpus = () => { d.authors = []; d.authorNames = 'Anonymous'; } + d.translators = (d.editors || []).filter( + (e) => e.role === 'translator' + ); + d.translatorNames = d.translators.map((t) => t.name).join(' · '); }); setCorpus(response.data); setLoading(false); diff --git a/src/components/CorpusIndex.jsx b/src/components/CorpusIndex.jsx index ab364c13..c92d0646 100644 --- a/src/components/CorpusIndex.jsx +++ b/src/components/CorpusIndex.jsx @@ -42,6 +42,12 @@ function formatAuthor(authorNames, d) {
))} + {d.translators && d.translators.length > 0 && ( + <> +
+ trans. {d.translatorNames} + + )} ); } @@ -135,17 +141,23 @@ const CorpusIndex = ({data}) => { dataField: 'authorNames', text: 'Authors', sort: true, - filterValue: (cell, row) => - `${cell} ${row.authors - .map((a) => { - const refs = a.refs ? a.refs.map((r) => r.ref).join(' ') : ''; - let value = refs; - if (a.alsoKnownAs) { - value += a.alsoKnownAs.join(' '); - } - return value; - }) - .join(' ')} `, + filterValue: (cell, row) => { + const buildTokens = (people) => + people + .map((a) => { + const refs = a.refs ? a.refs.map((r) => r.ref).join(' ') : ''; + let value = refs; + if (a.alsoKnownAs) { + value += a.alsoKnownAs.join(' '); + } + return value; + }) + .join(' '); + return ( + `${cell} ${buildTokens(row.authors)} ` + + `${row.translatorNames || ''} ${buildTokens(row.translators || [])} ` + ); + }, formatter: formatAuthor, }, { diff --git a/src/components/CorpusIndex.scss b/src/components/CorpusIndex.scss index fdf588ee..ed76b4c7 100644 --- a/src/components/CorpusIndex.scss +++ b/src/components/CorpusIndex.scss @@ -2,6 +2,11 @@ table.table.corpus { table-layout: auto; } +.translators { + color: var(--gray, #6c757d); + font-style: italic; +} + .year-details { font-size: 60%; white-space: nowrap; diff --git a/src/components/PlayDetailsHeader.jsx b/src/components/PlayDetailsHeader.jsx index 01e5985b..9337d506 100644 --- a/src/components/PlayDetailsHeader.jsx +++ b/src/components/PlayDetailsHeader.jsx @@ -16,6 +16,7 @@ const PlayDetailsHeader = ({play, children}) => { const { id, authors, + editors = [], corpus, title, subtitle, @@ -25,6 +26,8 @@ const PlayDetailsHeader = ({play, children}) => { yearWritten, } = play; + const translators = editors.filter((e) => e.role === 'translator'); + const {corpora} = useContext(DracorContext); const {acronym} = corpora.find((c) => c.name === corpus) || {}; @@ -67,7 +70,10 @@ const PlayDetailsHeader = ({play, children}) => {
{authors.map((a) => ( - + + ))} + {translators.map((t) => ( + ))}
@@ -79,7 +85,15 @@ const PlayDetailsHeader = ({play, children}) => {

{title}

{authors.map((a) => ( -

{a.fullname}

+

{a.fullname}

+ ))} + {translators.map((t) => ( +

+ tr. {t.fullname} +

))}
diff --git a/src/components/PlayDetailsHeader.module.scss b/src/components/PlayDetailsHeader.module.scss index 54647492..f052179e 100644 --- a/src/components/PlayDetailsHeader.module.scss +++ b/src/components/PlayDetailsHeader.module.scss @@ -170,6 +170,11 @@ content: ''; margin: 0; } + + &.translator { + font-style: italic; + opacity: 0.85; + } } } diff --git a/src/types.ts b/src/types.ts index 59aa16e6..0d1309d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,7 @@ export interface Author { fullname?: string; shortname?: string; refs?: Ref[]; + role?: string; // DEPRECATED key?: string; } @@ -54,6 +55,7 @@ export interface Play { title: string; subtitle?: string; authors: Author[]; + editors?: Author[]; genre: string; libretto: boolean; originalSource: string; From 96f7a294177e9a0985a91baa2797133395ed75d8 Mon Sep 17 00:00:00 2001 From: Carsten Milling Date: Mon, 6 Jul 2026 14:44:40 +0200 Subject: [PATCH 2/2] Change translator abbreviations to "transl." https://github.com/dracor-org/dracor-frontend/pull/408#issuecomment-4889780295 --- src/components/CorpusIndex.jsx | 2 +- src/components/PlayDetailsHeader.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/CorpusIndex.jsx b/src/components/CorpusIndex.jsx index c92d0646..a0795f80 100644 --- a/src/components/CorpusIndex.jsx +++ b/src/components/CorpusIndex.jsx @@ -45,7 +45,7 @@ function formatAuthor(authorNames, d) { {d.translators && d.translators.length > 0 && ( <>
- trans. {d.translatorNames} + transl. {d.translatorNames} )} diff --git a/src/components/PlayDetailsHeader.jsx b/src/components/PlayDetailsHeader.jsx index 9337d506..67c3d721 100644 --- a/src/components/PlayDetailsHeader.jsx +++ b/src/components/PlayDetailsHeader.jsx @@ -92,7 +92,7 @@ const PlayDetailsHeader = ({play, children}) => { key={`translator-${t.fullname}`} className={cx('translator')} > - tr. {t.fullname} + transl. {t.fullname} ))}