diff --git a/.changeset/id-batch-7.md b/.changeset/id-batch-7.md new file mode 100644 index 0000000000..a3cda267e8 --- /dev/null +++ b/.changeset/id-batch-7.md @@ -0,0 +1,5 @@ +--- +"@emdash-cms/admin": patch +--- + +Updates Indonesian (`id`) translations in the admin UI. diff --git a/packages/admin/src/locales/id/messages.po b/packages/admin/src/locales/id/messages.po index 38d0a37477..60b6c4c1f7 100644 --- a/packages/admin/src/locales/id/messages.po +++ b/packages/admin/src/locales/id/messages.po @@ -439,7 +439,7 @@ msgstr "{0} dihapus dari postingan ini." #. placeholder {0}: SYSTEM_FIELDS.length #. placeholder {1}: fields.length -#. placeholder {2}: import { Badge, Button, Checkbox, Input, InputArea, Label, Select, Switch } from "@cloudflare/kumo"; import { DndContext, closestCenter, type DragEndEvent, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core"; import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import type { MessageDescriptor } from "@lingui/core"; import { msg, plural } from "@lingui/core/macro"; import { Trans, useLingui } from "@lingui/react/macro"; import { Plus, DotsSixVertical, Pencil, Trash, Database, FileText } from "@phosphor-icons/react"; import { useQuery } from "@tanstack/react-query"; import { useNavigate } from "@tanstack/react-router"; import * as React from "react"; import { fetchCollections, fetchRelations } from "../lib/api"; import type { SchemaCollectionWithFields, SchemaField, CreateFieldInput, CreateCollectionInput, UpdateCollectionInput, } from "../lib/api"; import type { CreateRelationInput, RelationDef, UpdateRelationInput, } from "../lib/api/relations.js"; import { cn, slugifyIdentifier } from "../lib/utils"; import { ArrowPrev } from "./ArrowIcons.js"; import { ConfirmDialog } from "./ConfirmDialog"; import { getMutationError } from "./DialogError"; import { EditorHeader } from "./EditorHeader"; import { FieldEditor } from "./FieldEditor"; import { RelationImpact } from "./RelationImpact.js"; import { RelationsPanel } from "./RelationsPanel.js"; import { RouterLinkButton } from "./RouterLinkButton.js"; import { SaveButton } from "./SaveButton"; const MULTIPLE_PLACEHOLDERS_IN_SEGMENT = /\{\w+\}[^/]*\{\w+\}/; export interface ContentTypeEditorProps { collection?: SchemaCollectionWithFields; isNew?: boolean; isSaving?: boolean; onSave: (input: CreateCollectionInput | UpdateCollectionInput) => void; /** Resolves once the field is saved; rejects with the server's message. */ onAddField?: (input: CreateFieldInput) => Promise; /** Resolves once the field is saved; rejects with the server's message. */ onUpdateField?: (fieldSlug: string, input: CreateFieldInput) => Promise; /** `deleteRelation` also removes the relationship a reference field views, * its links, and the field on the other end. */ onDeleteField?: (fieldSlug: string, options?: { deleteRelation?: boolean }) => void; onReorderFields?: (fieldSlugs: string[]) => void; /** Resolves with the new relation; rejects with the server's message. */ onCreateRelation?: (input: CreateRelationInput) => Promise; /** Resolves once the relation is saved; rejects with the server's message. */ onUpdateRelation?: (id: string, input: UpdateRelationInput) => Promise; /** Also removes the relation's links and the reference fields bound to it. */ onDeleteRelation?: (id: string) => Promise; isDeletingRelation?: boolean; deleteRelationError?: unknown; } interface SupportOptionDef { value: string; label: MessageDescriptor; description: MessageDescriptor; } const MODERATION_OPTIONS: Record<"all" | "first_time" | "none", MessageDescriptor> = { all: msg`All comments require approval`, first_time: msg`First-time commenters only`, none: msg`No moderation (auto-approve all)`, }; const SUPPORT_OPTIONS: SupportOptionDef[] = [ { value: "drafts", label: msg`Drafts`, description: msg`Save content as draft before publishing`, }, { value: "revisions", label: msg`Revisions`, description: msg`Track content history`, }, { value: "preview", label: msg`Preview`, description: msg`Preview content before publishing`, }, { value: "search", label: msg`Search`, description: msg`Enable full-text search on this collection`, }, ]; /** * System fields that exist on every collection * These are created automatically and cannot be modified */ interface SystemFieldDef { slug: string; label: MessageDescriptor; type: string; description: MessageDescriptor; } const SYSTEM_FIELDS: SystemFieldDef[] = [ { slug: "id", label: msg`ID`, type: "text", description: msg`Unique identifier (ULID)`, }, { slug: "slug", label: msg`Slug`, type: "text", description: msg`URL-friendly identifier`, }, { slug: "status", label: msg`Status`, type: "text", description: msg`draft, published, or archived`, }, { slug: "created_at", label: msg`Created At`, type: "datetime", description: msg`When the entry was created`, }, { slug: "updated_at", label: msg`Updated At`, type: "datetime", description: msg`When the entry was last modified`, }, { slug: "published_at", label: msg`Published At`, type: "datetime", description: msg`When the entry was published`, }, ]; /** * Content Type editor for creating/editing collections */ export function ContentTypeEditor({ collection, isNew, isSaving, onSave, onAddField, onUpdateField, onDeleteField, onReorderFields, onCreateRelation, onUpdateRelation, onDeleteRelation, isDeletingRelation, deleteRelationError, }: ContentTypeEditorProps) { const { t } = useLingui(); const _navigate = useNavigate(); // Form state const [slug, setSlug] = React.useState(collection?.slug ?? ""); const [label, setLabel] = React.useState(collection?.label ?? ""); const [labelSingular, setLabelSingular] = React.useState(collection?.labelSingular ?? ""); const [description, setDescription] = React.useState(collection?.description ?? ""); const [urlPattern, setUrlPattern] = React.useState(collection?.urlPattern ?? ""); const [routable, setRoutable] = React.useState(collection?.routable ?? true); const [editLocking, setEditLocking] = React.useState(collection?.editLocking ?? true); const [icon, setIcon] = React.useState(collection?.icon ?? ""); const [group, setGroup] = React.useState(collection?.group ?? ""); const [hidden, setHidden] = React.useState(collection?.hidden ?? false); const [quickCreate, setQuickCreate] = React.useState(collection?.admin?.quickCreate ?? true); // SEO is managed via the separate `hasSeo` field; strip any legacy "seo" entry // so it isn't sent back on save (the API enum rejects it). const [supports, setSupports] = React.useState( (collection?.supports ?? ["drafts", "revisions"]).filter((s) => s !== "seo"), ); // SEO state const [hasSeo, setHasSeo] = React.useState(collection?.hasSeo ?? false); // Comment settings state const [commentsEnabled, setCommentsEnabled] = React.useState( collection?.commentsEnabled ?? false, ); const [commentsModeration, setCommentsModeration] = React.useState<"all" | "first_time" | "none">( collection?.commentsModeration ?? "first_time", ); const [commentsClosedAfterDays, setCommentsClosedAfterDays] = React.useState( collection?.commentsClosedAfterDays ?? 90, ); const [commentsAutoApproveUsers, setCommentsAutoApproveUsers] = React.useState( collection?.commentsAutoApproveUsers ?? true, ); // Field editor state const [fieldEditorOpen, setFieldEditorOpen] = React.useState(false); const [editingField, setEditingField] = React.useState(); const [fieldSaving, setFieldSaving] = React.useState(false); const [fieldSaveError, setFieldSaveError] = React.useState(null); const [deleteFieldTarget, setDeleteFieldTarget] = React.useState(null); // Checked by default: deleting a reference field almost always means the // relationship it views is finished too. const [deleteFieldRelation, setDeleteFieldRelation] = React.useState(true); const urlPatternChanged = urlPattern !== (collection?.urlPattern ?? ""); const urlPatternSharesSegment = urlPatternChanged && MULTIPLE_PLACEHOLDERS_IN_SEGMENT.test(urlPattern); const urlPatternValid = !urlPattern || (urlPattern.includes("{slug}") && !urlPatternSharesSegment); const slugError = isNew && !slug && label.trim().length > 0 ? t`A slug cannot be generated from this label. Type one manually using lowercase letters, numbers, and underscores.` : undefined; // Track whether form has unsaved changes const hasChanges = React.useMemo(() => { if (isNew) return slug && label; if (!collection) return false; return ( label !== collection.label || labelSingular !== (collection.labelSingular ?? "") || description !== (collection.description ?? "") || urlPattern !== (collection.urlPattern ?? "") || routable !== (collection.routable ?? true) || editLocking !== (collection.editLocking ?? true) || icon !== (collection.icon ?? "") || group !== (collection.group ?? "") || hidden !== (collection.hidden ?? false) || quickCreate !== (collection.admin?.quickCreate ?? true) || JSON.stringify([...supports].toSorted()) !== JSON.stringify(collection.supports.filter((s) => s !== "seo").toSorted()) || hasSeo !== collection.hasSeo || commentsEnabled !== collection.commentsEnabled || commentsModeration !== collection.commentsModeration || commentsClosedAfterDays !== collection.commentsClosedAfterDays || commentsAutoApproveUsers !== collection.commentsAutoApproveUsers ); }, [ isNew, collection, slug, label, labelSingular, description, urlPattern, routable, editLocking, icon, group, hidden, quickCreate, supports, hasSeo, commentsEnabled, commentsModeration, commentsClosedAfterDays, commentsAutoApproveUsers, ]); // Auto-generate slug from plural label const handleLabelChange = (value: string) => { setLabel(value); if (isNew) { setSlug(slugifyIdentifier(value)); } }; // Auto-generate plural label (and slug) from singular label const handleSingularLabelChange = (value: string) => { setLabelSingular(value); if (isNew) { const pluralLabel = value ? `${value}s` : ""; handleLabelChange(pluralLabel); } }; const handleSupportToggle = (value: string) => { setSupports((prev) => prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value], ); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (isNew) { onSave({ slug, label, labelSingular: labelSingular || undefined, description: description || undefined, urlPattern: urlPattern || undefined, routable, editLocking, icon: icon.trim() || undefined, group: group.trim() || undefined, hidden, admin: quickCreate ? undefined : { quickCreate: false }, supports, hasSeo, }); } else { onSave({ label, labelSingular: labelSingular || undefined, description: description || undefined, urlPattern: urlPattern || undefined, routable, editLocking, icon: icon.trim(), group: group.trim() || null, hidden, admin: quickCreate !== (collection?.admin?.quickCreate ?? true) ? { ...collection?.admin, quickCreate: quickCreate ? undefined : false } : undefined, supports, hasSeo, commentsEnabled, commentsModeration, commentsClosedAfterDays, commentsAutoApproveUsers, }); } }; const handleFieldSave = async (input: CreateFieldInput) => { setFieldSaving(true); setFieldSaveError(null); try { if (editingField) { await onUpdateField?.(editingField.slug, input); } else { await onAddField?.(input); } setFieldEditorOpen(false); setEditingField(undefined); } catch (err) { setFieldSaveError(getMutationError(err)); } finally { setFieldSaving(false); } }; const requestDeleteField = (field: SchemaField) => { setDeleteFieldRelation(true); setDeleteFieldTarget(field); }; const handleEditField = (field: SchemaField) => { if (field.unsupportedType) return; setEditingField(field); setFieldSaveError(null); setFieldEditorOpen(true); }; const handleAddField = () => { setEditingField(undefined); setFieldSaveError(null); setFieldEditorOpen(true); }; const isFromCode = collection?.source === "code"; const fields = collection?.fields ?? []; const { data: relations = [], isLoading: relationsLoading } = useQuery({ queryKey: ["relations"], queryFn: () => fetchRelations(), }); const { data: allCollections = [] } = useQuery({ queryKey: ["schema", "collections"], queryFn: fetchCollections, }); const targetRelationSlug = deleteFieldTarget?.validation?.relation; const targetRelation = relations.find((rel) => rel.slug === targetRelationSlug); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; const oldIndex = fields.findIndex((f) => f.id === active.id); const newIndex = fields.findIndex((f) => f.id === over.id); if (oldIndex === -1 || newIndex === -1) return; const reordered = arrayMove(fields, oldIndex, newIndex); onReorderFields?.(reordered.map((f) => f.slug)); }; return (
} /> } actions={ !isFromCode && !isNew ? ( ) : null } >

{isNew ? t`New Content Type` : collection?.label}

{!isNew && (

{collection?.slug} {isFromCode && {t`Defined in code`}}

)}
{isFromCode && (

{t`This collection is defined in code. Some settings cannot be changed here. Edit your live.config.ts file to modify the schema.`}

)}
{/* Settings form */}

{t`Settings`}

handleSingularLabelChange(e.target.value)} placeholder={t`Post`} disabled={isFromCode} /> handleLabelChange(e.target.value)} placeholder={t`Posts`} disabled={isFromCode} /> {isNew && (
setSlug(e.target.value)} placeholder="posts" disabled={!isNew} error={slugError} />

{t`Used in URLs and API endpoints`}

)} setDescription(e.target.value)} placeholder={t`A brief description of this content type`} rows={3} disabled={isFromCode} /> {t`Routable`}

{t`Require a slug before content can be published`}

} /> {t`Edit locking`}

{t`Hold an entry while someone is editing it, and refuse other writers`}

} />
setUrlPattern(e.target.value)} placeholder={`/${slug === "pages" ? "" : `${slug}/`}{slug}`} disabled={isFromCode} /> {urlPattern && !urlPattern.includes("{slug}") && (

{t`Pattern must include a ${"{slug}"} placeholder`}

)} {urlPatternSharesSegment && (

{t`Each path segment can contain at most one placeholder, e.g. ${"/{year}/{slug}"}`}

)}

{t`Pattern for generating URLs, e.g. /blog/${"{slug}"}. Tokens: ${"{slug}"}, ${"{id}"}, and date tokens ${"{year}"}/${"{month}"}/${"{day}"} (also ${"{hour}"}/${"{minute}"}/${"{second}"}) from the publish date — e.g. ${"/{year}/{month}/{day}/{slug}.html"} for WordPress-style permalinks.`}

setGroup(e.target.value)} placeholder={t`Calendar`} disabled={isFromCode} />

{t`Content types with the same group share a collapsible folder in the sidebar`}

setIcon(e.target.value)} disabled={isFromCode} />

{t`Phosphor icon name, e.g. calendar-blank or trophy`}

{t`Hide from navigation`}

{t`Removes the sidebar entry, command palette link, and dashboard quick action; entries stay reachable by URL, API, and plugins`}

} /> {t`Quick action on the dashboard`}

{t`Shows a button for creating a new entry`}

} />
{SUPPORT_OPTIONS.map((option) => (
handleSupportToggle(option.value)} disabled={isFromCode} label={
{t(option.label)}

{t(option.description)}

} />
))}
{/* SEO toggle */}
setHasSeo(checked)} disabled={isFromCode} label={
{t`SEO`}

{t`Add SEO metadata fields (title, description, image) and include in sitemap`}

} />
{/* Comments settings — only for existing collections */} {!isNew && (

{t`Comments`}

setCommentsEnabled(checked)} disabled={isFromCode} label={
{t`Enable comments`}

{t`Allow visitors to leave comments on this collection's content`}

} /> {commentsEnabled && ( <> { const parsed = Number.parseInt(e.target.value, 10); setCommentsClosedAfterDays(Number.isNaN(parsed) ? 0 : Math.max(0, parsed)); }} disabled={isFromCode} />

{t`Set to 0 to never close comments automatically.`}

setCommentsAutoApproveUsers(checked)} disabled={isFromCode} label={
{t`Auto-approve authenticated users`}

{t`Comments from logged-in CMS users are approved automatically`}

} /> )}
)} {!isFromCode && (isNew ? ( ) : ( ))} {/* Fields and relations - only shown for existing collections */} {!isNew && (

{t`Fields`}

{SYSTEM_FIELDS.length} system + {fields.length} custom{" "} {plural(fields.length, { one: "field", other: "fields" })}

{!isFromCode && ( )}
{/* System fields - always shown */}
{t`System Fields`}
{SYSTEM_FIELDS.map((field) => ( ))}
{/* Custom fields */} {fields.length === 0 ? (

{t`No custom fields yet`}

{t`Add fields to define the structure of your content`}

{!isFromCode && ( )}
) : ( <>
{t`Custom Fields`}
f.id)} strategy={verticalListSortingStrategy} >
{fields.map((field) => ( handleEditField(field)} onDelete={() => requestDeleteField(field)} /> ))}
)}
{collection && ( )}
)} {/* Field editor dialog */} { setFieldEditorOpen(nextOpen); if (!nextOpen) setFieldSaveError(null); }} field={editingField} onSave={handleFieldSave} isSaving={fieldSaving} saveError={fieldSaveError} collectionSlug={collection?.slug} onCreateRelation={onCreateRelation} /> setDeleteFieldTarget(null)} title={t`Delete Field?`} description={ deleteFieldTarget ? t`Are you sure you want to delete the "${deleteFieldTarget.label}" field?` : "" } confirmLabel={t`Delete`} pendingLabel={t`Deleting...`} isPending={false} error={null} onConfirm={() => { if (deleteFieldTarget) { onDeleteField?.( deleteFieldTarget.slug, targetRelation ? { deleteRelation: deleteFieldRelation } : undefined, ); setDeleteFieldTarget(null); } }} > {targetRelation && (
setDeleteFieldRelation(checked === true)} label={t`Also delete the relationship this field uses`} /> {deleteFieldRelation && collection && (

{t`This also removes:`}

)}
)}
); } interface FieldRowProps { field: SchemaField; isFromCode?: boolean; onEdit: () => void; onDelete: () => void; } function FieldRow({ field, isFromCode, onEdit, onDelete }: FieldRowProps) { const { t } = useLingui(); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: field.id, disabled: isFromCode, }); const style = { transform: CSS.Transform.toString(transform), transition }; return (
{!isFromCode && ( )}
{field.label} {field.slug}
{field.unsupportedType?.type ?? field.type} {field.unsupportedType && {t`Unsupported`}} {field.required && {t`Required`}} {field.unique && {t`Unique`}} {field.searchable && {t`Searchable`}}
{!isFromCode && (
)}
); } interface SystemFieldInfo { slug: string; label: MessageDescriptor; type: string; description: MessageDescriptor; } function SystemFieldRow({ field }: { field: SystemFieldInfo }) { const { t } = useLingui(); return (
{/* Spacer for alignment with draggable fields */}
{t(field.label)} {field.slug} {t`System`}

{t(field.description)}

); } +#. placeholder {2}: import { Badge, Button, Checkbox, Input, InputArea, Label, Select, Switch } from "@cloudflare/kumo"; import { DndContext, closestCenter, type DragEndEvent, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core"; import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import type { MessageDescriptor } from "@lingui/core"; import { msg, plural } from "@lingui/core/macro"; import { Trans, useLingui } from "@lingui/react/macro"; import { Plus, DotsSixVertical, Pencil, Trash, Database, FileText } from "@phosphor-icons/react"; import { useQuery } from "@tanstack/react-query"; import { useNavigate } from "@tanstack/react-router"; import * as React from "react"; import { fetchCollections, fetchRelations } from "../lib/api"; import type { SchemaCollectionWithFields, SchemaField, CreateFieldInput, CreateCollectionInput, UpdateCollectionInput, } from "../lib/api"; import type { CreateRelationInput, RelationDef, UpdateRelationInput, } from "../lib/api/relations.js"; import { cn, slugifyIdentifier } from "../lib/utils"; import { ArrowPrev } from "./ArrowIcons.js"; import { ConfirmDialog } from "./ConfirmDialog"; import { getMutationError } from "./DialogError"; import { EditorHeader } from "./EditorHeader"; import { FieldEditor } from "./FieldEditor"; import { RelationImpact } from "./RelationImpact.js"; import { RelationsPanel } from "./RelationsPanel.js"; import { RouterLinkButton } from "./RouterLinkButton.js"; import { SaveButton } from "./SaveButton"; const MULTIPLE_PLACEHOLDERS_IN_SEGMENT = /\{\w+\}[^/]*\{\w+\}/; export interface ContentTypeEditorProps { collection?: SchemaCollectionWithFields; isNew?: boolean; isSaving?: boolean; onSave: (input: CreateCollectionInput | UpdateCollectionInput) => void; /** Resolves once the field is saved; rejects with the server's message. */ onAddField?: (input: CreateFieldInput) => Promise; /** Resolves once the field is saved; rejects with the server's message. */ onUpdateField?: (fieldSlug: string, input: CreateFieldInput) => Promise; /** `deleteRelation` also removes the relationship a reference field views, * its links, and the field on the other end. */ onDeleteField?: (fieldSlug: string, options?: { deleteRelation?: boolean }) => void; onReorderFields?: (fieldSlugs: string[]) => void; /** Resolves with the new relation; rejects with the server's message. */ onCreateRelation?: (input: CreateRelationInput) => Promise; /** Resolves once the relation is saved; rejects with the server's message. */ onUpdateRelation?: (id: string, input: UpdateRelationInput) => Promise; /** Also removes the relation's links and the reference fields bound to it. */ onDeleteRelation?: (id: string) => Promise; isDeletingRelation?: boolean; deleteRelationError?: unknown; } interface SupportOptionDef { value: string; label: MessageDescriptor; description: MessageDescriptor; } const MODERATION_OPTIONS: Record<"all" | "first_time" | "none", MessageDescriptor> = { all: msg`All comments require approval`, first_time: msg`First-time commenters only`, none: msg`No moderation (auto-approve all)`, }; const SUPPORT_OPTIONS: SupportOptionDef[] = [ { value: "drafts", label: msg`Drafts`, description: msg`Save content as draft before publishing`, }, { value: "revisions", label: msg`Revisions`, description: msg`Track content history`, }, { value: "preview", label: msg`Preview`, description: msg`Preview content before publishing`, }, { value: "search", label: msg`Search`, description: msg`Enable full-text search on this collection`, }, ]; /** * System fields that exist on every collection * These are created automatically and cannot be modified */ interface SystemFieldDef { slug: string; label: MessageDescriptor; type: string; description: MessageDescriptor; } const SYSTEM_FIELDS: SystemFieldDef[] = [ { slug: "id", label: msg`ID`, type: "text", description: msg`Unique identifier (ULID)`, }, { slug: "slug", label: msg`Slug`, type: "text", description: msg`URL-friendly identifier`, }, { slug: "status", label: msg`Status`, type: "text", description: msg`draft, published, or archived`, }, { slug: "created_at", label: msg`Created At`, type: "datetime", description: msg`When the entry was created`, }, { slug: "updated_at", label: msg`Updated At`, type: "datetime", description: msg`When the entry was last modified`, }, { slug: "published_at", label: msg`Published At`, type: "datetime", description: msg`When the entry was published`, }, ]; /** * Content Type editor for creating/editing collections */ export function ContentTypeEditor({ collection, isNew, isSaving, onSave, onAddField, onUpdateField, onDeleteField, onReorderFields, onCreateRelation, onUpdateRelation, onDeleteRelation, isDeletingRelation, deleteRelationError, }: ContentTypeEditorProps) { const { t } = useLingui(); const _navigate = useNavigate(); // Form state const [slug, setSlug] = React.useState(collection?.slug ?? ""); const [label, setLabel] = React.useState(collection?.label ?? ""); const [labelSingular, setLabelSingular] = React.useState(collection?.labelSingular ?? ""); const [description, setDescription] = React.useState(collection?.description ?? ""); const [urlPattern, setUrlPattern] = React.useState(collection?.urlPattern ?? ""); const [routable, setRoutable] = React.useState(collection?.routable ?? true); const [editLocking, setEditLocking] = React.useState(collection?.editLocking ?? true); const [icon, setIcon] = React.useState(collection?.icon ?? ""); const [group, setGroup] = React.useState(collection?.group ?? ""); const [hidden, setHidden] = React.useState(collection?.hidden ?? false); const [quickCreate, setQuickCreate] = React.useState(collection?.admin?.quickCreate ?? true); // SEO is managed via the separate `hasSeo` field; strip any legacy "seo" entry // so it isn't sent back on save (the API enum rejects it). const [supports, setSupports] = React.useState( (collection?.supports ?? ["drafts", "revisions"]).filter((s) => s !== "seo"), ); // SEO state const [hasSeo, setHasSeo] = React.useState(collection?.hasSeo ?? false); // Comment settings state const [commentsEnabled, setCommentsEnabled] = React.useState( collection?.commentsEnabled ?? false, ); const [commentsModeration, setCommentsModeration] = React.useState<"all" | "first_time" | "none">( collection?.commentsModeration ?? "first_time", ); const [commentsClosedAfterDays, setCommentsClosedAfterDays] = React.useState( collection?.commentsClosedAfterDays ?? 90, ); const [commentsAutoApproveUsers, setCommentsAutoApproveUsers] = React.useState( collection?.commentsAutoApproveUsers ?? true, ); // Field editor state const [fieldEditorOpen, setFieldEditorOpen] = React.useState(false); const [editingField, setEditingField] = React.useState(); const [fieldSaving, setFieldSaving] = React.useState(false); const [fieldSaveError, setFieldSaveError] = React.useState(null); const [deleteFieldTarget, setDeleteFieldTarget] = React.useState(null); // Checked by default: deleting a reference field almost always means the // relationship it views is finished too. const [deleteFieldRelation, setDeleteFieldRelation] = React.useState(true); const urlPatternChanged = urlPattern !== (collection?.urlPattern ?? ""); const urlPatternSharesSegment = urlPatternChanged && MULTIPLE_PLACEHOLDERS_IN_SEGMENT.test(urlPattern); const urlPatternValid = !urlPattern || (urlPattern.includes("{slug}") && !urlPatternSharesSegment); const slugError = isNew && !slug && label.trim().length > 0 ? t`A slug cannot be generated from this label. Type one manually using lowercase letters, numbers, and underscores.` : undefined; // Track whether form has unsaved changes const hasChanges = React.useMemo(() => { if (isNew) return slug && label; if (!collection) return false; return ( label !== collection.label || labelSingular !== (collection.labelSingular ?? "") || description !== (collection.description ?? "") || urlPattern !== (collection.urlPattern ?? "") || routable !== (collection.routable ?? true) || editLocking !== (collection.editLocking ?? true) || icon !== (collection.icon ?? "") || group !== (collection.group ?? "") || hidden !== (collection.hidden ?? false) || quickCreate !== (collection.admin?.quickCreate ?? true) || JSON.stringify([...supports].toSorted()) !== JSON.stringify(collection.supports.filter((s) => s !== "seo").toSorted()) || hasSeo !== collection.hasSeo || commentsEnabled !== collection.commentsEnabled || commentsModeration !== collection.commentsModeration || commentsClosedAfterDays !== collection.commentsClosedAfterDays || commentsAutoApproveUsers !== collection.commentsAutoApproveUsers ); }, [ isNew, collection, slug, label, labelSingular, description, urlPattern, routable, editLocking, icon, group, hidden, quickCreate, supports, hasSeo, commentsEnabled, commentsModeration, commentsClosedAfterDays, commentsAutoApproveUsers, ]); // Auto-generate slug from plural label const handleLabelChange = (value: string) => { setLabel(value); if (isNew) { setSlug(slugifyIdentifier(value)); } }; // Auto-generate plural label (and slug) from singular label const handleSingularLabelChange = (value: string) => { setLabelSingular(value); if (isNew) { const pluralLabel = value ? `${value}s` : ""; handleLabelChange(pluralLabel); } }; const handleSupportToggle = (value: string) => { setSupports((prev) => prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value], ); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (isNew) { onSave({ slug, label, labelSingular: labelSingular || undefined, description: description || undefined, urlPattern: urlPattern || undefined, routable, editLocking, icon: icon.trim() || undefined, group: group.trim() || undefined, hidden, admin: quickCreate ? undefined : { quickCreate: false }, supports, hasSeo, }); } else { onSave({ label, labelSingular: labelSingular || undefined, description: description || undefined, urlPattern: urlPattern || undefined, routable, editLocking, icon: icon.trim(), group: group.trim() || null, hidden, admin: quickCreate !== (collection?.admin?.quickCreate ?? true) ? { ...collection?.admin, quickCreate: quickCreate ? undefined : false } : undefined, supports, hasSeo, commentsEnabled, commentsModeration, commentsClosedAfterDays, commentsAutoApproveUsers, }); } }; const handleFieldSave = async (input: CreateFieldInput) => { setFieldSaving(true); setFieldSaveError(null); try { if (editingField) { await onUpdateField?.(editingField.slug, input); } else { await onAddField?.(input); } setFieldEditorOpen(false); setEditingField(undefined); } catch (err) { setFieldSaveError(getMutationError(err)); } finally { setFieldSaving(false); } }; const requestDeleteField = (field: SchemaField) => { setDeleteFieldRelation(true); setDeleteFieldTarget(field); }; const handleEditField = (field: SchemaField) => { if (field.unsupportedType) return; setEditingField(field); setFieldSaveError(null); setFieldEditorOpen(true); }; const handleAddField = () => { setEditingField(undefined); setFieldSaveError(null); setFieldEditorOpen(true); }; const isFromCode = collection?.source === "code"; const fields = collection?.fields ?? []; const { data: relations = [], isLoading: relationsLoading } = useQuery({ queryKey: ["relations"], queryFn: () => fetchRelations(), }); const { data: allCollections = [] } = useQuery({ queryKey: ["schema", "collections"], queryFn: fetchCollections, }); const targetRelationSlug = deleteFieldTarget?.validation?.relation; const targetRelation = relations.find((rel) => rel.slug === targetRelationSlug); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; const oldIndex = fields.findIndex((f) => f.id === active.id); const newIndex = fields.findIndex((f) => f.id === over.id); if (oldIndex === -1 || newIndex === -1) return; const reordered = arrayMove(fields, oldIndex, newIndex); onReorderFields?.(reordered.map((f) => f.slug)); }; return (
} /> } actions={ !isFromCode && !isNew ? ( ) : null } >

{isNew ? t`New Content Type` : collection?.label}

{!isNew && (

{collection?.slug} {isFromCode && {t`Defined in code`}}

)}
{isFromCode && (

{t`This collection is defined in code. Some settings cannot be changed here. Edit your live.config.ts file to modify the schema.`}

)}
{/* Settings form */}

{t`Settings`}

handleSingularLabelChange(e.target.value)} placeholder={t`Post`} disabled={isFromCode} /> handleLabelChange(e.target.value)} placeholder={t`Posts`} disabled={isFromCode} /> {isNew && (
setSlug(e.target.value)} placeholder="posts" disabled={!isNew} error={slugError} />

{t`Used in URLs and API endpoints`}

)} setDescription(e.target.value)} placeholder={t`A brief description of this content type`} rows={3} disabled={isFromCode} /> {t`Routable`}

{t`Require a slug before content can be published`}

} /> {t`Edit locking`}

{t`Hold an entry while someone is editing it, and refuse other writers`}

} />
setUrlPattern(e.target.value)} placeholder={`/${slug === "pages" ? "" : `${slug}/`}{slug}`} disabled={isFromCode} /> {urlPattern && !urlPattern.includes("{slug}") && (

{t`Pattern must include a ${"{slug}"} placeholder`}

)} {urlPatternSharesSegment && (

{t`Each path segment can contain at most one placeholder, e.g. ${"/{year}/{slug}"}`}

)}

{t`Pattern for generating URLs, e.g. /blog/${"{slug}"}. Tokens: ${"{slug}"}, ${"{id}"}, and date tokens ${"{year}"}/${"{month}"}/${"{day}"} (also ${"{hour}"}/${"{minute}"}/${"{second}"}) from the publish date — e.g. ${"/{year}/{month}/{day}/{slug}.html"} for WordPress-style permalinks.`}

setGroup(e.target.value)} placeholder={t`Calendar`} disabled={isFromCode} />

{t`Content types with the same group share a collapsible folder in the sidebar`}

setIcon(e.target.value)} disabled={isFromCode} />

{t`Phosphor icon name, e.g. calendar-blank or trophy`}

{t`Hide from navigation`}

{t`Removes the sidebar entry, command palette link, and dashboard quick action; entries stay reachable by URL, API, and plugins`}

} /> {t`Quick action on the dashboard`}

{t`Shows a button for creating a new entry`}

} />
{SUPPORT_OPTIONS.map((option) => (
handleSupportToggle(option.value)} disabled={isFromCode} label={
{t(option.label)}

{t(option.description)}

} />
))}
{/* SEO toggle */}
setHasSeo(checked)} disabled={isFromCode} label={
{t`SEO`}

{t`Add SEO metadata fields (title, description, image) and include in sitemap`}

} />
{/* Comments settings — only for existing collections */} {!isNew && (

{t`Comments`}

setCommentsEnabled(checked)} disabled={isFromCode} label={
{t`Enable comments`}

{t`Allow visitors to leave comments on this collection's content`}

} /> {commentsEnabled && ( <> { const parsed = Number.parseInt(e.target.value, 10); setCommentsClosedAfterDays(Number.isNaN(parsed) ? 0 : Math.max(0, parsed)); }} disabled={isFromCode} />

{t`Set to 0 to never close comments automatically.`}

setCommentsAutoApproveUsers(checked)} disabled={isFromCode} label={
{t`Auto-approve authenticated users`}

{t`Comments from logged-in CMS users are approved automatically`}

} /> )}
)} {!isFromCode && (isNew ? ( ) : ( ))} {/* Fields and relations - only shown for existing collections */} {!isNew && (

{t`Fields`}

{SYSTEM_FIELDS.length} system + {fields.length} custom{" "} {plural(fields.length, { one: "field", other: "fields" })}

{!isFromCode && ( )}
{/* System fields - always shown */}
{t`System Fields`}
{SYSTEM_FIELDS.map((field) => ( ))}
{/* Custom fields */} {fields.length === 0 ? (

{t`No custom fields yet`}

{t`Add fields to define the structure of your content`}

{!isFromCode && ( )}
) : ( <>
{t`Custom Fields`}
f.id)} strategy={verticalListSortingStrategy} >
{fields.map((field) => ( handleEditField(field)} onDelete={() => requestDeleteField(field)} /> ))}
)}
{collection && ( )}
)} {/* Field editor dialog */} { setFieldEditorOpen(nextOpen); if (!nextOpen) setFieldSaveError(null); }} field={editingField} onSave={handleFieldSave} isSaving={fieldSaving} saveError={fieldSaveError} collectionSlug={collection?.slug} onCreateRelation={onCreateRelation} /> setDeleteFieldTarget(null)} title={t`Delete Field?`} description={ deleteFieldTarget ? t`Are you sure you want to delete the "${deleteFieldTarget.label}" field?` : "" } confirmLabel={t`Delete`} pendingLabel={t`Deleting...`} isPending={false} error={null} onConfirm={() => { if (deleteFieldTarget) { onDeleteField?.( deleteFieldTarget.slug, targetRelation ? { deleteRelation: deleteFieldRelation } : undefined, ); setDeleteFieldTarget(null); } }} > {targetRelation && (
setDeleteFieldRelation(checked === true)} label={t`Also delete the relationship this field uses`} /> {deleteFieldRelation && collection && (

{t`This also removes:`}

)}
)}
); } interface FieldRowProps { field: SchemaField; isFromCode?: boolean; onEdit: () => void; onDelete: () => void; } function FieldRow({ field, isFromCode, onEdit, onDelete }: FieldRowProps) { const { t } = useLingui(); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: field.id, disabled: isFromCode, }); const style = { transform: CSS.Transform.toString(transform), transition }; return (
{!isFromCode && ( )}
{field.label} {field.slug}
{field.unsupportedType?.type ?? field.type} {field.unsupportedType && {t`Unsupported`}} {field.required && {t`Required`}} {field.unique && {t`Unique`}} {field.searchable && {t`Searchable`}}
{!isFromCode && (
)}
); } interface SystemFieldInfo { slug: string; label: MessageDescriptor; type: string; description: MessageDescriptor; } function SystemFieldRow({ field }: { field: SystemFieldInfo }) { const { t } = useLingui(); return (
{/* Spacer for alignment with draggable fields */}
{t(field.label)} {field.slug} {t`System`}

{t(field.description)}

); } #: packages/admin/src/components/ContentTypeEditor.tsx:754 msgid "{0} system + {1} custom {2}" msgstr "{0} sistem + {1} kustom {2}" @@ -1067,7 +1067,11 @@ msgstr "Slug tidak dapat dihasilkan dari label ini. Ketik secara manual mengguna #: packages/admin/src/components/FieldEditor.tsx:1242 msgid "A slug cannot be generated from this label. Type one manually." +<<<<<<< HEAD +msgstr "Slug tidak dapat dihasilkan dari label ini. Ketik yang baru secara manual." +======= msgstr "Slug tidak dapat dihasilkan dari label ini. Ketik secara manual." +>>>>>>> 1826f20 (i18n(id): add changeset and tighten slug generation message) #: packages/admin/src/components/TaxonomySidebar.tsx:762 msgid "A source and target locale are required" @@ -1344,7 +1348,6 @@ msgstr "Tambahkan tag meta noindex" msgid "Add Passkey" msgstr "Tambahkan Kunci Sandi" -#: packages/admin/src/components/ContentEditor.tsx:2840 #: packages/admin/src/components/ContentEditor.tsx:2840 msgid "Add reference" msgstr "Tambahkan referensi" @@ -1690,24 +1693,15 @@ msgstr "Kunci digital terenkripsi yang Anda buka menggunakan sidik jari, wajah, #: packages/admin/src/components/TaxonomySidebar.tsx:687 #: packages/admin/src/components/TaxonomySidebar.tsx:783 #: packages/admin/src/components/ThemeMarketplaceBrowse.tsx:114 -#: packages/admin/src/router.tsx:581 -#: packages/admin/src/router.tsx:596 -#: packages/admin/src/router.tsx:610 -#: packages/admin/src/router.tsx:624 -#: packages/admin/src/router.tsx:846 -#: packages/admin/src/router.tsx:1253 -#: packages/admin/src/router.tsx:1356 -#: packages/admin/src/router.tsx:1390 -#: packages/admin/src/router.tsx:1414 -#: packages/admin/src/router.tsx:1439 -#: packages/admin/src/router.tsx:1495 -#: packages/admin/src/router.tsx:1514 -#: packages/admin/src/router.tsx:1547 -#: packages/admin/src/router.tsx:1568 -#: packages/admin/src/router.tsx:2317 -#: packages/admin/src/router.tsx:2333 -#: packages/admin/src/router.tsx:2358 -#: packages/admin/src/router.tsx:2960 +#: packages/admin/src/router.tsx:581 packages/admin/src/router.tsx:596 +#: packages/admin/src/router.tsx:610 packages/admin/src/router.tsx:624 +#: packages/admin/src/router.tsx:846 packages/admin/src/router.tsx:1253 +#: packages/admin/src/router.tsx:1356 packages/admin/src/router.tsx:1390 +#: packages/admin/src/router.tsx:1414 packages/admin/src/router.tsx:1439 +#: packages/admin/src/router.tsx:1495 packages/admin/src/router.tsx:1514 +#: packages/admin/src/router.tsx:1547 packages/admin/src/router.tsx:1568 +#: packages/admin/src/router.tsx:2317 packages/admin/src/router.tsx:2333 +#: packages/admin/src/router.tsx:2358 packages/admin/src/router.tsx:2960 #: packages/admin/src/routes/byline-schema.tsx:103 #: packages/admin/src/routes/byline-schema.tsx:123 #: packages/admin/src/routes/byline-schema.tsx:139 @@ -5286,7 +5280,7 @@ msgstr "Gagal menghentikan impor" #: packages/admin/src/lib/api/schema.ts:319 msgid "Failed to activate block type version" -msgstr "" +msgstr "Gagal mengaktifkan versi tipe blok" #: packages/admin/src/components/settings/AllowedDomainsSettings.tsx:84 msgid "Failed to add domain" @@ -5298,11 +5292,11 @@ msgstr "Gagal menambahkan passkey" #: packages/admin/src/lib/api/taxonomies.ts:120 msgid "Failed to add term to entries" -msgstr "" +msgstr "Gagal menambahkan istilah ke entri" #: packages/admin/src/lib/api/import.ts:378 msgid "Failed to analyze file" -msgstr "" +msgstr "Gagal menganalisis berkas" #: packages/admin/src/lib/api/transfer.ts:442 msgid "Failed to analyze import" @@ -5327,7 +5321,7 @@ msgstr "Gagal mengubah domain" #: packages/admin/src/lib/api/marketplace.ts:401 msgid "Failed to check for updates" -msgstr "" +msgstr "Gagal memeriksa pembaruan" #: packages/admin/src/components/SandboxedPluginPage.tsx:78 msgid "Failed to communicate with plugin" @@ -5335,7 +5329,7 @@ msgstr "Gagal berkomunikasi dengan pengaya" #: packages/admin/src/lib/api/content.ts:594 msgid "Failed to compare revisions" -msgstr "" +msgstr "Gagal membandingkan revisi" #: packages/admin/src/lib/api/media.ts:379 msgid "Failed to confirm upload" @@ -5365,11 +5359,11 @@ msgstr "Gagal membuat admin" #: packages/admin/src/lib/api/users.ts:313 msgid "Failed to create allowed domain" -msgstr "" +msgstr "Gagal membuat domain yang diizinkan" #: packages/admin/src/lib/api/api-tokens.ts:91 msgid "Failed to create API token" -msgstr "" +msgstr "Gagal membuat token API" #: packages/admin/src/components/settings/BackupSettings.tsx:123 #: packages/admin/src/lib/api/backups.ts:51 @@ -5378,11 +5372,11 @@ msgstr "Gagal membuat cadangan" #: packages/admin/src/lib/api/schema.ts:283 msgid "Failed to create block type" -msgstr "" +msgstr "Gagal membuat tipe blok" #: packages/admin/src/lib/api/bylines.ts:127 msgid "Failed to create byline" -msgstr "" +msgstr "Gagal membuat byline" #: packages/admin/src/lib/api/byline-fields.ts:120 msgid "Failed to create byline field" @@ -5390,15 +5384,15 @@ msgstr "Gagal membuat kolom byline" #: packages/admin/src/lib/api/bylines.ts:178 msgid "Failed to create byline translation" -msgstr "" +msgstr "Gagal membuat terjemahan byline" #: packages/admin/src/lib/api/schema.ts:371 msgid "Failed to create collection" -msgstr "" +msgstr "Gagal membuat koleksi" #: packages/admin/src/lib/api/content.ts:306 msgid "Failed to create content" -msgstr "" +msgstr "Gagal membuat konten" #: packages/admin/src/lib/api/schema.ts:432 #: packages/admin/src/routes/byline-schema.tsx:102 @@ -5411,23 +5405,23 @@ msgstr "Gagal membuat map media" #: packages/admin/src/lib/api/menus.ts:137 msgid "Failed to create menu" -msgstr "" +msgstr "Gagal membuat menu" #: packages/admin/src/lib/api/menus.ts:182 msgid "Failed to create menu item" -msgstr "" +msgstr "Gagal membuat item menu" #: packages/admin/src/lib/api/menus.ts:270 msgid "Failed to create menu translation" -msgstr "" +msgstr "Gagal membuat terjemahan menu" #: packages/admin/src/lib/api/redirects.ts:89 msgid "Failed to create redirect" -msgstr "" +msgstr "Gagal membuat pengalihan" #: packages/admin/src/lib/api/relations.ts:119 msgid "Failed to create relation" -msgstr "" +msgstr "Gagal membuat relasi" #: packages/admin/src/lib/api/sections.ts:88 msgid "Failed to create section" @@ -5439,7 +5433,7 @@ msgstr "Gagal membuat beberapa koleksi" #: packages/admin/src/lib/api/taxonomies.ts:164 msgid "Failed to create taxonomy" -msgstr "" +msgstr "Gagal membuat taksonomi" #: packages/admin/src/components/TaxonomySidebar.tsx:732 #: packages/admin/src/lib/api/taxonomies.ts:215 @@ -5448,7 +5442,7 @@ msgstr "Gagal membuat istilah" #: packages/admin/src/lib/api/taxonomies.ts:314 msgid "Failed to create term translation" -msgstr "" +msgstr "Gagal membuat terjemahan istilah" #: packages/admin/src/components/TaxonomySidebar.tsx:782 #: packages/admin/src/router.tsx:1544 @@ -5457,16 +5451,14 @@ msgstr "Gagal membuat terjemahan" #: packages/admin/src/lib/api/widgets.ts:120 msgid "Failed to create widget" -msgstr "" +msgstr "Gagal membuat widget" #: packages/admin/src/lib/api/widgets.ts:98 msgid "Failed to create widget area" -msgstr "" +msgstr "Gagal membuat area widget" -#: packages/admin/src/router.tsx:580 -#: packages/admin/src/router.tsx:609 -#: packages/admin/src/router.tsx:692 -#: packages/admin/src/router.tsx:1567 +#: packages/admin/src/router.tsx:580 packages/admin/src/router.tsx:609 +#: packages/admin/src/router.tsx:692 packages/admin/src/router.tsx:1567 msgid "Failed to delete" msgstr "Gagal menghapus" @@ -5596,7 +5588,7 @@ msgstr "Gagal menduplikasi" #: packages/admin/src/lib/api/content.ts:409 msgid "Failed to duplicate content" -msgstr "" +msgstr "Gagal menduplikasi konten" #: packages/admin/src/lib/api/settings.ts:105 msgid "Failed to email users" @@ -5621,15 +5613,15 @@ msgstr "Gagal menjalankan impor" #: packages/admin/src/lib/api/redirects.ts:127 msgid "Failed to fetch 404 summary" -msgstr "" +msgstr "Gagal mengambil ringkasan 404" #: packages/admin/src/lib/api/users.ts:297 msgid "Failed to fetch allowed domains" -msgstr "" +msgstr "Gagal mengambiil domain yang diizinkan" #: packages/admin/src/lib/api/api-tokens.ts:76 msgid "Failed to fetch API tokens" -msgstr "" +msgstr "Gagal mengambil token API" #: packages/admin/src/lib/api/client.ts:374 msgid "Failed to fetch auth mode" @@ -5641,23 +5633,23 @@ msgstr "Gagal mengambil pengaturan cadangan" #: packages/admin/src/lib/api/schema.ts:270 msgid "Failed to fetch block type" -msgstr "" +msgstr "Gagal mengambil tipe blok" #: packages/admin/src/lib/api/schema.ts:261 msgid "Failed to fetch block types" -msgstr "" +msgstr "Gagal mengambil tipe blok" #: packages/admin/src/lib/api/bylines.ts:118 msgid "Failed to fetch byline" -msgstr "" +msgstr "Gagal mengambil byline" #: packages/admin/src/lib/api/bylines.ts:157 msgid "Failed to fetch byline translations" -msgstr "" +msgstr "Gagal mengambil terjemahan byline" #: packages/admin/src/lib/api/bylines.ts:112 msgid "Failed to fetch bylines" -msgstr "" +msgstr "Gagal mengambil byline" #: packages/admin/src/lib/api/schema.ts:351 #: packages/admin/src/lib/api/schema.ts:355 @@ -5666,28 +5658,28 @@ msgstr "Gagal mengambil koleksi" #: packages/admin/src/lib/api/schema.ts:331 msgid "Failed to fetch collections" -msgstr "" +msgstr "Gagal mengambil koleksi" #: packages/admin/src/lib/api/comments.ts:85 msgid "Failed to fetch comment" -msgstr "" +msgstr "Gagal mengambil komentar" #: packages/admin/src/lib/api/comments.ts:77 msgid "Failed to fetch comment counts" -msgstr "" +msgstr "Gagal mengambil jumlah komentar" #: packages/admin/src/lib/api/comments.ts:68 msgid "Failed to fetch comments" -msgstr "" +msgstr "Gagal mengambil komentar" #: packages/admin/src/lib/api/content.ts:237 #: packages/admin/src/lib/api/content.ts:277 msgid "Failed to fetch content" -msgstr "" +msgstr "Gagal mengambil konten" #: packages/admin/src/lib/api/content.ts:258 msgid "Failed to fetch content authors" -msgstr "" +msgstr "Gagal mengambil penulis konten" #: packages/admin/src/lib/api/dashboard.ts:56 msgid "Failed to fetch dashboard stats" @@ -5703,7 +5695,7 @@ msgstr "Gagal mengambil istilah entri" #: packages/admin/src/lib/api/schema.ts:413 msgid "Failed to fetch fields" -msgstr "" +msgstr "Gagal mengambil kolom" #: packages/admin/src/lib/api/client.ts:357 msgid "Failed to fetch manifest" @@ -5735,23 +5727,23 @@ msgstr "Gagal mengambil rincian penggunaan media" #: packages/admin/src/lib/api/menus.ts:125 msgid "Failed to fetch menu" -msgstr "" +msgstr "Gagal mengambil menu" #: packages/admin/src/lib/api/menus.ts:249 msgid "Failed to fetch menu translations" -msgstr "" +msgstr "Gagal mengambil terjemahan menu" #: packages/admin/src/lib/api/menus.ts:117 msgid "Failed to fetch menus" -msgstr "" +msgstr "Gagal mengambil menu" #: packages/admin/src/lib/api/schema.ts:525 msgid "Failed to fetch orphaned tables" -msgstr "" +msgstr "Gagal mengambil tabel yang tidak tertaut" #: packages/admin/src/lib/api/users.ts:234 msgid "Failed to fetch passkeys" -msgstr "" +msgstr "Gagal mengambil kunci sandi" #: packages/admin/src/lib/api/marketplace.ts:324 #: packages/admin/src/lib/api/plugins.ts:70 @@ -5773,23 +5765,23 @@ msgstr "Gagal mengambil media penyedia" #: packages/admin/src/lib/api/redirects.ts:77 msgid "Failed to fetch redirects" -msgstr "" +msgstr "Gagal mengambil pengalihan" #: packages/admin/src/lib/api/relations.ts:174 msgid "Failed to fetch reference children" -msgstr "" +msgstr "Gagal mengambil anak referensi" #: packages/admin/src/lib/api/relations.ts:193 msgid "Failed to fetch reference parents" -msgstr "" +msgstr "Gagal mengambil induk referensi" #: packages/admin/src/lib/api/relations.ts:106 msgid "Failed to fetch relation" -msgstr "" +msgstr "Gagal mengambil relasi" #: packages/admin/src/lib/api/relations.ts:94 msgid "Failed to fetch relations" -msgstr "" +msgstr "Gagal mengambil relasi" #: packages/admin/src/lib/api/content.ts:641 #: packages/admin/src/lib/api/content.ts:646 @@ -5798,7 +5790,7 @@ msgstr "Gagal mengambil revisi" #: packages/admin/src/lib/api/content.ts:628 msgid "Failed to fetch revisions" -msgstr "" +msgstr "Gagal mengambil revisi" #: packages/admin/src/lib/api/sections.ts:76 msgid "Failed to fetch section" @@ -5818,11 +5810,11 @@ msgstr "Gagal mengambil status penyiapan" #: packages/admin/src/lib/api/taxonomies.ts:137 msgid "Failed to fetch taxonomies" -msgstr "" +msgstr "Gagal mengambil taksonomi" #: packages/admin/src/lib/api/taxonomies.ts:290 msgid "Failed to fetch term translations" -msgstr "" +msgstr "Gagal mengambil terjemahan istilah" #: packages/admin/src/components/TaxonomySidebar.tsx:144 #: packages/admin/src/lib/api/taxonomies.ts:196 @@ -5835,15 +5827,15 @@ msgstr "Gagal mengambil domain situs" #: packages/admin/src/lib/api/theme-marketplace.ts:99 msgid "Failed to fetch theme" -msgstr "" +msgstr "Gagal mengambil tema" #: packages/admin/src/lib/api/content.ts:113 msgid "Failed to fetch translations" -msgstr "" +msgstr "Gagal mengambil terjemahan" #: packages/admin/src/lib/api/content.ts:375 msgid "Failed to fetch trashed content" -msgstr "" +msgstr "Gagal mengambil konten yang dihapus" #: packages/admin/src/lib/api/core-update.ts:29 msgid "Failed to fetch update status" @@ -5851,27 +5843,26 @@ msgstr "Gagal mengambil status pembaruan" #: packages/admin/src/lib/api/current-user.ts:22 #: packages/admin/src/lib/api/users.ts:91 -#: packages/admin/src/lib/api/users.ts:96 -#: packages/admin/src/router.tsx:1127 +#: packages/admin/src/lib/api/users.ts:96 packages/admin/src/router.tsx:1127 #: packages/admin/src/router.tsx:2263 msgid "Failed to fetch user" msgstr "Gagal mengambil pengguna" #: packages/admin/src/lib/api/users.ts:77 msgid "Failed to fetch users" -msgstr "" +msgstr "Gagal mengambil pengguna" #: packages/admin/src/lib/api/widgets.ts:86 msgid "Failed to fetch widget area" -msgstr "" +msgstr "Gagal mengambil area widget" #: packages/admin/src/lib/api/widgets.ts:76 msgid "Failed to fetch widget areas" -msgstr "" +msgstr "Gagal mengambil area widget" #: packages/admin/src/lib/api/widgets.ts:168 msgid "Failed to fetch widget components" -msgstr "" +msgstr "Gagal mengambil komponen widget" #: packages/admin/src/components/ThemeMarketplaceBrowse.tsx:263 msgid "Failed to generate preview" @@ -5896,7 +5887,7 @@ msgstr "Gagal mendapatkan URL unggahan" #: packages/admin/src/lib/api/import.ts:432 msgid "Failed to import" -msgstr "" +msgstr "Gagal mengimpor" #: packages/admin/src/components/WordPressImport.tsx:456 #: packages/admin/src/lib/api/import.ts:257 @@ -5916,7 +5907,7 @@ msgstr "Gagal memasang pengaya" #: packages/admin/src/lib/api/users.ts:168 msgid "Failed to invite user" -msgstr "" +msgstr "Gagal mengundang pengguna" #: packages/admin/src/lib/api/byline-fields.ts:98 msgid "Failed to list byline fields" @@ -6061,10 +6052,9 @@ msgstr "Gagal menyiapkan impor" #: packages/admin/src/lib/api/import.ts:710 msgid "Failed to probe URL" -msgstr "" +msgstr "Gagal menyelidiki URL" -#: packages/admin/src/router.tsx:648 -#: packages/admin/src/router.tsx:1389 +#: packages/admin/src/router.tsx:648 packages/admin/src/router.tsx:1389 msgid "Failed to publish" msgstr "Gagal memublikasikan" @@ -6074,7 +6064,7 @@ msgstr "Gagal membaca penggunaan kolom byline" #: packages/admin/src/lib/api/schema.ts:548 msgid "Failed to register orphaned table" -msgstr "" +msgstr "Gagal mendaftarkan tabel yang tidak tertaut" #: packages/admin/src/lib/api/entry-lock.ts:70 msgid "Failed to release the entry lock" @@ -6113,11 +6103,11 @@ msgstr "Gagal mengurutkan kolom" #: packages/admin/src/lib/api/menus.ts:236 msgid "Failed to reorder menu items" -msgstr "" +msgstr "Gagal mengurutkan ulang item menu" #: packages/admin/src/lib/api/taxonomies.ts:261 msgid "Failed to reorder terms" -msgstr "" +msgstr "Gagal mengurutkan ulang istilah" #: packages/admin/src/lib/api/widgets.ts:158 msgid "Failed to reorder widgets" @@ -6149,8 +6139,7 @@ msgstr "Gagal mencabut token API" msgid "Failed to rewrite URLs" msgstr "Gagal menulis ulang URL" -#: packages/admin/src/router.tsx:841 -#: packages/admin/src/router.tsx:1250 +#: packages/admin/src/router.tsx:841 packages/admin/src/router.tsx:1250 #: packages/admin/src/router.tsx:2959 msgid "Failed to save" msgstr "Gagal menyimpan" @@ -6229,7 +6218,7 @@ msgstr "Gagal memperbarui {0}" #: packages/admin/src/lib/api/users.ts:335 msgid "Failed to update allowed domain" -msgstr "" +msgstr "Gagal memperbarui domain yang diizinkan" #: packages/admin/src/lib/api/backups.ts:46 msgid "Failed to update backup settings" @@ -6237,11 +6226,11 @@ msgstr "Gagal memperbarui pengaturan cadangan" #: packages/admin/src/lib/api/schema.ts:299 msgid "Failed to update block type" -msgstr "" +msgstr "Gagal memperbarui tipe blok" #: packages/admin/src/lib/api/bylines.ts:139 msgid "Failed to update byline" -msgstr "" +msgstr "Gagal memperbarui byline" #: packages/admin/src/lib/api/byline-fields.ts:135 msgid "Failed to update byline field" @@ -6249,15 +6238,15 @@ msgstr "Gagal memperbarui kolom byline" #: packages/admin/src/lib/api/schema.ts:390 msgid "Failed to update collection" -msgstr "" +msgstr "Gagal memperbarui koleksi" #: packages/admin/src/lib/api/comments.ts:104 msgid "Failed to update comment status" -msgstr "" +msgstr "Gagal memperbarui status komentar" #: packages/admin/src/lib/api/content.ts:330 msgid "Failed to update content" -msgstr "" +msgstr "Gagal memperbarui konten" #: packages/admin/src/components/settings/AllowedDomainsSettings.tsx:107 msgid "Failed to update domain" @@ -6265,7 +6254,7 @@ msgstr "Gagal memperbarui domain" #: packages/admin/src/lib/api/schema.ts:455 msgid "Failed to update field" -msgstr "" +msgstr "Gagal memperbarui kolom" #: packages/admin/src/lib/api/media.ts:647 msgid "Failed to update media" @@ -6273,11 +6262,11 @@ msgstr "Gagal memperbarui media" #: packages/admin/src/lib/api/menus.ts:153 msgid "Failed to update menu" -msgstr "" +msgstr "Gagal memperbarui menu" #: packages/admin/src/lib/api/menus.ts:202 msgid "Failed to update menu item" -msgstr "" +msgstr "Gagal memperbarui item menu" #: packages/admin/src/components/PluginManager.tsx:326 #: packages/admin/src/lib/api/marketplace.ts:373 @@ -6295,11 +6284,11 @@ msgstr "Gagal memperbarui pengaturan pengaya" #: packages/admin/src/lib/api/redirects.ts:101 msgid "Failed to update redirect" -msgstr "" +msgstr "Gagal memperbarui pengalihan" #: packages/admin/src/lib/api/relations.ts:132 msgid "Failed to update relation" -msgstr "" +msgstr "Gagal memperbarui relasi" #: packages/admin/src/lib/api/sections.ts:100 msgid "Failed to update section" @@ -6315,15 +6304,15 @@ msgstr "Gagal memperbarui status" #: packages/admin/src/lib/api/taxonomies.ts:239 msgid "Failed to update term" -msgstr "" +msgstr "Gagal memperbarui istilah" #: packages/admin/src/lib/api/users.ts:112 msgid "Failed to update user" -msgstr "" +msgstr "Gagal memperbarui pengguna" #: packages/admin/src/lib/api/widgets.ts:136 msgid "Failed to update widget" -msgstr "" +msgstr "Gagal memperbarui widget" #: packages/admin/src/lib/api/transfer.ts:431 msgid "Failed to upload a package file" @@ -7881,8 +7870,7 @@ msgstr "Memuat koleksi..." msgid "Loading comments..." msgstr "Memuat komentar..." -#: packages/admin/src/router.tsx:3212 -#: packages/admin/src/router.tsx:3224 +#: packages/admin/src/router.tsx:3212 packages/admin/src/router.tsx:3224 msgid "Loading configuration..." msgstr "Memuat konfigurasi..." @@ -8157,7 +8145,7 @@ msgstr "Dikelola oleh penyedia media eksternal" #: packages/admin/src/components/users/UserDetail.tsx:155 msgid "Managed by your identity provider. Change the name there." -msgstr "" +msgstr "Dikelola oleh penyedia identitas Anda. Ubah nama di sana." #: packages/admin/src/components/Redirects.tsx:398 msgid "Manual" @@ -8208,7 +8196,7 @@ msgstr "Konfigurasi Marketplace sudah tidak berlaku lagi" #: packages/admin/src/lib/api/marketplace.ts:311 msgid "Marketplace search failed" -msgstr "" +msgstr "Pencarian marketplace gagal" #: packages/admin/src/components/settings/transfer/ImportReview.tsx:339 msgid "Matched by email" @@ -10000,8 +9988,7 @@ msgstr "Registri Pengaya" msgid "Plugin registry configuration error" msgstr "Galat konfigurasi registri pengaya" -#: packages/admin/src/router.tsx:2555 -#: packages/admin/src/router.tsx:2586 +#: packages/admin/src/router.tsx:2555 packages/admin/src/router.tsx:2586 msgid "Plugin registry is not configured." msgstr "Registri pengaya tidak dikonfigurasikan." @@ -11545,7 +11532,6 @@ msgstr "SCSS" #: packages/admin/src/components/ContentTypeEditor.tsx:106 #: packages/admin/src/components/Settings.tsx:165 -#: packages/admin/src/components/Settings.tsx:165 #: packages/admin/src/components/Widgets.tsx:152 msgid "Search" msgstr "Cari" @@ -12292,7 +12278,7 @@ msgstr "Masuk gagal. Silakan coba lagi." #: packages/admin/src/lib/api/users.ts:377 msgid "Signup request failed" -msgstr "" +msgstr "Permintaan daftar gagal" #: packages/admin/src/components/FieldEditor.tsx:427 msgid "Single choice from options" @@ -13289,7 +13275,7 @@ msgstr "Tema tidak ditemukan" #: packages/admin/src/lib/api/theme-marketplace.ts:87 msgid "Theme search failed" -msgstr "" +msgstr "Pencarian tema gagal" #: packages/admin/src/components/SectionEditor.tsx:205 msgid "Theme Section" @@ -13381,11 +13367,11 @@ msgstr "Entri ini diubah di tempat lain. Simpan saja, atau muat ulang untuk mend #: packages/admin/src/router.tsx:1869 msgid "This entry doesn't exist or isn't published. Your role can only view published content." -msgstr "" +msgstr "Entri ini tidak ada atau tidak dipublikasikan. Peran Anda hanya dapat melihat konten yang dipublikasikan." #: packages/admin/src/router.tsx:1870 msgid "This entry doesn't exist or was deleted." -msgstr "" +msgstr "Entri ini tidak ada atau telah dihapus." #: packages/admin/src/components/editor/LinkDestinationInput.tsx:221 msgid "This entry has no URL until it is published. Paste a URL instead."