Skip to content

Commit

Permalink
feat(i18n): localize global search filters and results
Browse files Browse the repository at this point in the history
  • Loading branch information
rexxars committed Nov 3, 2023
1 parent 1e5e4b0 commit dc2b4c4
Show file tree
Hide file tree
Showing 36 changed files with 1,239 additions and 509 deletions.
419 changes: 370 additions & 49 deletions dev/test-studio/plugins/locale-no-nb/bundles/studio.ts

Large diffs are not rendered by default.

55 changes: 27 additions & 28 deletions packages/sanity/src/core/form/inputs/DateInputs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,36 @@ export function getCalendarLabels(
t: (key: string, values?: Record<string, unknown>) => string,
): CalendarLabels {
return {
goToNextMonth: t('inputs.datetime.calendar.action.go-to-next-month'),
goToPreviousMonth: t('inputs.datetime.calendar.action.go-to-previous-month'),
goToNextYear: t('inputs.datetime.calendar.action.go-to-next-year'),
goToPreviousYear: t('inputs.datetime.calendar.action.go-to-previous-year'),
setToCurrentTime: t('inputs.datetime.calendar.action.set-to-current-time'),
selectHour: t('inputs.datetime.calendar.action.select-hour'),
selectMinute: t('inputs.datetime.calendar.action.select-minute'),
goToNextMonth: t('calendar.action.go-to-next-month'),
goToPreviousMonth: t('calendar.action.go-to-previous-month'),
goToNextYear: t('calendar.action.go-to-next-year'),
goToPreviousYear: t('calendar.action.go-to-previous-year'),
setToCurrentTime: t('calendar.action.set-to-current-time'),
selectHour: t('calendar.action.select-hour'),
selectMinute: t('calendar.action.select-minute'),
monthNames: [
t('inputs.datetime.calendar.month-names.january'),
t('inputs.datetime.calendar.month-names.february'),
t('inputs.datetime.calendar.month-names.march'),
t('inputs.datetime.calendar.month-names.april'),
t('inputs.datetime.calendar.month-names.may'),
t('inputs.datetime.calendar.month-names.june'),
t('inputs.datetime.calendar.month-names.july'),
t('inputs.datetime.calendar.month-names.august'),
t('inputs.datetime.calendar.month-names.september'),
t('inputs.datetime.calendar.month-names.october'),
t('inputs.datetime.calendar.month-names.november'),
t('inputs.datetime.calendar.month-names.december'),
t('calendar.month-names.january'),
t('calendar.month-names.february'),
t('calendar.month-names.march'),
t('calendar.month-names.april'),
t('calendar.month-names.may'),
t('calendar.month-names.june'),
t('calendar.month-names.july'),
t('calendar.month-names.august'),
t('calendar.month-names.september'),
t('calendar.month-names.october'),
t('calendar.month-names.november'),
t('calendar.month-names.december'),
],
weekDayNamesShort: [
t('inputs.datetime.calendar.weekday-names.short.monday'),
t('inputs.datetime.calendar.weekday-names.short.tuesday'),
t('inputs.datetime.calendar.weekday-names.short.wednesday'),
t('inputs.datetime.calendar.weekday-names.short.thursday'),
t('inputs.datetime.calendar.weekday-names.short.friday'),
t('inputs.datetime.calendar.weekday-names.short.saturday'),
t('inputs.datetime.calendar.weekday-names.short.sunday'),
t('calendar.weekday-names.short.monday'),
t('calendar.weekday-names.short.tuesday'),
t('calendar.weekday-names.short.wednesday'),
t('calendar.weekday-names.short.thursday'),
t('calendar.weekday-names.short.friday'),
t('calendar.weekday-names.short.saturday'),
t('calendar.weekday-names.short.sunday'),
],
setToTimePreset: (time, date) =>
t('inputs.datetime.calendar.action.set-to-time-preset', {time, date}),
setToTimePreset: (time, date) => t('calendar.action.set-to-time-preset', {time, date}),
}
}
415 changes: 369 additions & 46 deletions packages/sanity/src/core/i18n/bundles/studio.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ export default function CommonDateRangeStory() {
Start:
</Text>
<Text muted size={1}>
{value?.dateMin ? value.dateMin : <em>Empty</em>}
{value?.from ? value.from : <em>Empty</em>}
</Text>
</Inline>
<Inline space={1}>
<Text muted size={1} weight="semibold">
End:
</Text>
<Text muted size={1}>
{value?.dateMax ? value.dateMax : <em>Empty</em>}
{value?.to ? value.to : <em>Empty</em>}
</Text>
</Inline>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import isEqual from 'lodash/isEqual'
import React, {useCallback, useId, useMemo} from 'react'
import styled from 'styled-components'
import {useTranslation} from '../../../../../i18n'
import {ORDERINGS} from '../definitions/orderings'
import {useSearchState} from '../contexts/search/useSearchState'
import type {SearchOrdering} from '../types'
Expand Down Expand Up @@ -41,6 +42,7 @@ function isSearchDivider(item: SearchDivider | SearchOrdering): item is SearchDi
}

function CustomMenuItem({ordering}: {ordering: SearchOrdering}) {
const {t} = useTranslation()
const {
dispatch,
state: {ordering: currentOrdering},
Expand All @@ -56,14 +58,15 @@ function CustomMenuItem({ordering}: {ordering: SearchOrdering}) {
<MenuItem onClick={handleClick} padding={3} pressed={isSelected} tone="default">
<Flex align="center" justify="space-between" paddingRight={2}>
<Text size={1} weight="medium">
{ordering.title}
{t(ordering.titleKey)}
</Text>
</Flex>
</MenuItem>
)
}

export function SortMenu() {
const {t} = useTranslation()
const {
state: {ordering},
} = useSearchState()
Expand Down Expand Up @@ -92,7 +95,7 @@ export function SortMenu() {
</Box>
<Inline space={2}>
<Text size={1} weight="medium">
{currentMenuItem.title}
{t(currentMenuItem.titleKey)}
</Text>
</Inline>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {Box, Flex} from '@sanity/ui'
import React from 'react'
import React, {useMemo} from 'react'
import styled from 'styled-components'
import {TextWithTone} from '../../../../../../components'
import {TranslateComponentMap, Translate} from '../../../../../../i18n/Translate'
import {isRecord} from '../../../../../../util'
import {useTranslation} from '../../../../../../i18n'
import {useSearchState} from '../../contexts/search/useSearchState'
import {getOperatorDefinition} from '../../definitions/operators'
import type {SearchFilter} from '../../types'
import type {SearchFilter, SearchFilterValues} from '../../types'
import {FilterTitle} from './FilterTitle'

interface FilterLabelProps {
Expand All @@ -18,38 +21,87 @@ const CustomBox = styled(Box)<{$flexShrink?: number}>`
`

export function FilterLabel({filter, fontSize = 1, showContent = true}: FilterLabelProps) {
const {t} = useTranslation()
const {
state: {definitions, fullscreen},
} = useSearchState()

const operator = getOperatorDefinition(definitions.operators, filter.operatorType)

const ButtonValue = operator?.buttonValueComponent
const filterValue = filter.value

return (
<Flex align="center" gap={1}>
{/* Title */}
<CustomBox $flexShrink={fullscreen ? 1 : 0}>
<TextWithTone tone="default" size={fontSize} textOverflow="ellipsis" weight="semibold">
<FilterTitle filter={filter} maxLength={fullscreen ? 25 : 40} />
</TextWithTone>
</CustomBox>
{/* Operator */}
{showContent && operator?.buttonLabel && (
<CustomBox $flexShrink={0}>
<TextWithTone tone="default" size={fontSize} textOverflow="ellipsis" weight="regular">
{operator.buttonLabel}
</TextWithTone>
</CustomBox>
)}
{/* Value */}
{showContent && ButtonValue && (
<CustomBox $flexShrink={1}>
const components: TranslateComponentMap = useMemo(
() => ({
Field: () => (
<CustomBox $flexShrink={fullscreen ? 1 : 0}>
<TextWithTone tone="default" size={fontSize} textOverflow="ellipsis" weight="semibold">
<ButtonValue value={filter?.value} />
<FilterTitle filter={filter} maxLength={fullscreen ? 25 : 40} />
</TextWithTone>
</CustomBox>
)}
),
Operator: ({children}) =>
showContent && (
<CustomBox $flexShrink={0}>
<TextWithTone tone="default" size={fontSize} textOverflow="ellipsis" weight="regular">
{children}
</TextWithTone>
</CustomBox>
),
Value: ({children}) =>
showContent && (
<CustomBox $flexShrink={1}>
<TextWithTone tone="default" size={fontSize} textOverflow="ellipsis" weight="semibold">
{ButtonValue ? <ButtonValue value={filterValue} /> : children}
</TextWithTone>
</CustomBox>
),
}),
[filter, fontSize, fullscreen, showContent, ButtonValue, filterValue],
)

if (!operator?.descriptionKey) {
console.warn('Missing `descriptionKey` for operator `%s`', filter.operatorType)
}

if (!showContent || !operator?.descriptionKey) {
const Field = components.Field
return (
<Flex align="center" gap={1}>
<Field />
</Flex>
)
}

return (
<Flex align="center" gap={1}>
<Translate
t={t}
i18nKey={operator?.descriptionKey}
components={components}
values={getFilterValues(filter)}
/>
</Flex>
)
}

function getFilterValues(filter: SearchFilter): SearchFilterValues {
const values: SearchFilterValues = {}
if (typeof filter.value === 'number') {
values.count = filter.value
}
if (isStringOrNumber(filter.value)) {
values.value = filter.value
}
if (isRecord(filter.value) && 'from' in filter.value && isStringOrNumber(filter.value.from)) {
values.from = filter.value.from
}
if (isRecord(filter.value) && 'to' in filter.value && isStringOrNumber(filter.value.to)) {
values.to = filter.value.to
}
return values
}

function isStringOrNumber(value: unknown): value is string | number {
return typeof value === 'string' || typeof value === 'number'
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type {Reference} from '@sanity/types'
import {format, isValid} from 'date-fns'
import pluralize from 'pluralize-esm'
import {isValid} from 'date-fns'
import React from 'react'
import {useSchema} from '../../../../../../../hooks'
import type {OperatorNumberRangeValue} from '../../../definitions/operators/common'
import {useUnitFormatter} from '../../../../../../../hooks/useUnitFormatter'
import {useIntlDateTimeFormat} from '../../../../../../../i18n/hooks/useIntlDateTimeFormat'
import type {
OperatorDateDirectionValue,
OperatorDateEqualValue,
OperatorDateLastValue,
OperatorDateRangeValue,
Expand All @@ -24,75 +23,40 @@ export function SearchButtonValueBoolean({value}: OperatorButtonValueComponentPr
export function SearchButtonValueDate({
value,
}: OperatorButtonValueComponentProps<OperatorDateEqualValue>) {
const dateFormat = useIntlDateTimeFormat({
dateStyle: 'medium',
timeStyle: value.includeTime ? 'short' : undefined,
})
const date = value?.date ? new Date(value.date) : null
if (!date || !isValid(date)) {
return null
}
return <>{format(date, DEFAULT_DATE_FORMAT)}</>
}

export function SearchButtonValueDateDirection({
value,
}: OperatorButtonValueComponentProps<OperatorDateDirectionValue>) {
const date = value?.date ? new Date(value.date) : null
if (!date || !isValid(date)) {
return null
}
return <>{format(date, DEFAULT_DATE_FORMAT)}</>
return <>{dateFormat.format(date)}</>
}

export function SearchButtonValueDateLast({
value,
}: OperatorButtonValueComponentProps<OperatorDateLastValue>) {
return (
<>
{Math.floor(value?.unitValue ?? 0)} {value.unit}
</>
)
const formatUnit = useUnitFormatter()
return <>{formatUnit(Math.floor(value?.unitValue ?? 0), value.unit)}</>
}

export function SearchButtonValueDateRange({
value,
}: OperatorButtonValueComponentProps<OperatorDateRangeValue>) {
const startDate = value?.dateMin ? new Date(value.dateMin) : null
const endDate = value?.dateMax ? new Date(value.dateMax) : null
const dateFormat = useIntlDateTimeFormat({
dateStyle: 'medium',
timeStyle: value.includeTime ? 'short' : undefined,
})
const startDate = value?.from ? new Date(value.from) : null
const endDate = value?.to ? new Date(value.to) : null
if (!endDate || !startDate || !isValid(endDate) || !isValid(startDate)) {
return null
}
return (
<>
{format(startDate, DEFAULT_DATE_FORMAT)}{format(endDate, DEFAULT_DATE_FORMAT)}
</>
)
}

export function SearchButtonValueNumber({value}: OperatorButtonValueComponentProps<number>) {
return <>{value}</>
}

export function SearchButtonValueNumberCount({value}: OperatorButtonValueComponentProps<number>) {
return (
<>
{value} {pluralize('item', value)}
</>
)
}

export function SearchButtonValueNumberRange({
value,
}: OperatorButtonValueComponentProps<OperatorNumberRangeValue>) {
return (
<>
{value.min}{value.max}
</>
)
}

export function SearchButtonValueNumberCountRange({
value,
}: OperatorButtonValueComponentProps<OperatorNumberRangeValue>) {
const {t} = useTranslation()
return <>{t('search.number-items-range', {min: value.min, max: value.max})}</>
const from = dateFormat.format(startDate)
const to = dateFormat.format(endDate)
return <>{`${from}${to}`}</>
}

export function SearchButtonValueReference({value}: OperatorButtonValueComponentProps<Reference>) {
Expand All @@ -104,9 +68,3 @@ export function SearchButtonValueReference({value}: OperatorButtonValueComponent
}
return <ReferencePreviewTitle documentId={documentId} schemaType={schemaType} />
}

export function SearchButtonValueString({
value,
}: OperatorButtonValueComponentProps<string | number>) {
return <>{value}</>
}
Loading

0 comments on commit dc2b4c4

Please sign in to comment.