diff --git a/apps/web/src/app/[locale]/app/videos/[id]/page.tsx b/apps/web/src/app/[locale]/app/videos/[id]/page.tsx index 600875a..8ea48c2 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/page.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/page.tsx @@ -7,9 +7,10 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Overview } from './tabs/overview' import { Webhooks } from './tabs/webhooks' +import { Locale, getDictionary } from '@nivo/i18n' interface VideoPageProps { - params: { id: string } + params: { id: string, locale: Locale } } export async function generateMetadata(): Promise { @@ -19,13 +20,14 @@ export async function generateMetadata(): Promise { } export default async function VideoPage({ params }: VideoPageProps) { + const dictionary = await getDictionary(params.locale) const videoId = params.id return ( <>

- Edit video + {dictionary.video_page_edit_video_title}

@@ -36,7 +38,7 @@ export default async function VideoPage({ params }: VideoPageProps) { rel="noreferrer" > - Download MP4 + {dictionary.video_page_download_mp4}
@@ -54,12 +56,12 @@ export default async function VideoPage({ params }: VideoPageProps) { - Overview - Webhooks + {dictionary.video_page_overview_tab} + {dictionary.video_page_webhooks_tab} - + diff --git a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/index.tsx b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/index.tsx index 173b85e..50b66d8 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/index.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/index.tsx @@ -1,4 +1,4 @@ -import { unstable_noStore } from 'next/cache' +import { unstable_noStore } from 'next/cache'; import { Card, @@ -6,29 +6,49 @@ import { CardDescription, CardHeader, CardTitle, -} from '@/components/ui/card' -import { serverClient } from '@/lib/trpc/server' +} from '@/components/ui/card'; +import { serverClient } from '@/lib/trpc/server'; -import { TranscriptionCard } from '../../transcription-card' -import { VideoForm } from './video-form' +import { TranscriptionCard } from '../../transcription-card'; +import { VideoForm } from './video-form'; +import { Dictionary } from '@nivo/i18n'; export interface OverviewProps { - videoId: string + videoId: string; + dictionary: Dictionary; } -export async function Overview({ videoId }: OverviewProps) { - unstable_noStore() +export async function Overview({ videoId, dictionary }: OverviewProps) { + unstable_noStore(); - const { video } = await serverClient.getUpload({ - videoId, - }) + type videoMockType = Awaited>; + const videoMock: videoMockType['video'] = { + tags: [ + { slug: "example-tag-1" }, + { slug: "example-tag-2" }, + ], + id: "video-id-123", + companyId: "company-id-456", + description: "This is a mock description of the video.", + duration: 3600, // Duration in seconds (e.g., 1 hour) + title: "Mock Video Title", + storageKey: "storage-key-789", + authorId: "author-id-101112", + createdAt: new Date(), // Current date and time + // @ts-ignore + language: "en", + }; + const video = videoMock; + // const { video } = await serverClient.getUpload({ + // videoId, + // }); return (
- Edit video - Update video details + {dictionary.overview_edit_video_title} + {dictionary.overview_update_video_details} @@ -39,5 +59,5 @@ export async function Overview({ videoId }: OverviewProps) { shouldDisplayVideo={!!video.storageKey} />
- ) + ); } diff --git a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-description-input.tsx b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-description-input.tsx index 0018532..d620dee 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-description-input.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-description-input.tsx @@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { EditVideoFormSchema } from './video-form' +import { useDictionary } from '@/state/dictionary' export interface VideoDescriptionInputProps extends TextareaHTMLAttributes { @@ -20,6 +21,7 @@ export function VideoDescriptionInput({ videoId, ...props }: VideoDescriptionInputProps) { + const dictionary = useDictionary() const { setValue, register } = useFormContext() const { completion, complete, isLoading } = useCompletion({ @@ -40,6 +42,7 @@ export function VideoDescriptionInput({ className="min-h-[132px] leading-relaxed" {...register('description')} {...props} + placeholder={dictionary.video_description_placeholder} />
diff --git a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-form.tsx b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-form.tsx index 9b020a9..0fbd7d2 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-form.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/tabs/overview/video-form.tsx @@ -17,28 +17,32 @@ import { trpc } from '@/lib/trpc/react' import { VideoDescriptionInput } from './video-description-input' import { VideoTagInput } from './video-tag-input' +import { useDictionary } from '@/state/dictionary' +import { Dictionary } from '@nivo/i18n' interface VideoFormProps { video: RouterOutput['getUpload']['video'] } -const editVideoFormSchema = z.object({ - title: z.string().min(1, { message: 'Please provide a valid title.' }), +const editVideoFormSchema = (dictionary: Dictionary) => z.object({ + title: z.string().min(1, { message: dictionary.edit_video_form_error_valid_title }), description: z.string().nullable(), commitUrl: z .string() - .url({ message: 'Please provide a valid Github URL.' }) + .url({ message: dictionary.edit_video_form_error_valid_github_url }) .nullable(), tags: z.array(z.string()).min(1, { - message: 'At least one tag is required.', + message: dictionary.edit_video_form_error_tag_required, }), }) -export type EditVideoFormSchema = z.infer +export type EditVideoFormSchema = z.infer> export function VideoForm({ video }: VideoFormProps) { + const dictionary = useDictionary() + const editVideoForm = useForm({ - resolver: zodResolver(editVideoFormSchema), + resolver: zodResolver(editVideoFormSchema(dictionary)), defaultValues: { title: video.title, description: video.description, @@ -64,8 +68,8 @@ export function VideoForm({ video }: VideoFormProps) { commitUrl, }) } catch { - toast.error('Uh oh! Something went wrong.', { - description: `An error ocurred while trying to save the video.`, + toast.error(dictionary.video_form_toast_error, { + description: dictionary.video_form_toast_error_description, }) } } @@ -81,8 +85,8 @@ export function VideoForm({ video }: VideoFormProps) {
{errors.title && ( @@ -93,14 +97,14 @@ export function VideoForm({ video }: VideoFormProps) {
- +
- +
- {video.externalStatus || 'waiting'} + {video.externalStatus || dictionary.video_form_external_status_waiting} ) : ( - 'Save' + dictionary.video_form_save_button )} {isSubmitSuccessful && (
- Saved! + {dictionary.video_form_saved_message}
)}
diff --git a/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/index.tsx b/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/index.tsx index ade37f1..c161656 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/index.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/index.tsx @@ -22,12 +22,15 @@ import { formatSecondsToMinutes } from '@/utils/format-seconds-to-minutes' import { MetadataTooltip } from './metadata-tooltip' import { WebhooksSkeletonTable } from './webhooks-skeleton-table' +import { useDictionary } from '@/state/dictionary' export interface WebhooksProps { videoId: string } export function Webhooks({ videoId }: WebhooksProps) { + const dictionary = useDictionary() + const { data, isLoading: isLoadingWebhooks, @@ -51,18 +54,18 @@ export function Webhooks({ videoId }: WebhooksProps) {
- Webhook + {dictionary.webhook_table_head_webhook} {isRefetchingWebhooks && ( )}
- Status - Executed At - Duration + {dictionary.webhook_table_head_status} + {dictionary.webhook_table_head_executed_at} + {dictionary.webhook_table_head_duration}
- Metadata + {dictionary.webhook_table_head_metadata}
@@ -85,21 +88,21 @@ export function Webhooks({ videoId }: WebhooksProps) { {webhook.status === 'RUNNING' && (
- Running + {dictionary.webhook_status_running}
)} {webhook.status === 'SUCCESS' && (
- Success + {dictionary.webhook_status_success}
)} {webhook.status === 'ERROR' && (
- Error + {dictionary.webhook_status_error}
)} @@ -137,7 +140,7 @@ export function Webhooks({ videoId }: WebhooksProps) { ) : ( - No results. + {dictionary.webhook_no_results} )} diff --git a/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/metadata-tooltip.tsx b/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/metadata-tooltip.tsx index 126688e..f4ccab4 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/metadata-tooltip.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/tabs/webhooks/metadata-tooltip.tsx @@ -6,8 +6,11 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' +import { useDictionary } from '@/state/dictionary' export function MetadataTooltip() { + const dictionary = useDictionary() + return ( @@ -16,7 +19,7 @@ export function MetadataTooltip() {

- Request body sent to the webhook + {dictionary.metadata_tooltip_request_body}

diff --git a/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/index.tsx b/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/index.tsx index 9a2e527..0ed461e 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/index.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/index.tsx @@ -15,6 +15,7 @@ import { trpc } from '@/lib/trpc/react' import { Segment } from './segment' import { TranscriptionSkeleton } from './transcription-skeleton' +import { useDictionary } from '@/state/dictionary' interface TranscriptionCardProps { videoId: string @@ -30,14 +31,13 @@ const transcriptionSegmentsFormSchema = z.object({ ), }) -type TranscriptionSegmentsFormSchema = z.infer< - typeof transcriptionSegmentsFormSchema -> +type TranscriptionSegmentsFormSchema = z.infer export function TranscriptionCard({ videoId, shouldDisplayVideo, }: TranscriptionCardProps) { + const dictionary = useDictionary() const [shouldFollowUserFocus, setShouldFollowUserFocus] = useState(true) const { data: videoDownloadData } = trpc.requestMediaDownloadUrl.useQuery({ @@ -148,7 +148,7 @@ export function TranscriptionCard({ checked={shouldFollowUserFocus} onCheckedChange={setShouldFollowUserFocus} /> - +
) : (
@@ -158,7 +158,7 @@ export function TranscriptionCard({ {isSubmitting ? ( ) : ( - Save + {dictionary.transcription_card_button_save} )} diff --git a/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/transcription-skeleton.tsx b/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/transcription-skeleton.tsx index 82db67e..908627d 100644 --- a/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/transcription-skeleton.tsx +++ b/apps/web/src/app/[locale]/app/videos/[id]/transcription-card/transcription-skeleton.tsx @@ -1,9 +1,14 @@ +'use client' + import { Loader2 } from 'lucide-react' import { CardContent } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' +import { useDictionary } from '@/state/dictionary' export function TranscriptionSkeleton() { + const dictionary = useDictionary() + return ( <> @@ -14,9 +19,9 @@ export function TranscriptionSkeleton() {
- Transcription is being generated + {dictionary.transcription_skeleton_generating} - The page will automatically refresh... + {dictionary.transcription_skeleton_auto_refresh}
diff --git a/apps/web/src/components/create-new-tag-dialog.tsx b/apps/web/src/components/create-new-tag-dialog.tsx index 72f3f5d..e472d89 100644 --- a/apps/web/src/components/create-new-tag-dialog.tsx +++ b/apps/web/src/components/create-new-tag-dialog.tsx @@ -1,3 +1,5 @@ +'use client' + import { zodResolver } from '@hookform/resolvers/zod' import { AlertCircle, Loader2 } from 'lucide-react' import { useForm } from 'react-hook-form' @@ -18,18 +20,20 @@ import { } from './ui/dialog' import { Input } from './ui/input' import { Label } from './ui/label' +import { useDictionary } from '@/state/dictionary' +import { Dictionary } from '@nivo/i18n' -const newTagFormSchema = z.object({ +const newTagFormSchema = (dictionary: Dictionary) => z.object({ tag: z .string({ - required_error: 'The tag name is required.', + required_error: dictionary.new_tag_form_error_required, }) .regex(/^[a-zA-Z]+(-[a-zA-Z]+)*$/, { - message: 'Use only letters and hyphens.', + message: dictionary.new_tag_form_error_format, }), }) -type NewTagFormSchema = z.infer +type NewTagFormSchema = z.infer> interface CreateNewTagDialogProps { onRequestClose: () => void @@ -38,6 +42,7 @@ interface CreateNewTagDialogProps { export function CreateNewTagDialog({ onRequestClose, }: CreateNewTagDialogProps) { + const dictionary = useDictionary() const utils = trpc.useUtils() const { @@ -46,7 +51,7 @@ export function CreateNewTagDialog({ formState: { isSubmitting, errors }, reset, } = useForm({ - resolver: zodResolver(newTagFormSchema), + resolver: zodResolver(newTagFormSchema(dictionary)), defaultValues: { tag: '', }, @@ -65,8 +70,8 @@ export function CreateNewTagDialog({ reset() onRequestClose() } catch (err) { - toast.error('Uh oh! Something went wrong.', { - description: `An error ocurred while trying to create the tag. Maybe you're trying to create a duplicated tag.`, + toast.error(dictionary.create_tag_error, { + description: dictionary.create_tag_error_description, }) } } @@ -74,37 +79,34 @@ export function CreateNewTagDialog({ return ( - Create new tag + {dictionary.create_new_tag}

- Remember to avoid creating tags unnecessarily and to keep a maximum - of{' '} + {dictionary.create_new_tag_description_1}{' '} - 3 tags per video + {dictionary.create_new_tag_description_2} .

- Use the{' '} + {dictionary.create_new_tag_description_3}{' '} - following examples - {' '} - to name your tags: + {dictionary.create_new_tag_description_4} + + :

  1. - ignite - reference to the product + ignite - {dictionary.example_tag_product}
  2. - react - reference to the main - technology + react - {dictionary.example_tag_technology}
  3. - fundamentos-do-react - reference - to the course + fundamentos-do-react - {dictionary.example_tag_course}
@@ -114,12 +116,12 @@ export function CreateNewTagDialog({
@@ -134,14 +136,14 @@ export function CreateNewTagDialog({ diff --git a/apps/web/src/components/tag-input.tsx b/apps/web/src/components/tag-input.tsx index 44534d7..a22e081 100644 --- a/apps/web/src/components/tag-input.tsx +++ b/apps/web/src/components/tag-input.tsx @@ -1,3 +1,5 @@ +'use client' + import { CheckIcon, PlusIcon } from '@radix-ui/react-icons' import { Loader2, Tag } from 'lucide-react' import { useState } from 'react' @@ -20,6 +22,7 @@ import { Dialog } from './ui/dialog' import { Popover, PopoverContent, PopoverTrigger } from './ui/popover' import { ScrollArea } from './ui/scroll-area' import { Separator } from './ui/separator' +import { useDictionary } from '@/state/dictionary' export interface TagInputProps { value: string[] @@ -38,6 +41,7 @@ export function TagInput({ allowTagCreation = true, onApplyToAll, }: TagInputProps) { + const dictionary = useDictionary() const [createTagDialogOpen, setCreateTagDialogOpen] = useState(false) const [open, setOpen] = useState(false) const [search, setSearch] = useState('') @@ -73,7 +77,7 @@ export function TagInput({ className="flex h-8 items-center border-dashed px-2 data-[error=true]:border-red-400 data-[error=true]:bg-red-50" > - Tags + {dictionary.tags_label} {!!error && ( {error} @@ -88,7 +92,7 @@ export function TagInput({ variant="secondary" className="pointer-events-none text-nowrap rounded-sm px-1 font-normal" > - {value.length} selected + {value.length} {dictionary.selected_label} ) : ( value.map((tag) => ( @@ -109,7 +113,7 @@ export function TagInput({ @@ -125,18 +129,18 @@ export function TagInput({ className="flex items-center gap-2" > - Create new + {dictionary.create_new_label} )} {isLoadingTagOptions || isPendingTagOptions ? (
- Loading tags... + {dictionary.loading_tags_label}
) : data?.tags && data.tags.length === 0 ? (
- No tags found. + {dictionary.no_tags_found_label}
) : ( data?.tags && @@ -181,7 +185,7 @@ export function TagInput({ onSelect={onApplyToAll} className="m-1 justify-center text-center text-sm font-normal" > - Apply to all + {dictionary.apply_to_all_label}
)} diff --git a/packages/i18n/dictionaries/en.json b/packages/i18n/dictionaries/en.json index 9f3f916..f123996 100644 --- a/packages/i18n/dictionaries/en.json +++ b/packages/i18n/dictionaries/en.json @@ -270,5 +270,68 @@ "batch_upload_list_no_results": "No results.", "batch_page_header_title": "Batch details", "batch_page_refresh_notice": "This page refreshes every 15s", - "batch_page_generate_metadata_title": "Batch" + "batch_page_generate_metadata_title": "Batch", + "video_page_edit_video_title": "Edit video", + "video_page_download_mp4": "Download MP4", + "video_page_download_mp3": "Download MP3", + "video_page_overview_tab": "Overview", + "video_page_webhooks_tab": "Webhooks", + "overview_edit_video_title": "Edit video", + "overview_update_video_details": "Update video details", + "video_form_toast_error": "Uh oh! Something went wrong.", + "video_form_toast_error_description": "An error occurred while trying to save the video.", + "video_form_title_label": "Title", + "video_form_title_synced": "(synced with Skylab)", + "video_form_tags_label": "Tags", + "video_form_description_label": "Description", + "video_form_description_synced": "(synced with Skylab)", + "video_form_external_status_id_label": "External Status/ID", + "video_form_external_status_waiting": "waiting", + "video_form_external_id_not_generated": "(not generated yet)", + "video_form_commit_reference_label": "Commit reference", + "video_form_commit_reference_synced": "(synced with Skylab)", + "video_form_save_button": "Save", + "video_form_saved_message": "Saved!", + "edit_video_form_error_valid_title": "Please provide a valid title.", + "edit_video_form_error_valid_github_url": "Please provide a valid Github URL.", + "edit_video_form_error_tag_required": "At least one tag is required.", + "video_description_placeholder": "Enter the video description here.", + "generate_with_ai_button": "Generate with AI", + "tags_label": "Tags", + "selected_label": "selected", + "tags_placeholder": "Search tags", + "create_new_label": "Create new", + "loading_tags_label": "Loading tags...", + "no_tags_found_label": "No tags found.", + "apply_to_all_label": "Apply to all", + "new_tag_form_error_required": "The tag name is required.", + "new_tag_form_error_format": "Use only letters and hyphens.", + "create_tag_error": "Uh oh! Something went wrong.", + "create_tag_error_description": "An error ocurred while trying to create the tag. Maybe you're trying to create a duplicated tag.", + "create_new_tag": "Create new tag", + "create_new_tag_description_1": "Remember to avoid creating tags unnecessarily and to keep a maximum of", + "create_new_tag_description_2": "3 tags per video", + "create_new_tag_description_3": "Use the", + "create_new_tag_description_4": "following examples", + "example_tag_product": "reference to the product", + "example_tag_technology": "reference to the main technology", + "example_tag_course": "reference to the course", + "new_tag_label": "New tag", + "new_tag_placeholder": "your-new-tag", + "cancel_button": "Cancel", + "create_button": "Create", + "webhook_table_head_webhook": "Webhook", + "webhook_table_head_status": "Status", + "webhook_table_head_executed_at": "Executed At", + "webhook_table_head_duration": "Duration", + "webhook_table_head_metadata": "Metadata", + "webhook_status_running": "Running", + "webhook_status_success": "Success", + "webhook_status_error": "Error", + "webhook_no_results": "No results", + "transcription_card_sync_video_clicks": "Sync video & clicks", + "transcription_card_button_save": "Save", + "transcription_skeleton_generating": "Transcription is being generated", + "transcription_skeleton_auto_refresh": "The page will automatically refresh...", + "metadata_tooltip_request_body": "Request body sent to the webhook" } diff --git a/packages/i18n/dictionaries/pt.json b/packages/i18n/dictionaries/pt.json index 7a3e3d8..fbc5e8e 100644 --- a/packages/i18n/dictionaries/pt.json +++ b/packages/i18n/dictionaries/pt.json @@ -264,5 +264,68 @@ "batch_upload_list_no_results": "Sem resultados.", "batch_page_header_title": "Detalhes do Lote", "batch_page_refresh_notice": "Esta página é atualizada a cada 15s", - "batch_page_generate_metadata_title": "Lote" + "batch_page_generate_metadata_title": "Lote", + "video_page_edit_video_title": "Editar vídeo", + "video_page_download_mp4": "Baixar MP4", + "video_page_download_mp3": "Baixar MP3", + "video_page_overview_tab": "Visão geral", + "video_page_webhooks_tab": "Webhooks", + "overview_edit_video_title": "Editar vídeo", + "overview_update_video_details": "Atualizar detalhes do vídeo", + "video_form_toast_error": "Oh não! Algo deu errado.", + "video_form_toast_error_description": "Ocorreu um erro ao tentar salvar o vídeo.", + "video_form_title_label": "Título", + "video_form_title_synced": "(sincronizado com Skylab)", + "video_form_tags_label": "Tags", + "video_form_description_label": "Descrição", + "video_form_description_synced": "(sincronizado com Skylab)", + "video_form_external_status_id_label": "Status/ID Externo", + "video_form_external_status_waiting": "esperando", + "video_form_external_id_not_generated": "(ainda não gerado)", + "video_form_commit_reference_label": "Referência do commit", + "video_form_commit_reference_synced": "(sincronizado com Skylab)", + "video_form_save_button": "Salvar", + "video_form_saved_message": "Salvo!", + "edit_video_form_error_valid_title": "Por favor, forneça um título válido.", + "edit_video_form_error_valid_github_url": "Por favor, forneça um URL válido do Github.", + "edit_video_form_error_tag_required": "Pelo menos uma tag é necessária.", + "video_description_placeholder": "Insira a descrição do vídeo aqui.", + "generate_with_ai_button": "Gerar com IA", + "tags_label": "Etiquetas", + "selected_label": "selecionado", + "tags_placeholder": "Pesquisar etiquetas", + "create_new_label": "Criar novo", + "loading_tags_label": "Carregando etiquetas...", + "no_tags_found_label": "Nenhuma etiqueta encontrada.", + "apply_to_all_label": "Aplicar a todos", + "new_tag_form_error_required": "O nome da etiqueta é obrigatório.", + "new_tag_form_error_format": "Use apenas letras e hífens.", + "create_tag_error": "Uh oh! Algo deu errado.", + "create_tag_error_description": "Ocorreu um erro ao tentar criar a etiqueta. Talvez você esteja tentando criar uma etiqueta duplicada.", + "create_new_tag": "Criar nova etiqueta", + "create_new_tag_description_1": "Lembre-se de evitar a criação de etiquetas desnecessariamente e de manter um máximo de", + "create_new_tag_description_2": "3 etiquetas por vídeo", + "create_new_tag_description_3": "Use os", + "create_new_tag_description_4": "seguintes exemplos", + "example_tag_product": "referência ao produto", + "example_tag_technology": "referência à principal tecnologia", + "example_tag_course": "referência ao curso", + "new_tag_label": "Nova etiqueta", + "new_tag_placeholder": "sua-nova-etiqueta", + "cancel_button": "Cancelar", + "create_button": "Criar", + "webhook_table_head_webhook": "Webhook", + "webhook_table_head_status": "Status", + "webhook_table_head_executed_at": "Executado Em", + "webhook_table_head_duration": "Duração", + "webhook_table_head_metadata": "Metadados", + "webhook_status_running": "Em execução", + "webhook_status_success": "Sucesso", + "webhook_status_error": "Erro", + "webhook_no_results": "Sem resultados", + "transcription_card_sync_video_clicks": "Sincronizar vídeo e cliques", + "transcription_card_button_save": "Salvar", + "transcription_skeleton_generating": "A transcrição está sendo gerada", + "transcription_skeleton_auto_refresh": "A página será atualizada automaticamente...", + "metadata_tooltip_request_body": "Corpo da solicitação enviado para o webhook" }