diff options
author | (quasar) nebula <qznebula@protonmail.com> | 2023-05-25 22:08:35 -0300 |
---|---|---|
committer | (quasar) nebula <qznebula@protonmail.com> | 2023-05-25 22:08:35 -0300 |
commit | 22ca57c7fd366ff6ca055ec5c28f527e57509bb8 (patch) | |
tree | 9f976a06b14b59902505c6c68eaf6db2d7fbc8ae /src | |
parent | 6d8fe82b5386af536ca96eb1d89150e201c603e9 (diff) |
content: multiline content & fill out album/track pages more
Diffstat (limited to 'src')
-rw-r--r-- | src/content/dependencies/generateAlbumAdditionalFilesList.js | 10 | ||||
-rw-r--r-- | src/content/dependencies/generateAlbumInfoPageContent.js | 186 | ||||
-rw-r--r-- | src/content/dependencies/generateAlbumTrackList.js | 2 | ||||
-rw-r--r-- | src/content/dependencies/generateTrackInfoPageContent.js | 190 | ||||
-rw-r--r-- | src/content/dependencies/transformContent.js | 67 | ||||
-rw-r--r-- | src/data/things/album.js | 17 | ||||
-rw-r--r-- | src/data/things/thing.js | 17 | ||||
-rw-r--r-- | src/data/yaml.js | 1 | ||||
-rw-r--r-- | src/strings-default.json | 11 |
9 files changed, 364 insertions, 137 deletions
diff --git a/src/content/dependencies/generateAlbumAdditionalFilesList.js b/src/content/dependencies/generateAlbumAdditionalFilesList.js index f8fd5499..5fd4e05b 100644 --- a/src/content/dependencies/generateAlbumAdditionalFilesList.js +++ b/src/content/dependencies/generateAlbumAdditionalFilesList.js @@ -9,24 +9,24 @@ export default { 'urls', ], - data(album, {fileSize = true} = {}) { + data(album, additionalFiles, {fileSize = true} = {}) { return { albumDirectory: album.directory, - fileLocations: album.additionalFiles.flatMap(({files}) => files), + fileLocations: additionalFiles.flatMap(({files}) => files), showFileSizes: fileSize, }; }, - relations(relation, album, {fileSize = true} = {}) { + relations(relation, album, additionalFiles, {fileSize = true} = {}) { return { additionalFilesList: - relation('generateAdditionalFilesList', album.additionalFiles, { + relation('generateAdditionalFilesList', additionalFiles, { fileSize, }), additionalFileLinks: Object.fromEntries( - album.additionalFiles + additionalFiles .flatMap(({files}) => files) .map(file => [ file, diff --git a/src/content/dependencies/generateAlbumInfoPageContent.js b/src/content/dependencies/generateAlbumInfoPageContent.js index 76862f9c..7b8522b7 100644 --- a/src/content/dependencies/generateAlbumInfoPageContent.js +++ b/src/content/dependencies/generateAlbumInfoPageContent.js @@ -13,65 +13,104 @@ export default { 'linkExternal', ], - extraDependencies: [ - 'html', - 'language', - 'transformMultiline', - ], + extraDependencies: ['html', 'language'], relations(relation, album) { const relations = {}; + const sections = relations.sections = {}; const contributionLinksRelation = contribs => contribs.map(contrib => relation('linkContribution', contrib.who, contrib.what)); + // Section: Release info + + const releaseInfo = sections.releaseInfo = {}; + + if (!empty(album.artistContribs)) { + releaseInfo.artistContributionLinks = + contributionLinksRelation(album.artistContribs); + } + if (album.hasCoverArt) { relations.cover = relation('generateCoverArtwork', album.artTags); + releaseInfo.coverArtistContributionLinks = + contributionLinksRelation(album.coverArtistContribs); + } else { + relations.cover = null; } - relations.artistLinks = - contributionLinksRelation(album.artistContribs); + if (album.hasWallpaperArt) { + releaseInfo.wallpaperArtistContributionLinks = + contributionLinksRelation(album.wallpaperArtistContribs); + } + + if (album.hasBannerArt) { + releaseInfo.bannerArtistContributionLinks = + contributionLinksRelation(album.bannerArtistContribs); + } + + // Section: Listen on + + if (!empty(album.urls)) { + const listen = sections.listen = {}; - relations.coverArtistLinks = - contributionLinksRelation(album.coverArtistContribs); + listen.heading = + relation('generateContentHeading'); + + listen.externalLinks = + album.urls.map(url => + relation('linkExternal', url, {type: 'album'})); + } - relations.wallpaperArtistLinks = - contributionLinksRelation(album.wallpaperArtistContribs); + // Section: Extra links - relations.bannerArtistLinks = - contributionLinksRelation(album.bannerArtistContribs); + const extra = sections.extra = {}; if (album.tracks.some(t => t.hasUniqueCoverArt)) { - relations.galleryLink = + extra.galleryLink = relation('linkAlbumGallery', album); } if (album.commentary || album.tracks.some(t => t.commentary)) { - relations.commentaryLink = + extra.commentaryLink = relation('linkAlbumCommentary', album); } - relations.externalLinks = - album.urls.map(url => - relation('linkExternal', url, {type: 'album'})); + if (!empty(album.additionalFiles)) { + extra.additionalFilesShortcut = + relation('generateAdditionalFilesShortcut', album.additionalFiles); + } + + // Section: Track list + + relations.trackList = + relation('generateAlbumTrackList', album); - relations.trackList = relation('generateAlbumTrackList', album); + // Section: Additional files if (!empty(album.additionalFiles)) { - relations.additionalFilesShortcut = - relation('generateAdditionalFilesShortcut', album.additionalFiles); + const additionalFiles = sections.additionalFiles = {}; - relations.additionalFilesHeading = + additionalFiles.heading = relation('generateContentHeading'); - relations.additionalFilesList = - relation('generateAlbumAdditionalFilesList', album); + additionalFiles.additionalFilesList = + relation('generateAlbumAdditionalFilesList', album, album.additionalFiles); } - relations.artistCommentaryHeading = - relation('generateContentHeading'); + // Section: Artist commentary + + if (album.commentary) { + const artistCommentary = sections.artistCommentary = {}; + + artistCommentary.heading = + relation('generateContentHeading'); + + artistCommentary.content = + relation('transformContent', album.commentary); + } return relations; }, @@ -99,7 +138,6 @@ export default { } data.dateAddedToWiki = album.dateAddedToWiki; - data.artistCommentary = album.commentary; return data; }, @@ -107,18 +145,20 @@ export default { generate(data, relations, { html, language, - transformMultiline, }) { const content = {}; - const formatContributions = contributionLinks => - language.formatConjunctionList( - contributionLinks.map(link => - link - .slots({ - showContribution: true, - showIcons: true, - }))); + const {sections: sec} = relations; + + const formatContributions = + (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) => + contributionLinks && + language.$(stringKey, { + artists: + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))), + }); if (data.hasCoverArt) { content.cover = relations.cover @@ -126,6 +166,8 @@ export default { path: ['media.albumCover', data.coverArtDirectory, data.coverArtFileExtension], alt: language.$('misc.alt.trackCover') }); + } else { + content.cover = null; } content.main = { @@ -137,25 +179,10 @@ export default { [html.joinChildren]: html.tag('br'), }, [ - !empty(relations.artistLinks) && - language.$('releaseInfo.by', { - artists: formatContributions(relations.artistLinks), - }), - - !empty(relations.coverArtistLinks) && - language.$('releaseInfo.coverArtBy', { - artists: formatContributions(relations.coverArtistLinks), - }), - - !empty(relations.wallpaperArtistLinks) && - language.$('releaseInfo.wallpaperArtBy', { - artists: formatContributions(relations.wallpaperArtistLinks), - }), - - !empty(relations.bannerArtistLinks) && - language.$('releaseInfo.bannerArtBy', { - artists: formatContributions(relations.bannerArtistLinks), - }), + formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks), + formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks), + formatContributions('releaseInfo.wallpaperArtBy', sec.releaseInfo.wallpaperArtistContributionLinks), + formatContributions('releaseInfo.bannerArtBy', sec.releaseInfo.bannerArtistContributionLinks), data.date && language.$('releaseInfo.released', { @@ -176,35 +203,48 @@ export default { }), ]), + sec.listen && + sec.listen.heading.slots({ + id: 'listen-on', + title: + language.$('releaseInfo.listenOn', { + links: language.formatDisjunctionList(sec.listen.externalLinks), + }), + }), + html.tag('p', { [html.onlyIfContent]: true, [html.joinChildren]: html.tag('br'), }, [ - relations.additionalFilesShortcut, + sec.extra.additionalFilesShortcut, + + sec.extra.galleryLink && sec.extra.commentaryLink && + language.$('releaseInfo.viewGalleryOrCommentary', { + gallery: + sec.extra.galleryLink + .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.gallery')), + commentary: + sec.extra.commentaryLink + .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.commentary')), + }), - relations.galleryLink && + sec.extra.galleryLink && !sec.extra.commentaryLink && language.$('releaseInfo.viewGallery', { link: - relations.galleryLink + sec.extra.galleryLink .slot('content', language.$('releaseInfo.viewGallery.link')), }), - relations.commentaryLink && + !sec.extra.galleryLink && sec.extra.commentaryLink && language.$('releaseInfo.viewCommentary', { link: - relations.commentaryLink + sec.extra.commentaryLink .slot('content', language.$('releaseInfo.viewCommentary.link')), }), ]), - !empty(relations.externalLinks) && - html.tag('p', - language.$('releaseInfo.listenOn', { - links: language.formatDisjunctionList(relations.externalLinks), - })), - relations.trackList, html.tag('p', @@ -219,11 +259,10 @@ export default { }), ]), - relations.additionalFilesList && [ - relations.additionalFilesHeading + sec.additionalFiles && [ + sec.additionalFiles.heading .slots({ id: 'additional-files', - title: language.$('releaseInfo.additionalFiles.heading', { additionalFiles: @@ -231,18 +270,19 @@ export default { }), }), - relations.additionalFilesList, + sec.additionalFiles.additionalFilesList, ], - data.artistCommentary && [ - relations.artistCommentaryHeading + sec.artistCommentary && [ + sec.artistCommentary.heading .slots({ id: 'artist-commentary', title: language.$('releaseInfo.artistCommentary') }), html.tag('blockquote', - transformMultiline(data.artistCommentary)), + sec.artistCommentary.content + .slot('mode', 'multiline')), ], ]), }; diff --git a/src/content/dependencies/generateAlbumTrackList.js b/src/content/dependencies/generateAlbumTrackList.js index f2f2279d..ce174953 100644 --- a/src/content/dependencies/generateAlbumTrackList.js +++ b/src/content/dependencies/generateAlbumTrackList.js @@ -101,7 +101,7 @@ export default { {class: 'album-group-list'}, data.trackSectionInfo.map((info, index) => [ html.tag('dt', - {class: 'content-heading'}, + {class: 'content-heading', tabindex: '0'}, language.$('trackList.section.withDuration', { section: info.name, duration: diff --git a/src/content/dependencies/generateTrackInfoPageContent.js b/src/content/dependencies/generateTrackInfoPageContent.js index 57bdc0c2..47a9130d 100644 --- a/src/content/dependencies/generateTrackInfoPageContent.js +++ b/src/content/dependencies/generateTrackInfoPageContent.js @@ -2,6 +2,8 @@ import {empty} from '../../util/sugar.js'; export default { contentDependencies: [ + 'generateAdditionalFilesShortcut', + 'generateAlbumAdditionalFilesList', 'generateContentHeading', 'generateCoverArtwork', 'generateTrackList', @@ -12,11 +14,7 @@ export default { 'linkTrack', ], - extraDependencies: [ - 'html', - 'language', - 'transformMultiline', - ], + extraDependencies: ['html', 'language'], relations(relation, track, {topLevelGroups}) { const {album} = track; @@ -28,6 +26,11 @@ export default { contribs.map(contrib => relation('linkContribution', contrib.who, contrib.what)); + const additionalFilesSection = additionalFiles => ({ + heading: relation('generateContentHeading'), + list: relation('generateAlbumAdditionalFilesList', album, additionalFiles), + }); + // Section: Release info const releaseInfo = sections.releaseInfo = {}; @@ -60,6 +63,15 @@ export default { relation('linkExternal', url)); } + // Section: Extra links + + const extra = sections.extra = {}; + + if (!empty(track.additionalFiles)) { + extra.additionalFilesShortcut = + relation('generateAdditionalFilesShortcut', track.additionalFiles); + } + // Section: Other releases if (!empty(track.otherReleases)) { @@ -113,6 +125,44 @@ export default { topLevelGroups); } + // Section: Lyrics + + if (track.lyrics) { + const lyrics = sections.lyrics = {}; + + lyrics.heading = + relation('generateContentHeading'); + + lyrics.content = + relation('transformContent', track.lyrics); + } + + // Sections: Sheet music files, MIDI/proejct files, additional files + + if (!empty(track.sheetMusicFiles)) { + sections.sheetMusicFiles = additionalFilesSection(track.sheetMusicFiles); + } + + if (!empty(track.midiProjectFiles)) { + sections.midiProjectFiles = additionalFilesSection(track.midiProjectFiles); + } + + if (!empty(track.additionalFiles)) { + sections.additionalFiles = additionalFilesSection(track.additionalFiles); + } + + // Section: Artist commentary + + if (track.commentary) { + const artistCommentary = sections.artistCommentary = {}; + + artistCommentary.heading = + relation('generateContentHeading'); + + artistCommentary.content = + relation('transformContent', track.commentary); + } + return relations; }, @@ -141,23 +191,27 @@ export default { data.coverArtFileExtension = album.coverArtFileExtension; } + if (!empty(track.additionalFiles)) { + data.numAdditionalFiles = track.additionalFiles.length; + } + return data; }, - generate(data, relations, { - html, - language, - // transformMultiline, - }) { + generate(data, relations, {html, language}) { const content = {}; const {sections: sec} = relations; const formatContributions = - (contributionLinks, {showContribution = true, showIcons = true} = {}) => - language.formatConjunctionList( - contributionLinks.map(link => - link.slots({showContribution, showIcons}))); + (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) => + contributionLinks && + language.$(stringKey, { + artists: + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))), + }); if (data.hasUniqueCoverArt) { content.cover = relations.cover @@ -190,15 +244,8 @@ export default { [html.onlyIfContent]: true, [html.joinChildren]: html.tag('br'), }, [ - sec.releaseInfo.artistContributionLinks && - language.$('releaseInfo.by', { - artists: formatContributions(sec.releaseInfo.artistContributionLinks), - }), - - sec.releaseInfo.coverArtistContributionLinks && - language.$('releaseInfo.coverArtBy', { - artists: formatContributions(sec.releaseInfo.coverArtistContributionLinks), - }), + formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks), + formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks), data.date && language.$('releaseInfo.released', { @@ -216,41 +263,48 @@ export default { }), ]), - /* + sec.listen.heading.slots({ + id: 'listen-on', + title: + (sec.listen.externalLinks + ? language.$('releaseInfo.listenOn', { + links: language.formatDisjunctionList(sec.listen.externalLinks), + }) + : language.$('releaseInfo.listenOn.noLinks', { + name: html.tag('i', data.name), + })), + }), + html.tag('p', { [html.onlyIfContent]: true, [html.joinChildren]: '<br>', }, [ - hasSheetMusicFiles && + sec.sheetMusicFiles && language.$('releaseInfo.sheetMusicFiles.shortcut', { link: html.tag('a', {href: '#sheet-music-files'}, language.$('releaseInfo.sheetMusicFiles.shortcut.link')), }), - hasMidiProjectFiles && + sec.midiProjectFiles && language.$('releaseInfo.midiProjectFiles.shortcut', { link: html.tag('a', {href: '#midi-project-files'}, language.$('releaseInfo.midiProjectFiles.shortcut.link')), }), - hasAdditionalFiles && - generateAdditionalFilesShortcut(track.additionalFiles), - ]), - */ + sec.additionalFiles && + sec.extra.additionalFilesShortcut, - sec.listen.heading.slots({ - id: 'listen-on', - title: - (sec.listen.externalLinks - ? language.$('releaseInfo.listenOn', { - links: language.formatDisjunctionList(sec.listen.externalLinks), - }) - : language.$('releaseInfo.listenOn.noLinks')), - }), + sec.artistCommentary && + language.$('releaseInfo.readCommentary', { + link: html.tag('a', + {href: '#artist-commentary'}, + language.$('releaseInfo.readCommentary.link')), + }), + ]), sec.otherReleases && [ sec.otherReleases.heading @@ -309,6 +363,64 @@ export default { sec.referencedBy.list, ], + + sec.lyrics && [ + sec.lyrics.heading + .slots({ + id: 'lyrics', + title: language.$('releaseInfo.lyrics'), + }), + + html.tag('blockquote', + sec.lyrics.content + .slot('mode', 'lyrics')), + ], + + sec.sheetMusicFiles && [ + sec.sheetMusicFiles.heading + .slots({ + id: 'sheet-music-files', + title: language.$('releaseInfo.sheetMusicFiles.heading'), + }), + + sec.sheetMusicFiles.list, + ], + + sec.midiProjectFiles && [ + sec.midiProjectFiles.heading + .slots({ + id: 'midi-project-files', + title: language.$('releaseInfo.midiProjectFiles.heading'), + }), + + sec.midiProjectFiles.list, + ], + + sec.additionalFiles && [ + sec.additionalFiles.heading + .slots({ + id: 'additional-files', + title: + language.$('releaseInfo.additionalFiles.heading', { + additionalFiles: + language.countAdditionalFiles(data.numAdditionalFiles, {unit: true}), + }), + }), + + sec.additionalFiles.list, + ], + + sec.artistCommentary && [ + sec.artistCommentary.heading + .slots({ + id: 'artist-commentary', + title: language.$('releaseInfo.artistCommentary') + }), + + html.tag('blockquote', + sec.artistCommentary.content + .slot('mode', 'multiline')), + ], ]), }; diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js index 262c2982..c2ca548a 100644 --- a/src/content/dependencies/transformContent.js +++ b/src/content/dependencies/transformContent.js @@ -1,3 +1,5 @@ +import {marked} from 'marked'; + import {bindFind} from '../../util/find.js'; import {parseInput} from '../../util/replacer.js'; import {replacerSpec} from '../../util/transform-content.js'; @@ -191,7 +193,13 @@ export default { } if (node.type === 'link') { - const {link, label, hash} = relations.links[linkIndex++]; + const linkNode = relations.links[linkIndex++]; + if (linkNode.type === 'text') { + return {type: 'text', data: linkNode.data}; + } + + const {link, label, hash} = linkNode; + return { type: 'link', data: link.slots({content: label, hash}), @@ -242,16 +250,65 @@ export default { return html.tags(contentFromNodes.map(node => node.data)); } - // In multiline mode... + // Multiline mode has a secondary processing stage where it's passed... + // through marked! Rolling your own Markdown only gets you so far :D + + const markedOptions = { + headerIds: false, + mangle: false, + }; + + // This is separated into its own function just since we're gonna reuse + // it in a minute if everything goes to heck in lyrics mode. + const transformMultiline = () => + marked.parse( + contentFromNodes + .map(node => { + if (node.type === 'text') { + return node.data.replace(/\n+/g, '\n\n'); + } else { + return node.data.toString(); + } + }) + .join(''), + markedOptions); if (slots.mode === 'multiline') { - return html.tags(contentFromNodes.map(node => node.data)); + // Unfortunately, we kind of have to be super evil here and stringify + // the links, or else parse marked's output into html tags, which is + // very out of scope at the moment. + return transformMultiline(); } - // In lyrics mode... + // Lyrics mode goes through marked too, but line breaks are processed + // differently. Instead of having each line get its own paragraph, + // "adjacent" lines are joined together (with blank lines separating + // each verse/paragraph). if (slots.mode === 'lyrics') { - return html.tags(contentFromNodes.map(node => node.data)); + // If it looks like old data, using <br> instead of bunched together + // lines... then oh god... just use transformMultiline. Perishes. + if ( + contentFromNodes.some(node => + node.type === 'text' && + node.data.includes('<br')) + ) { + return transformMultiline(); + } + + // Lyrics mode is also evil for the same stringifying reasons as + // multiline. + return marked.parse( + contentFromNodes + .map(node => { + if (node.type === 'text') { + return node.data.replace(/\b\n\b/g, '<br>\n'); + } else { + return node.data.toString(); + } + }) + .join(''), + markedOptions); } }, }); diff --git a/src/data/things/album.js b/src/data/things/album.js index 2a188f2d..47416521 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -103,7 +103,6 @@ export class Album extends Thing { update: {validate: isDimensions}, }, - hasCoverArt: Thing.common.flag(true), hasTrackArt: Thing.common.flag(true), hasTrackNumbers: Thing.common.flag(true), isListedOnHomepage: Thing.common.flag(true), @@ -123,18 +122,16 @@ export class Album extends Thing { artistContribs: Thing.common.dynamicContribs('artistContribsByRef'), coverArtistContribs: Thing.common.dynamicContribs('coverArtistContribsByRef'), - trackCoverArtistContribs: Thing.common.dynamicContribs( - 'trackCoverArtistContribsByRef' - ), - wallpaperArtistContribs: Thing.common.dynamicContribs( - 'wallpaperArtistContribsByRef' - ), - bannerArtistContribs: Thing.common.dynamicContribs( - 'bannerArtistContribsByRef' - ), + trackCoverArtistContribs: Thing.common.dynamicContribs('trackCoverArtistContribsByRef'), + wallpaperArtistContribs: Thing.common.dynamicContribs('wallpaperArtistContribsByRef'), + bannerArtistContribs: Thing.common.dynamicContribs('bannerArtistContribsByRef'), commentatorArtists: Thing.common.commentatorArtists(), + hasCoverArt: Thing.common.contribsPresent('coverArtistContribsByRef'), + hasWallpaperArt: Thing.common.contribsPresent('wallpaperArtistContribsByRef'), + hasBannerArt: Thing.common.contribsPresent('bannerArtistContribsByRef'), + tracks: { flags: {expose: true}, diff --git a/src/data/things/thing.js b/src/data/things/thing.js index f0065b55..cefcd012 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -23,6 +23,7 @@ import { import {inspect} from 'util'; import {color} from '../../util/cli.js'; +import {empty} from '../../util/sugar.js'; import {getKebabCase} from '../../util/wiki-data.js'; import find from '../../util/find.js'; @@ -297,6 +298,22 @@ export default class Thing extends CacheableObject { }, }), + // Nice 'n simple shorthand for an exposed-only flag which is true when any + // contributions are present in the specified property. + contribsPresent: ( + contribsByRefProperty + ) => ({ + flags: {expose: true}, + expose: { + dependencies: [contribsByRefProperty], + compute({ + [contribsByRefProperty]: contribsByRef, + }) { + return !empty(contribsByRef); + }, + } + }), + // Neat little shortcut for "reversing" the reference lists stored on other // things - for example, tracks specify a "referenced tracks" property, and // you would use this to compute a corresponding "referenced *by* tracks" diff --git a/src/data/yaml.js b/src/data/yaml.js index 1b1195ea..5a6f2031 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -191,7 +191,6 @@ export const processAlbumDocument = makeProcessDocument(T.Album, { color: 'Color', urls: 'URLs', - hasCoverArt: 'Has Cover Art', hasTrackArt: 'Has Track Art', hasTrackNumbers: 'Has Track Numbers', isListedOnHomepage: 'Listed on Homepage', diff --git a/src/strings-default.json b/src/strings-default.json index b7151f16..7d487e5a 100644 --- a/src/strings-default.json +++ b/src/strings-default.json @@ -96,14 +96,19 @@ "releaseInfo.viewCommentary.link": "commentary page", "releaseInfo.viewGallery": "View {LINK}!", "releaseInfo.viewGallery.link": "gallery page", + "releaseInfo.viewGalleryOrCommentary": "View {GALLERY} or {COMMENTARY}!", + "releaseInfo.viewGalleryOrCommentary.gallery": "gallery page", + "releaseInfo.viewGalleryOrCommentary.commentary": "commentary page", "releaseInfo.viewOriginalFile": "View {LINK}.", "releaseInfo.viewOriginalFile.withSize": "View {LINK} ({SIZE}).", "releaseInfo.viewOriginalFile.link": "original file", "releaseInfo.viewOriginalFile.sizeWarning": "(Heads up! If you're on a mobile plan, this is a large download.)", "releaseInfo.listenOn": "Listen on {LINKS}.", - "releaseInfo.listenOn.noLinks": "This track has no URLs at which it can be listened.", + "releaseInfo.listenOn.noLinks": "This wiki doesn't have any listening links for {NAME}.", "releaseInfo.visitOn": "Visit on {LINKS}.", "releaseInfo.playOn": "Play on {LINKS}.", + "releaseInfo.readCommentary": "Read {LINK}.", + "releaseInfo.readCommentary.link": "artist commentary", "releaseInfo.alsoReleasedAs": "Also released as:", "releaseInfo.alsoReleasedAs.item": "{TRACK} (on {ALBUM})", "releaseInfo.contributors": "Contributors:", @@ -120,8 +125,8 @@ "releaseInfo.artistCommentary.seeOriginalRelease": "See {ORIGINAL}!", "releaseInfo.artTags": "Tags:", "releaseInfo.artTags.inline": "Tags: {TAGS}", - "releaseInfo.additionalFiles.shortcut": "{ANCHOR_LINK} {TITLES}", - "releaseInfo.additionalFiles.shortcut.anchorLink": "Additional files:", + "releaseInfo.additionalFiles.shortcut": "View {ANCHOR_LINK}: {TITLES}", + "releaseInfo.additionalFiles.shortcut.anchorLink": "additional files", "releaseInfo.additionalFiles.heading": "View or download {ADDITIONAL_FILES}:", "releaseInfo.additionalFiles.entry": "{TITLE}", "releaseInfo.additionalFiles.entry.withDescription": "{TITLE}: {DESCRIPTION}", |