1
Fork 0

Add more translations

This commit is contained in:
viktorstrate 2021-04-05 18:49:12 +02:00
parent 12df24f366
commit e530ce5555
No known key found for this signature in database
GPG Key ID: 3F855605109C1E8A
8 changed files with 414 additions and 118 deletions

View File

@ -1,4 +1,14 @@
{
"album_filter": {
"only_favorites": "Vis kun favoritter",
"sort_by": "Sorter efter",
"sorting_options": {
"date_imported": "Dato for importering",
"date_shot": "Dato",
"title": "Titel",
"type": "Type"
}
},
"albums_page": {
"title": "Album"
},
@ -6,8 +16,24 @@
"action": {
"add": "Tilføj",
"cancel": "Annuller",
"remove": "Slet",
"delete": "Slet",
"remove": "Fjern",
"save": "Gem"
},
"loading": {
"default": "Loader...",
"shares": null
}
},
"header": {
"search": {
"loading": "Loader resultater...",
"no_results": "Fandt ingen resultater",
"placeholder": "Søg",
"result_type": {
"albums": "Albums",
"photos": "Billeder"
}
}
},
"loading": {
@ -96,6 +122,76 @@
"title": "Brugere"
}
},
"sidebar": {
"album": {
"title": "Album indstillinger"
},
"download": {
"filesize": {
"byte": "{{count}} Byte",
"byte_plural": "{{count}} Bytes",
"giga_byte": "{{count}} GB",
"kilo_byte": "{{count}} KB",
"mega_byte": "{{count}} MB",
"tera_byte": "{{count}} TB"
},
"table_columns": {
"dimensions": "Dimension",
"file_size": "Størrelse",
"file_type": "Type",
"name": "Navn"
},
"title": "Download"
},
"media": {
"exif": {
"exposure_program": {
"action_program": "Actionprogram",
"aperture_priority": "Blændeprioritet",
"bulb": "Bulb",
"creative_program": "Kreativ program",
"landscape_mode": "Landskabsmode",
"manual": "Manuel",
"normal_program": "Normal program",
"not_defined": "Ikke defineret",
"portrait_mode": "Portræt mode",
"shutter_priority": "Lukkerprioritet"
},
"flash": {
"auto": "Auto",
"did_not_fire": "Blitz affyrede ikke",
"fired": "Affyrede",
"no_flash": "Ingen blitz",
"no_flash_function": "Ingen blitz-funktion",
"off": "Slukket",
"on": "Tændt",
"red_eye_reduction": "Røde øjne reduktion",
"return_detected": "Retur registreret",
"return_not_detected": "Retur ikke registreret"
},
"name": {
"aperture": "Blænde",
"camera": "Kamera",
"date_shot": "Dato",
"exposure": "Lukketid",
"exposure_program": "Lukketid program",
"flash": "Blitz",
"focal_length": "Fokallængde",
"iso": "ISO",
"lens": "Lense",
"maker": "Mærke"
}
}
},
"sharing": {
"add_share": "Tilføj deling",
"copy_link": "Kopier link",
"no_shares_found": "Ingen delinger fundet",
"public_link": "Offentligt link",
"table_header": "Offentlige delinger",
"title": "Indstillinger for deling"
}
},
"sidemenu": {
"albums": "Albums",
"logout": "Log af",

View File

@ -1,4 +1,14 @@
{
"album_filter": {
"only_favorites": "Show only favorites",
"sort_by": "Sort by",
"sorting_options": {
"date_imported": "Date imported",
"date_shot": "Date shot",
"title": "Title",
"type": "Kind"
}
},
"albums_page": {
"title": "Albums"
},
@ -6,8 +16,24 @@
"action": {
"add": "Add",
"cancel": "Cancel",
"delete": "Delete",
"remove": "Remove",
"save": "Save"
},
"loading": {
"default": "Loading...",
"shares": "Loading shares..."
}
},
"header": {
"search": {
"loading": "Loading results...",
"no_results": "No results found",
"placeholder": "Search",
"result_type": {
"albums": "Albums",
"photos": "Photos"
}
}
},
"loading": {
@ -96,6 +122,76 @@
"title": "Users"
}
},
"sidebar": {
"album": {
"title": "Album options"
},
"download": {
"filesize": {
"byte": "{{count}} Byte",
"byte_plural": "{{count}} Bytes",
"giga_byte": "{{count}} GB",
"kilo_byte": "{{count}} KB",
"mega_byte": "{{count}} MB",
"tera_byte": "{{count}} TB"
},
"table_columns": {
"dimensions": "Dimensions",
"file_size": "Size",
"file_type": "Type",
"name": "Name"
},
"title": "Download"
},
"media": {
"exif": {
"exposure_program": {
"action_program": "Action program",
"aperture_priority": "Aperture priority",
"bulb": "Bulb",
"creative_program": "Creative program",
"landscape_mode": "Landscape mode",
"manual": "Manual",
"normal_program": "Normal program",
"not_defined": "Not defined",
"portrait_mode": "Portrait mode",
"shutter_priority": "Shutter priority"
},
"flash": {
"auto": "Auto",
"did_not_fire": "Did not fire",
"fired": "Fired",
"no_flash": "No Flash",
"no_flash_function": "No flash function",
"off": "Off",
"on": "On",
"red_eye_reduction": "Red-eye reduction",
"return_detected": "Return detected",
"return_not_detected": "Return not detected"
},
"name": {
"aperture": "Aperture",
"camera": "Camera",
"date_shot": "Date shot",
"exposure": "Exposure",
"exposure_program": "Program",
"flash": "Flash",
"focal_length": "Focal length",
"iso": "ISO",
"lens": "Lens",
"maker": "Maker"
}
}
},
"sharing": {
"add_share": "Add shares",
"copy_link": "Copy Link",
"no_shares_found": "No shares found",
"public_link": "Public Link",
"table_header": "Public shares",
"title": "Sharing options"
}
},
"sidemenu": {
"albums": "Albums",
"logout": "Log out",

View File

@ -3,29 +3,7 @@ import { authToken } from '../helpers/authentication'
import { Checkbox, Dropdown, Button, Icon } from 'semantic-ui-react'
import styled from 'styled-components'
import PropTypes from 'prop-types'
const sortingOptions = [
{
key: 'date_shot',
value: 'date_shot',
text: 'Date shot',
},
{
key: 'updated_at',
value: 'updated_at',
text: 'Date imported',
},
{
key: 'title',
value: 'title',
text: 'Title',
},
{
key: 'type',
value: 'type',
text: 'Kind',
},
]
import { useTranslation } from 'react-i18next'
const FavoritesCheckboxStyle = styled(Checkbox)`
margin-bottom: 16px;
@ -56,14 +34,18 @@ const FavoritesCheckboxStyle = styled(Checkbox)`
}
`
export const FavoritesCheckbox = ({ onlyFavorites, setOnlyFavorites }) => (
<FavoritesCheckboxStyle
toggle
label="Show only favorites"
checked={onlyFavorites}
onChange={(e, result) => setOnlyFavorites(result.checked)}
/>
)
export const FavoritesCheckbox = ({ onlyFavorites, setOnlyFavorites }) => {
const { t } = useTranslation()
return (
<FavoritesCheckboxStyle
toggle
label={t('album_filter.only_favorites', 'Show only favorites')}
checked={onlyFavorites}
onChange={(e, result) => setOnlyFavorites(result.checked)}
/>
)
}
FavoritesCheckbox.propTypes = {
onlyFavorites: PropTypes.bool.isRequired,
@ -75,17 +57,46 @@ const OrderDirectionButton = styled(Button)`
margin-left: 10px !important;
`
const SortByLabel = styled.strong`
margin-left: 4px;
margin-right: 6px;
`
const AlbumFilter = ({
onlyFavorites,
setOnlyFavorites,
setOrdering,
ordering,
}) => {
const { t } = useTranslation()
const onChangeOrderDirection = (e, data) => {
const direction = data.children.props.name === 'arrow up' ? 'DESC' : 'ASC'
setOrdering({ orderDirection: direction })
}
const sortingOptions = [
{
key: 'date_shot',
value: 'date_shot',
text: t('album_filter.sorting_options.date_shot', 'Date shot'),
},
{
key: 'updated_at',
value: 'updated_at',
text: t('album_filter.sorting_options.date_imported', 'Date imported'),
},
{
key: 'title',
value: 'title',
text: t('album_filter.sorting_options.title', 'Title'),
},
{
key: 'type',
value: 'type',
text: t('album_filter.sorting_options.type', 'Kind'),
},
]
return (
<>
{authToken() && (
@ -94,7 +105,7 @@ const AlbumFilter = ({
setOnlyFavorites={setOnlyFavorites}
/>
)}
<strong> Sort by </strong>
<SortByLabel>{t('album_filter.sort_by', 'Sort by')}</SortByLabel>
<Dropdown
selection
options={sortingOptions}

View File

@ -5,6 +5,7 @@ import { useLazyQuery, gql } from '@apollo/client'
import { debounce } from '../../helpers/utils'
import { ProtectedImage } from '../photoGallery/ProtectedMedia'
import { NavLink } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
const Container = styled.div`
height: 60px;
@ -77,6 +78,7 @@ const SEARCH_QUERY = gql`
`
const SearchBar = () => {
const { t } = useTranslation()
const [fetchSearches, fetchResult] = useLazyQuery(SEARCH_QUERY)
const [query, setQuery] = useState('')
const [fetched, setFetched] = useState(false)
@ -111,7 +113,11 @@ const SearchBar = () => {
return (
<Container>
<SearchField type="search" placeholder="Search" onChange={fetchEvent} />
<SearchField
type="search"
placeholder={t('header.search.placeholder', 'Search')}
onChange={fetchEvent}
/>
{results}
</Container>
)
@ -123,6 +129,7 @@ const ResultTitle = styled.h1`
`
const SearchResults = ({ result }) => {
const { t } = useTranslation()
const { data, loading } = result
const query = data && data.search.query
@ -130,9 +137,9 @@ const SearchResults = ({ result }) => {
const albums = (data && data.search.albums) || []
let message = null
if (loading) message = 'Loading results...'
if (loading) message = t('header.search.loading', 'Loading results...')
else if (data && media.length == 0 && albums.length == 0)
message = 'No results found'
message = t('header.search.no_results', 'No results found')
const albumElements = albums.map(album => (
<AlbumRow key={album.id} query={query} album={album} />
@ -151,9 +158,17 @@ const SearchResults = ({ result }) => {
show={data}
>
{message}
{albumElements.length > 0 && <ResultTitle>Albums</ResultTitle>}
{albumElements.length > 0 && (
<ResultTitle>
{t('header.search.result_type.albums', 'Albums')}
</ResultTitle>
)}
{albumElements}
{mediaElements.length > 0 && <ResultTitle>Photos</ResultTitle>}
{mediaElements.length > 0 && (
<ResultTitle>
{t('header.search.result_type.photos', 'Photos')}
</ResultTitle>
)}
{mediaElements}
</Results>
)

View File

@ -2,6 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import { useQuery, gql } from '@apollo/client'
import SidebarShare from './Sharing'
import { useTranslation } from 'react-i18next'
const albumQuery = gql`
query getAlbumSidebar($id: ID!) {
@ -13,16 +14,17 @@ const albumQuery = gql`
`
const AlbumSidebar = ({ albumId }) => {
const { t } = useTranslation()
const { loading, error, data } = useQuery(albumQuery, {
variables: { id: albumId },
})
if (loading) return <div>Loading...</div>
if (loading) return <div>{t('general.loading.default', 'Loading...')}</div>
if (error) return <div>{error.message}</div>
return (
<div>
<p>Album options</p>
<p>{t('sidebar.album.title', 'Album options')}</p>
<div>
<h1>{data.album.title}</h1>
<SidebarShare album={data.album} />

View File

@ -9,6 +9,7 @@ import SidebarDownload from './SidebarDownload'
import SidebarItem from './SidebarItem'
import { SidebarFacesOverlay } from '../facesOverlay/FacesOverlay'
import { isNil } from '../../helpers/utils'
import { useTranslation } from 'react-i18next'
const mediaQuery = gql`
query sidebarPhoto($id: ID!) {
@ -123,10 +124,13 @@ const MetadataInfoContainer = styled.div`
`
export const MetadataInfo = ({ media }) => {
const { t } = useTranslation()
let exifItems = []
const exifName = exifNameLookup(t)
if (media?.exif) {
let exifKeys = Object.keys(exifNameLookup).filter(
let exifKeys = Object.keys(exifName).filter(
x => media.exif[x] !== null && x != '__typename'
)
@ -146,6 +150,8 @@ export const MetadataInfo = ({ media }) => {
exif.exposure = `1/${1 / exif.exposure}`
}
const exposurePrograms = exposureProgramsLookup(t)
if (
!isNil(exif.exposureProgram) &&
exposurePrograms[exif.exposureProgram]
@ -168,7 +174,7 @@ export const MetadataInfo = ({ media }) => {
}
exifItems = exifKeys.map(key => (
<SidebarItem key={key} name={exifNameLookup[key]} value={exif[key]} />
<SidebarItem key={key} name={exifName[key]} value={exif[key]} />
))
}
@ -206,62 +212,98 @@ MetadataInfo.propTypes = {
media: PropTypes.object,
}
const exifNameLookup = {
camera: 'Camera',
maker: 'Maker',
lens: 'Lens',
exposureProgram: 'Program',
dateShot: 'Date Shot',
exposure: 'Exposure',
aperture: 'Aperture',
iso: 'ISO',
focalLength: 'Focal Length',
flash: 'Flash',
}
const exifNameLookup = t => ({
camera: t('sidebar.media.exif.name.camera', 'Camera'),
maker: t('sidebar.media.exif.name.maker', 'Maker'),
lens: t('sidebar.media.exif.name.lens', 'Lens'),
exposureProgram: t('sidebar.media.exif.name.exposure_program', 'Program'),
dateShot: t('sidebar.media.exif.name.date_shot', 'Date shot'),
exposure: t('sidebar.media.exif.name.exposure', 'Exposure'),
aperture: t('sidebar.media.exif.name.aperture', 'Aperture'),
iso: t('sidebar.media.exif.name.iso', 'ISO'),
focalLength: t('sidebar.media.exif.name.focal_length', 'Focal length'),
flash: t('sidebar.media.exif.name.flash', 'Flash'),
})
// From https://exiftool.org/TagNames/EXIF.html
const exposurePrograms = {
0: 'Not defined',
1: 'Manual',
2: 'Normal program',
3: 'Aperture priority',
4: 'Shutter priority',
5: 'Creative program',
6: 'Action program',
7: 'Portrait mode',
8: 'Landscape mode ',
9: 'Bulb',
}
const exposureProgramsLookup = t => ({
0: t('sidebar.media.exif.exposure_program.not_defined', 'Not defined'),
1: t('sidebar.media.exif.exposure_program.manual', 'Manual'),
2: t('sidebar.media.exif.exposure_program.normal_program', 'Normal program'),
3: t(
'sidebar.media.exif.exposure_program.aperture_priority',
'Aperture priority'
),
4: t(
'sidebar.media.exif.exposure_program.shutter_priority',
'Shutter priority'
),
5: t(
'sidebar.media.exif.exposure_program.creative_program',
'Creative program'
),
6: t('sidebar.media.exif.exposure_program.action_program', 'Action program'),
7: t('sidebar.media.exif.exposure_program.portrait_mode', 'Portrait mode'),
8: t('sidebar.media.exif.exposure_program.landscape_mode', 'Landscape mode'),
9: t('sidebar.media.exif.exposure_program.bulb', 'Bulb'),
})
// From https://exiftool.org/TagNames/EXIF.html#Flash
const flash = {
0x0: 'No Flash',
0x1: 'Fired',
0x5: 'Fired, Return not detected',
0x7: 'Fired, Return detected',
0x8: 'On, Did not fire',
0x9: 'On, Fired',
0xd: 'On, Return not detected',
0xf: 'On, Return detected',
0x10: 'Off, Did not fire',
0x14: 'Off, Did not fire, Return not detected',
0x18: 'Auto, Did not fire',
0x19: 'Auto, Fired',
0x1d: 'Auto, Fired, Return not detected',
0x1f: 'Auto, Fired, Return detected',
0x20: 'No flash function',
0x30: 'Off, No flash function',
0x41: 'Fired, Red-eye reduction',
0x45: 'Fired, Red-eye reduction, Return not detected',
0x47: 'Fired, Red-eye reduction, Return detected',
0x49: 'On, Red-eye reduction',
0x4d: 'On, Red-eye reduction, Return not detected',
0x4f: 'On, Red-eye reduction, Return detected',
0x50: 'Off, Red-eye reduction',
0x58: 'Auto, Did not fire, Red-eye reduction',
0x59: 'Auto, Fired, Red-eye reduction',
0x5d: 'Auto, Fired, Red-eye reduction, Return not detected',
0x5f: 'Auto, Fired, Red-eye reduction, Return detected',
const flash = t => {
const values = {
no_flash: t('sidebar.media.exif.flash.no_flash', 'No Flash'),
fired: t('sidebar.media.exif.flash.fired', 'Fired'),
did_not_fire: t('sidebar.media.exif.flash.did_not_fire', 'Did not fire'),
on: t('sidebar.media.exif.flash.on', 'On'),
off: t('sidebar.media.exif.flash.off', 'Off'),
auto: t('sidebar.media.exif.flash.auto', 'Auto'),
return_not_detected: t(
'sidebar.media.exif.flash.return_not_detected',
'Return not detected'
),
return_detected: t(
'sidebar.media.exif.flash.return_detected',
'Return detected'
),
no_flash_function: t(
'sidebar.media.exif.flash.no_flash_function',
'No flash function'
),
red_eye_reduction: t(
'sidebar.media.exif.flash.red_eye_reduction',
'Red-eye reduction'
),
}
return {
0x0: values['no_flash'],
0x1: values['fired'],
0x5: `${values['fired']}, ${values['return_not_detected']}`,
0x7: `${values['fired']}, ${values['return_detected']}`,
0x8: `${values['on']}, ${values['did_not_fire']}`,
0x9: `${values['on']}, ${values['fired']}`,
0xd: `${values['on']}, ${values['return_not_detected']}`,
0xf: `${values['on']}, ${values['return_detected']}`,
0x10: `${values['off']}, ${values['did_not_fire']}`,
0x14: `${values['off']}, ${values['did_not_fire']}, ${values['return_not_detected']}`,
0x18: `${values['auto']}, ${values['did_not_fire']}`,
0x19: `${values['auto']}, ${values['fired']}`,
0x1d: `${values['auto']}, ${values['fired']}, ${values['return_not_detected']}`,
0x1f: `${values['auto']}, ${values['fired']}, ${values['return_detected']}`,
0x20: `${values['no_flash_function']}`,
0x30: `${values['off']}, ${values['no_flash_function']}`,
0x41: `${values['fired']}, ${values['red_eye_reduction']}`,
0x45: `${values['fired']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
0x47: `${values['fired']}, ${values['red_eye_reduction']}, ${values['return_detected']}`,
0x49: `${values['on']}, ${values['red_eye_reduction']}`,
0x4d: `${values['on']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
0x4f: `${values['on']}, ${values['red_eye_reduction']}, ${values['return_detected']}`,
0x50: `${values['off']}, ${values['red_eye_reduction']}`,
0x58: `${values['auto']}, ${values['did_not_fire']}, ${values['red_eye_reduction']}`,
0x59: `${values['auto']}, ${values['fired']}, ${values['red_eye_reduction']}`,
0x5d: `${values['auto']}, ${values['red_eye_reduction']}, ${values['return_not_detected']}`,
0x5f: `${values['auto']}, ${values['red_eye_redcution']}, ${values['return_detected']}`,
}
}
// From https://exiftool.org/TagNames/EXIF.html

View File

@ -12,6 +12,7 @@ import {
import copy from 'copy-to-clipboard'
import { authToken } from '../../helpers/authentication'
import styled from 'styled-components'
import { useTranslation } from 'react-i18next'
const sharePhotoQuery = gql`
query sidbarGetPhotoShares($id: ID!) {
@ -73,6 +74,7 @@ const deleteShareMutation = gql`
`
const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
const { t } = useTranslation()
const query = isPhoto ? sharePhotoQuery : shareAlbumQuery
const [deleteShare, { loading: deleteShareLoading }] = useMutation(
@ -187,7 +189,7 @@ const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
}}
>
<Checkbox
label="Password"
label={t('login_page.field.password', 'Password')}
onClick={e => e.stopPropagation()}
checked={showPasswordInput}
onChange={() => {
@ -197,7 +199,7 @@ const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
{addPasswordInput}
</Dropdown.Item>
<Dropdown.Item
text="Delete"
text={t('general.action.delete', 'Delete')}
icon="delete"
disabled={deleteShareLoading}
onClick={() => {
@ -224,6 +226,7 @@ const ShareButtonGroup = styled(Button.Group)`
`
const SidebarShare = ({ photo, album }) => {
const { t } = useTranslation()
if ((!photo || !photo.id) && (!album || !album.id)) return null
if (!authToken()) return null
@ -257,7 +260,7 @@ const SidebarShare = ({ photo, album }) => {
}
if (!content && sharesLoading) {
content = <div>Loading shares...</div>
content = <div>{t('general.loading.shares', 'Loading shares...')}</div>
}
if (!content) {
@ -266,13 +269,13 @@ const SidebarShare = ({ photo, album }) => {
const optionsRows = shares.map(share => (
<Table.Row key={share.token}>
<Table.Cell>
<b>Public Link</b> {share.token}
<b>{t('sidebar.sharing.public_link', 'Public Link')}</b> {share.token}
</Table.Cell>
<Table.Cell>
<ShareButtonGroup>
<Button
icon="chain"
content="Copy link"
content={t('sidebar.sharing.copy_link', 'Copy Link')}
onClick={() => {
copy(`${location.origin}/share/${share.token}`)
}}
@ -286,7 +289,9 @@ const SidebarShare = ({ photo, album }) => {
if (optionsRows.length == 0) {
optionsRows.push(
<Table.Row key="no-shares">
<Table.Cell colSpan="2">No shares found</Table.Cell>
<Table.Cell colSpan="2">
{t('sidebar.sharing.no_shares_found', 'No shares found')}
</Table.Cell>
</Table.Row>
)
}
@ -296,7 +301,9 @@ const SidebarShare = ({ photo, album }) => {
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell colSpan="2">Public Shares</Table.HeaderCell>
<Table.HeaderCell colSpan="2">
{t('sidebar.sharing.table_header', 'Public shares')}
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>{optionsRows}</Table.Body>
@ -304,7 +311,7 @@ const SidebarShare = ({ photo, album }) => {
<Table.Row>
<Table.HeaderCell colSpan="2">
<Button
content="Add share"
content={t('sidebar.sharing.add_share', 'Add shares')}
icon="add"
floated="right"
positive
@ -328,7 +335,7 @@ const SidebarShare = ({ photo, album }) => {
return (
<div>
<h2>Sharing options</h2>
<h2>{t('sidebar.sharing.title', 'Sharing options')}</h2>
{content}
</div>
)

View File

@ -5,6 +5,7 @@ import styled from 'styled-components'
import { MessageState } from '../messages/Messages'
import { useLazyQuery, gql } from '@apollo/client'
import { authToken } from '../../helpers/authentication'
import { useTranslation } from 'react-i18next'
export const SIDEBAR_DOWNLOAD_QUERY = gql`
query sidebarDownloadQuery($mediaId: ID!) {
@ -23,14 +24,29 @@ export const SIDEBAR_DOWNLOAD_QUERY = gql`
}
`
function formatBytes(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
const formatBytes = t => bytes => {
if (bytes == 0) return '0 Byte'
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
const count = Math.round(bytes / Math.pow(1024, i), 2)
switch (i) {
case 0:
// i18next-extract-mark-plural-next-line
return t('sidebar.download.filesize.byte', '{{count}} Byte', { count })
case 1:
return t('sidebar.download.filesize.kilo_byte', '{{count}} KB', { count })
case 2:
return t('sidebar.download.filesize.mega_byte', '{{count}} MB', { count })
case 3:
return t('sidebar.download.filesize.giga_byte', '{{count}} GB', { count })
case 4:
return t('sidebar.download.filesize.tera_byte', '{{count}} TB', { count })
default:
return new Error(`invalid byte value: ${bytes}`)
}
}
const downloadMedia = async url => {
const downloadMedia = t => async url => {
const imgUrl = new URL(url, location.origin)
if (authToken() == null) {
@ -47,7 +63,7 @@ const downloadMedia = async url => {
let blob = null
if (response.headers.has('content-length')) {
blob = await downloadMediaShowProgress(response)
blob = await downloadMediaShowProgress(t)(response)
} else {
blob = await response.blob()
}
@ -57,7 +73,7 @@ const downloadMedia = async url => {
downloadBlob(blob, filename)
}
const downloadMediaShowProgress = async response => {
const downloadMediaShowProgress = t => async response => {
const totalBytes = Number(response.headers.get('content-length'))
const reader = response.body.getReader()
let data = new Uint8Array(totalBytes)
@ -98,7 +114,7 @@ const downloadMediaShowProgress = async response => {
props: {
header: 'Downloading photo',
percent: (receivedBytes / totalBytes) * 100,
content: `${formatBytes(receivedBytes)} of ${formatBytes(
content: `${formatBytes(t)(receivedBytes)} of ${formatBytes(t)(
totalBytes
)} bytes downloaded`,
},
@ -151,6 +167,7 @@ const DownloadTableRow = styled(Table.Row)`
`
const SidebarDownload = ({ photo }) => {
const { t } = useTranslation()
if (!photo || !photo.id) return null
const [
@ -176,29 +193,39 @@ const SidebarDownload = ({ photo }) => {
return url.split(/[#?]/)[0].split('.').pop().trim().toLowerCase()
}
const download = downloadMedia(t)
const bytes = formatBytes(t)
let downloadRows = downloads.map(x => (
<DownloadTableRow
key={x.mediaUrl.url}
onClick={() => downloadMedia(x.mediaUrl.url)}
onClick={() => download(x.mediaUrl.url)}
>
<Table.Cell>{`${x.title}`}</Table.Cell>
<Table.Cell>{`${x.mediaUrl.width} x ${x.mediaUrl.height}`}</Table.Cell>
<Table.Cell>{`${formatBytes(x.mediaUrl.fileSize)}`}</Table.Cell>
<Table.Cell>{`${bytes(x.mediaUrl.fileSize)}`}</Table.Cell>
<Table.Cell>{extractExtension(x.mediaUrl.url)}</Table.Cell>
</DownloadTableRow>
))
return (
<div style={{ marginBottom: 24 }}>
<h2>Download</h2>
<h2>{t('sidebar.download.title', 'Download')}</h2>
<Table selectable singleLine compact>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Dimensions</Table.HeaderCell>
<Table.HeaderCell>Size</Table.HeaderCell>
<Table.HeaderCell>Type</Table.HeaderCell>
<Table.HeaderCell>
{t('sidebar.download.table_columns.name', 'Name')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('sidebar.download.table_columns.dimensions', 'Dimensions')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('sidebar.download.table_columns.file_size', 'Size')}
</Table.HeaderCell>
<Table.HeaderCell>
{t('sidebar.download.table_columns.file_type', 'Type')}
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>{downloadRows}</Table.Body>