Skip to content

Commit

Permalink
feat(tasks): Localize Task feature (#6017)
Browse files Browse the repository at this point in the history
* feat(tasks): Localize Task feature

* feat(tasks): updated i18n keys to not have tasks prefix

* feat(tasks): updated missing string to be localized

* feat(task): i18n updates after rebase

* feat(task): added i18n attribute exception
  • Loading branch information
jtpetty authored Mar 19, 2024
1 parent 2afbc3b commit 06d812c
Show file tree
Hide file tree
Showing 25 changed files with 292 additions and 83 deletions.
11 changes: 2 additions & 9 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ const config = {
attributes: [
'animate',
'closed',
'documentType',
'exit',
'fill',
'full',
'initial',
'size',
'sortOrder',
'status',
'group',
],
},
Expand Down Expand Up @@ -280,15 +282,6 @@ const config = {
'@sanity/i18n/no-attribute-template-literals': 'off',
},
},
// Ignore i18n in Tasks files for now. This will need to be removed before task is completed.
{
files: ['**/*/Tasks*.{js,ts,tsx}', '**/*/tasks/**/*'],
rules: {
'i18next/no-literal-string': 'off',
'@sanity/i18n/no-attribute-string-literals': 'off',
'@sanity/i18n/no-attribute-template-literals': 'off',
},
},

// Prefer local components vs certain @sanity/ui imports (in sanity package)
{
Expand Down
27 changes: 27 additions & 0 deletions packages/sanity/src/tasks/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {defineLocaleResourceBundle} from 'sanity'

/**
* The locale namespace for the task tool
*
* @public
*/
export const tasksLocaleNamespace = 'tasks' as const

/**
* The default locale bundle for the task tool, which is US English.
*
* @internal
*/
export const tasksUsEnglishLocaleBundle = defineLocaleResourceBundle({
locale: 'en-US',
namespace: tasksLocaleNamespace,
resources: () => import('./resources'),
})

/**
* The locale resource keys for the task tool.
*
* @alpha
* @hidden
*/
export type {TasksLocaleResourceKeys} from './resources'
105 changes: 105 additions & 0 deletions packages/sanity/src/tasks/i18n/resources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* eslint sort-keys: "error" */
import {defineLocalesResources} from 'sanity'

/**
* Defined locale strings for the task tool, in US English.
*
* @internal
*/
const tasksLocaleStrings = defineLocalesResources('tasks', {
/** The label for the create task action */
'actions.create.text': 'Create new task',
/** The label for the open tasks panel action */
'actions.open.text': 'Tasks',
/** The label for the button to create a new task */
'buttons.create.text': 'Create Task',
/** The label for the button to discard changes */
'buttons.discard.text': 'Discard',
/** The label for the button to open the draft */
'buttons.draft.text': 'Draft',
/** The label for the button to create a new task */
'buttons.new.text': 'New task',
/** The label for the button that will navigate to the next task */
'buttons.next.tooltip': 'Go to next task',
/** The label for the button that will previous to the next task */
'buttons.previous.tooltip': 'Go to previous task',
/** Text for the remove task dialog asking for confirmation of deletion */
'dialog.remove-task.body': 'Are you sure you want to delete this task?',
/** Text for the remove task dialog clarifying that deletion is permanent */
'dialog.remove-task.body2': 'Once deleted, it cannot be restored.',
/** The label for the cancel button on the remove task dialog */
'dialog.remove-task.buttons.cancel.text': 'Cancel',
/** The label for the confirmation button on the remove task dialog */
'dialog.remove-task.buttons.confirm.text': 'Remove',
/** The title for the remove task dialog */
'dialog.remove-task.title': 'Remove task',
/** The text used as a placeholder for the footer action in a document with a single task */
'document.footer.open-tasks.placeholder_one': 'Open task',
/** The text used as a placeholder for the footer action in a document with multiple tasks */
'document.footer.open-tasks.placeholder_other': 'Open tasks',
/** The label used in the button in the footer action in a document with a single task */
'document.footer.open-tasks.text_one': '{{count}} open task',
/** The label used in the button in the footer action in a document with multiple tasks */
'document.footer.open-tasks.text_other': '{{count}} open tasks',
/** Text used in the assignee input when there is no user assigned */
'form.input.assignee.no-user-assigned.text': 'Not assigned',
/** Text used in the assignee input when searching and no users are found */
'form.input.assignee.search.no-users.text': 'No users found',
/** Placeholder text used in the search box in the assignee input */
'form.input.assignee.search.placeholder': 'Search username',
/** Text used in the assignee input when user is not authorized */
'form.input.assignee.unauthorized.text': 'Unauthorized',
/** Text used in the assignee input when user is not found */
'form.input.assignee.user-not-found.text': 'User not found',
/** The label used in the create more toggle */
'form.input.create-more.text': 'Create more',
/** The label used in the date input to remove the current value */
'form.input.date.buttons.remove.text': 'Remove',
/** Placeholder text used in the description input */
'form.input.description.placeholder': 'Optional additional description',
/** The label used in the target input to remove the current value */
'form.input.target.buttons.remove.text': 'Remove target content',
/** The text used in the target input when encountering a schema error */
'form.input.target.error.schema-not-found': 'Schema not found',
/** The placeholder text used in the target input for the search component */
'form.input.target.search.placeholder': 'Search document',
/** The placeholder text for the title input */
'form.input.title.placeholder': 'Task title',
/** The status error message presented when the user does not supply a title */
'form.status.error.title-required': 'Title is required',
/** The status message upon successful creation of a task */
'form.status.success': 'Task created',
/** The text displayed when no tasks are found */
'list.empty.text': 'No tasks',
/** The label for the copy link menu item */
'menuitem.copylink.text': 'Copy link to task',
/** The label for the delete task menu item */
'menuitem.delete.text': 'Delete task',
/** The label for the duplicate task menu item */
'menuitem.duplicate.text': 'Duplicate task',
/** Fragment used to construct the first entry in the activity log */
'panel.activity.created-fragment': 'created this task',
/** The title of the activity section of the task */
'panel.activity.title': 'Activity',
/** The text used in the activity log when unable to find the user */
'panel.activity.unknown-user': 'Unknown user',
/** The tooltip for the close button for the task panel */
'panel.close.tooltip': 'Close sidebar',
/** The placeholder text for the comment text box */
'panel.comment.placeholder': 'Add a comment...',
/** The title used in the task panel when showing the create task form */
'panel.create.title': 'Create',
/** The title used in the drafts pulldown */
'panel.drafts.title': 'Drafts',
/** The tooltip for the task navigation component */
'panel.navigation.tooltip': 'Open tasks',
/** Title of the Tasks panel */
'panel.title': 'Tasks',
})

/**
* @alpha
*/
export type TasksLocaleResourceKeys = keyof typeof tasksLocaleStrings

export default tasksLocaleStrings
1 change: 1 addition & 0 deletions packages/sanity/src/tasks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export {tasksLocaleNamespace, type TasksLocaleResourceKeys} from './i18n'
export * from './plugin'
export * from './src/tasks'
9 changes: 6 additions & 3 deletions packages/sanity/src/tasks/plugin/TaskCreateAction.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {TaskIcon} from '@sanity/icons'
import {useCallback} from 'react'
import {type DocumentActionDescription} from 'sanity'
import {type DocumentActionDescription, useTranslation} from 'sanity'

import {tasksLocaleNamespace} from '../i18n'
import {useTasksEnabled, useTasksNavigation} from '../src'

export function TaskCreateAction(): DocumentActionDescription | null {
Expand All @@ -13,12 +14,14 @@ export function TaskCreateAction(): DocumentActionDescription | null {
setViewMode({type: 'create'})
}, [handleOpenTasks, setViewMode])

const {t} = useTranslation(tasksLocaleNamespace)

if (!enabled) return null

return {
icon: TaskIcon,
label: 'Create new task',
title: 'Create new task',
label: t('actions.create.text'),
title: t('actions.create.text'),
group: ['paneActions'],
onHandle: handleCreateTaskFromDocument,
}
Expand Down
17 changes: 13 additions & 4 deletions packages/sanity/src/tasks/plugin/TasksFooterOpenTasks.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {TaskIcon} from '@sanity/icons'
import {Badge, useMediaIndex} from '@sanity/ui'
import {useCallback, useMemo} from 'react'
import {useTranslation} from 'sanity'
import styled from 'styled-components'

import {Button} from '../../ui-components'
import {tasksLocaleNamespace} from '../i18n'
import {useTasks, useTasksEnabled, useTasksNavigation} from '../src'

const ButtonContainer = styled.div`
Expand Down Expand Up @@ -44,9 +46,10 @@ export function TasksFooterOpenTasks() {
setActiveTab('document')
}, [handleOpenTasks, setActiveTab])

const {t} = useTranslation(tasksLocaleNamespace)

if (pendingTasks.length === 0 || !enabled) return null

const pluralizedTask = `task${pendingTasks.length > 1 ? 's' : ''}`
if (mediaIndex < 3) {
return (
<ButtonContainer>
Expand All @@ -56,7 +59,9 @@ export function TasksFooterOpenTasks() {
size={'large'}
onClick={handleOnClick}
tooltipProps={{
content: `Open ${pluralizedTask}`,
content: t('document.footer.open-tasks.placeholder', {
count: pendingTasks.length,
}),
}}
/>
<Badge tone="primary" fontSize={0}>
Expand All @@ -68,8 +73,12 @@ export function TasksFooterOpenTasks() {
return (
<Button
mode="bleed"
tooltipProps={{content: `Open ${pluralizedTask}`}}
text={`${pendingTasks.length} open ${pluralizedTask}`}
tooltipProps={{
content: t('document.footer.open-tasks.placeholder', {
count: pendingTasks.length,
}),
}}
text={t('document.footer.open-tasks.text', {count: pendingTasks.length})}
onClick={handleOnClick}
/>
)
Expand Down
11 changes: 7 additions & 4 deletions packages/sanity/src/tasks/plugin/TasksStudioNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {PanelRightIcon, TaskIcon} from '@sanity/icons'
import {useCallback, useMemo} from 'react'
import {type NavbarProps} from 'sanity'
import {type NavbarProps, useTranslation} from 'sanity'

import {tasksLocaleNamespace} from '../i18n'
import {useTasksEnabled, useTasksNavigation} from '../src'

const EMPTY_ARRAY: [] = []
Expand All @@ -21,6 +22,8 @@ function TasksStudioNavbarInner(props: NavbarProps) {
}
}, [handleCloseTasks, handleOpenTasks, isOpen])

const {t} = useTranslation(tasksLocaleNamespace)

const actions = useMemo((): NavbarProps['__internal_actions'] => {
return [
...(props?.__internal_actions || EMPTY_ARRAY),
Expand All @@ -30,18 +33,18 @@ function TasksStudioNavbarInner(props: NavbarProps) {
name: 'tasks-topbar',
onAction: handleAction,
selected: isOpen,
title: 'Tasks',
title: t('actions.open.text'),
},
{
icon: TaskIcon,
location: 'sidebar',
name: 'tasks-sidebar',
onAction: handleAction,
selected: isOpen,
title: 'Tasks',
title: t('actions.open.text'),
},
]
}, [handleAction, isOpen, props?.__internal_actions])
}, [handleAction, isOpen, props?.__internal_actions, t])

return props.renderDefault({
...props,
Expand Down
5 changes: 4 additions & 1 deletion packages/sanity/src/tasks/plugin/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {definePlugin, type ObjectInputProps} from 'sanity'

import {tasksUsEnglishLocaleBundle} from '../i18n'
import {TaskCreateAction} from './TaskCreateAction'
import {TasksDocumentInputLayout} from './TasksDocumentInputLayout'
import {TasksFooterOpenTasks} from './TasksFooterOpenTasks'
import {TasksStudioActiveToolLayout} from './TasksStudioActiveToolLayout'
import {TasksStudioLayout} from './TasksStudioLayout'
import {TasksStudioNavbar} from './TasksStudioNavbar'

/**
* @internal
* @beta
Expand Down Expand Up @@ -40,4 +40,7 @@ export const tasks = definePlugin({
},
},
},
i18n: {
bundles: [tasksUsEnglishLocaleBundle],
},
})
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {Card} from '@sanity/ui'
import {useCallback, useMemo, useRef, useState} from 'react'
import {useTranslation} from 'sanity'

import {
CommentInput,
type CommentInputHandle,
type CommentInputProps,
hasCommentMessageValue,
} from '../../../../../structure/comments'
import {tasksLocaleNamespace} from '../../../../i18n'
import {ActivityItem} from './TasksActivityItem'

interface TasksCommentActivityInputProps {
Expand Down Expand Up @@ -58,6 +60,7 @@ export function TasksActivityCommentInput(props: TasksCommentActivityInputProps)
},
[hasValue],
)
const {t} = useTranslation(tasksLocaleNamespace)

return (
<ActivityItem userId={currentUser.id}>
Expand All @@ -72,7 +75,7 @@ export function TasksActivityCommentInput(props: TasksCommentActivityInputProps)
onDiscardCancel={handleDiscardCancel}
onKeyDown={handleKeyDown}
onSubmit={handleSubmit}
placeholder="Add a comment..."
placeholder={t('panel.comment.placeholder')}
ref={editorRef}
value={value}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Flex, Text, TextSkeleton} from '@sanity/ui'
import {memo} from 'react'
import {useUser} from 'sanity'
import {useTranslation, useUser} from 'sanity'
import styled from 'styled-components'

import {Tooltip} from '../../../../../ui-components'
import {tasksLocaleNamespace} from '../../../../i18n'
import {UpdatedTimeAgo} from './helpers'
import {ActivityItem} from './TasksActivityItem'

Expand All @@ -22,15 +23,15 @@ export const TasksActivityCreatedAt = memo(
const {createdAt, authorId} = props
const [user, loading] = useUser(authorId)
const {timeAgo, formattedDate} = UpdatedTimeAgo(createdAt)

const {t} = useTranslation(tasksLocaleNamespace)
return (
<ActivityItem userId={authorId}>
<Flex align="center" paddingTop={1}>
<Text size={1} muted>
<strong style={{fontWeight: 600}}>
{loading ? <UserSkeleton /> : user?.displayName ?? 'Unknown user'}{' '}
{loading ? <UserSkeleton /> : user?.displayName ?? t('panel.activity.unknown-user')}{' '}
</strong>
created this task{' '}
{t('panel.activity.created-fragment')}{' '}
<Tooltip content={formattedDate} placement="top-end">
<time dateTime={createdAt}>{timeAgo}</time>
</Tooltip>
Expand Down
Loading

0 comments on commit 06d812c

Please sign in to comment.