Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(i18n): convert file, image and imagetool input strings to use i18n primitives #4984

Merged
merged 3 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions dev/test-studio/plugins/locale-no-nb/bundles/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,115 @@ const studioResources: Record<StudioLocaleResourceKeys, string> = {
/** Error label for toast when array could not resolve the initial value */
'inputs.array.error.cannot-resolve-initial-value-title':
'Kan ikke finne startverdi for type: {{schemaTypeTitle}}: {{errorMessage}}.',

/** --- File (Image, File and ImageTool) Inputs --- */

/** Open image edit dialog */
'inputs.files.image.actions-menu.edit-details.label': 'Åpne bilderedigeringsdialog',

/** Open image options menu */
'inputs.files.image.actions-menu.options.label': 'Åpne bildeinnstillingsmeny',

/** The upload could not be completed at this time. */
'inputs.files.image.upload-error.description':
'Opplastingen kunne ikke fullføres på dette tidspunktet.',

/** Upload failed */
'inputs.files.image.upload-error.title': 'Opplasting mislyktes',

/** Edit hotspot and crop */
'inputs.files.image.hotspot-dialog.title': 'Rediger fokuspunkt og beskjær',

/** Preview of uploaded image */
'inputs.files.image.preview-uploaded-image': 'Forhåndsvisning av opplastet bilde',

/** Cannot upload this file here */
'inputs.files.image.drag-overlay.cannot-upload-here': 'Kan ikke laste opp denne filen her',

/** This field is read only */
'inputs.files.image.drag-overlay.this-field-is-read-only': 'Dette feltet er skrivebeskyttet',

/** Drop image to upload */
'inputs.files.image.drag-overlay.drop-to-upload-image': 'Slipp bilde for å laste opp',

/** Invalid image value */
'inputs.files.image.invalid-image-warning.title': 'Ugyldig bildeverdi',

/** The value of this field is not a valid image. Resetting this field will let you choose a new image. */
'inputs.files.image.invalid-image-warning.description':
'Verdien i dette feltet er ikke et gyldig bilde. Ved å tilbakestille dette feltet kan du velge et nytt bilde.',

/** The URL is copied to the clipboard */
'inputs.files.common.actions-menu.notification.url-copied':
'URL-en er kopiert til utklippstavlen',

/** Replace */
'inputs.files.common.actions-menu.replace.label': 'Erstatt',

/** Upload */
'inputs.files.common.actions-menu.upload.label': 'Last opp',

/** Download */
'inputs.files.common.actions-menu.download.label': 'Last ned',

/** Copy URL */
'inputs.files.common.actions-menu.copy-url.label': 'Kopier URL',

/** Clear field */
'inputs.files.common.actions-menu.clear-field.label': 'Tøm felt',

/** Can't upload files here */
'inputs.files.common.placeholder.upload-not-supported': 'Kan ikke laste opp filer her',

/** Read only */
'inputs.files.common.placeholder.read-only': 'Skrivebeskyttet',

/** Drop to upload file */
'inputs.files.common.placeholder.drop-to-upload_file': 'Slipp for å laste opp fil',

/** Drop to upload image */
'inputs.files.common.placeholder.drop-to-upload_image': 'Slipp for å laste opp bilde',

/** Cannot upload `{{count}}` files */
'inputs.files.common.placeholder.cannot-upload-some-files_one': 'Kan ikke laste opp fil',
'inputs.files.common.placeholder.cannot-upload-some-files_other':
'Kan ikke laste opp {{count}} filer',

/** Drag or paste file here */
'inputs.files.common.placeholder.drag-or-paste-to-upload_file': 'Dra eller lim inn fil her',
/** Drag or paste image here */
'inputs.files.common.placeholder.drag-or-paste-to-upload_image': 'Dra eller lim inn bilde her',

/** Drop to upload */
'inputs.files.common.drop-message.drop-to-upload': 'Slipp for å laste opp',

/** Drop to upload `{{count}}` file */
'inputs.files.common.drop-message.drop-to-upload-multi_one':
'Slipp for å laste opp {{count}} fil',

/** Drop to upload `{{count}}` files */
'inputs.files.common.drop-message.drop-to-upload-multi_other':
'Slipp for å laste opp {{count}} filer',

/** Uploading <FileName/> */
'input.files.common.upload-progress': 'Laster opp <FileName/>',

/** Incomplete upload */
'inputs.files.common.stale-upload-warning.title': 'Ufullstendig opplasting',

/** An upload has made no progress for at least `{{staleThresholdMinutes}}` minutes and likely got interrupted. You can safely clear the incomplete upload and try uploading again. */
'inputs.files.common.stale-upload-warning.description':
'En opplasting har ikke gjort fremskritt på minst {{staleThresholdMinutes}} minutter og ble sannsynligvis avbrutt. Du kan trygt fjerne den ufullstendige opplastingen og prøve å laste opp på nytt.',

/** Clear upload */
'inputs.files.common.stale-upload-warning.clear': 'Fjern opplasting',

/** Hotspot & Crop */
'inputs.files.imagetool.field.title': 'Fokuspunkt & beskjæring',

/** Adjust the rectangle to crop image. Adjust the circle to specify the area that should always be visible. */
'inputs.files.imagetool.field.description':
'Juster rektangelet for å beskjære bildet. Juster sirkelen for å spesifisere området som alltid skal være synlig.',
}

export default studioResources
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ describe('FileInput with empty state', () => {
observeAsset: observeAssetStub,
render: (inputProps) => <BaseFileInput {...inputProps} />,
})

expect(result.queryByTestId('file-button-input')!.getAttribute('value')).toBe('')
expect(result.queryByText('Drag or paste file here')).toBeInTheDocument()
})

it.todo('renders new file when a new file in uploaded')
Expand Down Expand Up @@ -127,7 +125,9 @@ describe('FileInput with empty state', () => {
render: (inputProps) => <BaseFileInput {...inputProps} directUploads={false} />,
})

expect(result.queryByText(`Can't upload files here`)).toBeInTheDocument()
expect(result.queryByTestId('file-input-upload-button')!.getAttribute('data-disabled')).toBe(
'true',
)
})

/* readOnly - the file input is read only or not */
Expand Down Expand Up @@ -184,7 +184,7 @@ describe('FileInput with empty state', () => {
})

await waitFor(() => {
expect(result.queryByText(`Read only`)).toBeInTheDocument()
expect(result.queryByText(`inputs.files.common.placeholder.read-only`)).toBeInTheDocument()
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {MouseEventHandler, ReactNode, useCallback, useEffect, useState} f
import {EllipsisVerticalIcon, CropIcon} from '@sanity/icons'
import {Button, Inline, Menu, Popover, useClickOutside, useGlobalKeyDown} from '@sanity/ui'
import styled from 'styled-components'
import {useTranslation} from '../../../../i18n'

export const MenuActionsWrapper = styled(Inline)`
position: absolute;
Expand Down Expand Up @@ -79,11 +80,12 @@ export function ImageActionsMenu(props: ImageActionsMenuProps) {
}
}, [isMenuOpen, menuElement])

const {t} = useTranslation()
return (
<MenuActionsWrapper data-buttons space={1} padding={2}>
{showEdit && (
<Button
aria-label="Open image edit dialog"
aria-label={t('inputs.files.image.actions-menu.edit-details.label')}
data-testid="options-menu-edit-details"
icon={CropIcon}
mode="ghost"
Expand All @@ -101,7 +103,7 @@ export function ImageActionsMenu(props: ImageActionsMenuProps) {
constrainSize
>
<Button
aria-label="Open image options menu"
aria-label={t('inputs.files.image.actions-menu.options.label')}
data-testid="options-menu-button"
icon={EllipsisVerticalIcon}
mode="ghost"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {PresenceOverlay} from '../../../../presence'
import {FIXME} from '../../../../FIXME'
import {ImperativeToast} from '../../../../components'
import {ChangeIndicator} from '../../../../changeIndicators'
import {TFunction} from '../../../../i18n'
import {ImageActionsMenu} from './ImageActionsMenu'
import {ImagePreview} from './ImagePreview'
import {InvalidImageWarning} from './InvalidImageWarning'
Expand All @@ -74,6 +75,7 @@ export interface BaseImageInputProps
observeAsset: (documentId: string) => Observable<ImageAsset>
resolveUploader: UploaderResolver
client: SanityClient
t: (key: string, values?: Record<string, string>) => string
}

const getDevicePixelRatio = () => {
Expand Down Expand Up @@ -182,7 +184,7 @@ export class BaseImageInput extends React.PureComponent<BaseImageInputProps, Bas
}

uploadWith = (uploader: Uploader, file: File, assetDocumentProps: UploadOptions = {}) => {
const {schemaType, onChange, client} = this.props
const {schemaType, onChange, client, t} = this.props
const {label, title, description, creditLine, source} = assetDocumentProps
const options = {
metadata: get(schemaType, 'options.metadata'),
Expand All @@ -208,8 +210,8 @@ export class BaseImageInput extends React.PureComponent<BaseImageInputProps, Bas
console.error(err)
this.toast?.push({
status: 'error',
description: 'The upload could not be completed at this time.',
title: 'Upload failed',
description: t('inputs.files.image.upload-error.description'),
title: t('inputs.files.image.upload-error.title'),
})

this.clearUploadStatus()
Expand Down Expand Up @@ -395,14 +397,14 @@ export class BaseImageInput extends React.PureComponent<BaseImageInputProps, Bas
}

renderHotspotInput = (hotspotInputProps: Omit<InputProps, 'renderDefault'>) => {
const {value, changed, id, imageUrlBuilder} = this.props
const {value, changed, id, imageUrlBuilder, t} = this.props

const withImageTool = this.isImageToolEnabled() && value && value.asset

return (
<Dialog
__unstable_autoFocus={false}
header="Edit hotspot and crop"
header={t('inputs.files.image.hotspot-dialog.title')}
id={`${id}_dialog`}
onClickOutside={this.handleCloseDialog}
onClose={this.handleCloseDialog}
Expand All @@ -428,7 +430,7 @@ export class BaseImageInput extends React.PureComponent<BaseImageInputProps, Bas
}

renderPreview = () => {
const {value, schemaType, readOnly, directUploads, imageUrlBuilder, resolveUploader} =
const {value, schemaType, readOnly, directUploads, imageUrlBuilder, t, resolveUploader} =
this.props

if (!value || !isImageSource(value)) {
Expand All @@ -454,7 +456,7 @@ export class BaseImageInput extends React.PureComponent<BaseImageInputProps, Bas
isRejected={rejectedFilesCount > 0 || !directUploads}
readOnly={readOnly}
src={imageUrl}
alt="Preview of uploaded image"
alt={t('inputs.files.image.preview-uploaded-image')}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {ComponentProps, useCallback, useEffect, useState} from 'react'

import {AccessDeniedIcon, ImageIcon, ReadOnlyIcon} from '@sanity/icons'
import {Box, Card, CardTone, Heading, Text, useElementRect} from '@sanity/ui'
import {useTranslation} from '../../../../i18n'
import {
MAX_DEFAULT_HEIGHT,
RatioBox,
Expand Down Expand Up @@ -67,6 +68,7 @@ export function ImagePreview(props: ComponentProps<typeof Card> & Props) {
setLoaded(true)
}, [])

const {t} = useTranslation()
return (
<RatioBox {...rest} ref={setRootElement} style={{height: rootHeight}} tone="transparent">
<Card data-container tone="inherit">
Expand All @@ -91,7 +93,7 @@ export function ImagePreview(props: ComponentProps<typeof Card> & Props) {
<HoverIcon isRejected={isRejected} readOnly={readOnly} />
</Heading>
</Box>
<HoverText isRejected={isRejected} readOnly={readOnly} />
<Text size={1}>{t(getHoverTextTranslationKey({isRejected, readOnly}))}</Text>
</>
}
/>
Expand All @@ -110,16 +112,19 @@ function HoverIcon({isRejected, readOnly}: {isRejected: boolean; readOnly?: bool
return <ImageIcon />
}

function HoverText({isRejected, readOnly}: {isRejected: boolean; readOnly?: boolean}) {
let message = 'Drop image to upload'
function getHoverTextTranslationKey({
isRejected,
readOnly,
}: {
isRejected: boolean
readOnly?: boolean
}) {
if (isRejected) {
message = 'Cannot upload this file here'
return 'inputs.files.image.drag-overlay.this-field-is-read-only'
}
if (readOnly) {
message = 'This field is read only'
}

return <Text size={1}>{message}</Text>
return readOnly
? 'inputs.files.image.drag-overlay.cannot-upload-here'
: 'inputs.files.image.drag-overlay.drop-to-upload-image'
}

function OverlayComponent({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {ResetIcon, WarningOutlineIcon} from '@sanity/icons'
import {Card, Flex, Box, Text, Stack, Button} from '@sanity/ui'
import React from 'react'
import styled from 'styled-components'
import {useTranslation} from '../../../../i18n'

type Props = {
onClearValue?: () => void
Expand All @@ -12,6 +13,7 @@ const ButtonWrapper = styled(Button)`
`

export function InvalidImageWarning({onClearValue}: Props) {
const {t} = useTranslation('sanity')
return (
<Card tone="caution" padding={4} border radius={2}>
<Flex gap={4} marginBottom={4}>
Expand All @@ -22,12 +24,9 @@ export function InvalidImageWarning({onClearValue}: Props) {
</Box>
<Stack space={3}>
<Text size={1} weight="semibold">
Invalid image value
</Text>
<Text size={1}>
The value of this field is not a valid image. Resetting this field will let you choose a
new image.
{t('inputs.files.image.invalid-image-warning.title')}
</Text>
<Text size={1}>{t('inputs.files.image.invalid-image-warning.description')}</Text>
</Stack>
</Flex>
<ButtonWrapper icon={ResetIcon} text="Reset value" onClick={onClearValue} mode="ghost" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {ObjectInputProps} from '../../../types'
import {useDidUpdate} from '../../../hooks/useDidUpdate'
import {ChangeIndicator} from '../../../../changeIndicators'
import {EMPTY_ARRAY} from '../../../../util'
import {useTranslation} from '../../../../i18n'
import {ImageTool, HotspotImage, DEFAULT_CROP, DEFAULT_HOTSPOT} from './imagetool'
import {useLoadImage} from './useLoadImage'

Expand Down Expand Up @@ -108,11 +109,12 @@ export function ImageToolInput(props: ImageToolInputProps) {
[onChange, readOnly, schemaType.fields],
)

const {t} = useTranslation()
return (
<FormField
title="Hotspot &amp; crop"
title={t('inputs.files.imagetool.field.title')}
level={level}
description="Adjust the rectangle to crop image. Adjust the circle to specify the area that should always be visible."
description={t('inputs.files.imagetool.field.description')}
__unstable_presence={presence}
>
<div>
Expand Down
Loading
Loading