= useRef(null);
-
useImperativeHandle(forwardedRef, () => ({
clearEditor: (emitUpdate = false) => {
- editorRef.current?.chain().setMeta("skipImageDeletion", true).clearContent(emitUpdate).run();
+ editor?.chain().setMeta("skipImageDeletion", true).clearContent(emitUpdate).run();
},
setEditorValue: (content: string) => {
- editorRef.current?.commands.setContent(content, false, { preserveWhitespace: "full" });
+ editor?.commands.setContent(content, false, { preserveWhitespace: "full" });
},
getMarkDown: (): string => {
- const markdownOutput = editorRef.current?.storage.markdown.getMarkdown();
+ const markdownOutput = editor?.storage.markdown.getMarkdown();
return markdownOutput;
},
getDocument: () => {
const documentBinary = provider?.document ? Y.encodeStateAsUpdate(provider?.document) : null;
- const documentHTML = editorRef.current?.getHTML() ?? "
";
- const documentJSON = editorRef.current?.getJSON() ?? null;
+ const documentHTML = editor?.getHTML() ?? "
";
+ const documentJSON = editor?.getJSON() ?? null;
return {
binary: documentBinary,
@@ -106,35 +96,19 @@ export const useReadOnlyEditor = (props: CustomReadOnlyEditorProps) => {
};
},
scrollSummary: (marking: IMarking): void => {
- if (!editorRef.current) return;
- scrollSummary(editorRef.current, marking);
+ if (!editor) return;
+ scrollSummary(editor, marking);
},
getDocumentInfo: () => ({
- characters: editorRef?.current?.storage?.characterCount?.characters?.() ?? 0,
- paragraphs: getParagraphCount(editorRef?.current?.state),
- words: editorRef?.current?.storage?.characterCount?.words?.() ?? 0,
+ characters: editor.storage?.characterCount?.characters?.() ?? 0,
+ paragraphs: getParagraphCount(editor.state),
+ words: editor.storage?.characterCount?.words?.() ?? 0,
}),
- onHeadingChange: (callback: (headings: IMarking[]) => void) => {
- // Subscribe to update event emitted from headers extension
- editorRef.current?.on("update", () => {
- callback(editorRef.current?.storage.headingList.headings);
- });
- // Return a function to unsubscribe to the continuous transactions of
- // the editor on unmounting the component that has subscribed to this
- // method
- return () => {
- editorRef.current?.off("update");
- };
- },
- emitRealTimeUpdate: (message: TDocumentEventsServer) => provider?.sendStateless(message),
- listenToRealTimeUpdate: () => provider && { on: provider.on.bind(provider), off: provider.off.bind(provider) },
- getHeadings: () => editorRef?.current?.storage.headingList.headings,
}));
if (!editor) {
return null;
}
- editorRef.current = editor;
return editor;
};
diff --git a/packages/editor/src/core/plugins/drag-handle.ts b/packages/editor/src/core/plugins/drag-handle.ts
index 1c015dcb0..e71c38b30 100644
--- a/packages/editor/src/core/plugins/drag-handle.ts
+++ b/packages/editor/src/core/plugins/drag-handle.ts
@@ -1,5 +1,5 @@
-import { Fragment, Slice, Node } from "@tiptap/pm/model";
-import { NodeSelection, TextSelection } from "@tiptap/pm/state";
+import { Fragment, Slice, Node, Schema } from "@tiptap/pm/model";
+import { NodeSelection } from "@tiptap/pm/state";
// @ts-expect-error __serializeForClipboard's is not exported
import { __serializeForClipboard, EditorView } from "@tiptap/pm/view";
// extensions
@@ -8,6 +8,29 @@ import { SideMenuHandleOptions, SideMenuPluginProps } from "@/extensions";
const verticalEllipsisIcon =
' ';
+const generalSelectors = [
+ "li",
+ "p:not(:first-child)",
+ ".code-block",
+ "blockquote",
+ "h1, h2, h3, h4, h5, h6",
+ "[data-type=horizontalRule]",
+ ".table-wrapper",
+ ".issue-embed",
+ ".image-component",
+ ".image-upload-component",
+ ".editor-callout-component",
+].join(", ");
+
+const maxScrollSpeed = 20;
+const acceleration = 0.5;
+
+const scrollParentCache = new WeakMap();
+
+function easeOutQuadAnimation(t: number) {
+ return t * (2 - t);
+}
+
const createDragHandleElement = (): HTMLElement => {
const dragHandleElement = document.createElement("button");
dragHandleElement.type = "button";
@@ -30,33 +53,53 @@ const createDragHandleElement = (): HTMLElement => {
return dragHandleElement;
};
+const isScrollable = (node: HTMLElement | SVGElement) => {
+ if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
+ return false;
+ }
+ const style = getComputedStyle(node);
+ return ["overflow", "overflow-y"].some((propertyName) => {
+ const value = style.getPropertyValue(propertyName);
+ return value === "auto" || value === "scroll";
+ });
+};
+
+const getScrollParent = (node: HTMLElement | SVGElement) => {
+ if (scrollParentCache.has(node)) {
+ return scrollParentCache.get(node);
+ }
+
+ let currentParent = node.parentElement;
+
+ while (currentParent) {
+ if (isScrollable(currentParent)) {
+ scrollParentCache.set(node, currentParent);
+ return currentParent;
+ }
+ currentParent = currentParent.parentElement;
+ }
+
+ const result = document.scrollingElement || document.documentElement;
+ scrollParentCache.set(node, result);
+ return result;
+};
+
export const nodeDOMAtCoords = (coords: { x: number; y: number }) => {
const elements = document.elementsFromPoint(coords.x, coords.y);
- const generalSelectors = [
- "li",
- "p:not(:first-child)",
- ".code-block",
- "blockquote",
- "h1, h2, h3, h4, h5, h6",
- "[data-type=horizontalRule]",
- ".table-wrapper",
- ".issue-embed",
- ".image-component",
- ".image-upload-component",
- ".editor-callout-component",
- ].join(", ");
for (const elem of elements) {
+ // Check for table wrapper first
+ if (elem.matches(".table-wrapper")) {
+ return elem;
+ }
+
if (elem.matches("p:first-child") && elem.parentElement?.matches(".ProseMirror")) {
return elem;
}
- // if the element is a tag that is the first child of a td or th
- if (
- (elem.matches("td > p:first-child") || elem.matches("th > p:first-child")) &&
- elem?.textContent?.trim() !== ""
- ) {
- return elem; // Return only if p tag is not empty in td or th
+ // Skip table cells
+ if (elem.closest(".table-wrapper")) {
+ continue;
}
// apply general selector
@@ -85,140 +128,73 @@ const nodePosAtDOMForBlockQuotes = (node: Element, view: EditorView) => {
})?.inside;
};
-const calcNodePos = (pos: number, view: EditorView, node: Element) => {
- const maxPos = view.state.doc.content.size;
- const safePos = Math.max(0, Math.min(pos, maxPos));
- const $pos = view.state.doc.resolve(safePos);
-
- if ($pos.depth > 1) {
- if (node.matches("ul li, ol li")) {
- // only for nested lists
- const newPos = $pos.before($pos.depth);
- return Math.max(0, Math.min(newPos, maxPos));
- }
- }
-
- return safePos;
-};
-
export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOptions => {
let listType = "";
- const handleDragStart = (event: DragEvent, view: EditorView) => {
- view.focus();
-
- if (!event.dataTransfer) return;
-
- const node = nodeDOMAtCoords({
- x: event.clientX + 50 + options.dragHandleWidth,
- y: event.clientY,
- });
-
- if (!(node instanceof Element)) return;
-
- let draggedNodePos = nodePosAtDOM(node, view, options);
- if (draggedNodePos == null || draggedNodePos < 0) return;
- draggedNodePos = calcNodePos(draggedNodePos, view, node);
-
- const { from, to } = view.state.selection;
- const diff = from - to;
-
- const fromSelectionPos = calcNodePos(from, view, node);
- let differentNodeSelected = false;
-
- const nodePos = view.state.doc.resolve(fromSelectionPos);
-
- // Check if nodePos points to the top level node
- if (nodePos.node().type.name === "doc") differentNodeSelected = true;
- else {
- // TODO FIX ERROR
- const nodeSelection = NodeSelection.create(view.state.doc, nodePos.before());
- // Check if the node where the drag event started is part of the current selection
- differentNodeSelected = !(
- draggedNodePos + 1 >= nodeSelection.$from.pos && draggedNodePos <= nodeSelection.$to.pos
- );
- }
-
- if (!differentNodeSelected && diff !== 0 && !(view.state.selection instanceof NodeSelection)) {
- const endSelection = NodeSelection.create(view.state.doc, to - 1);
- const multiNodeSelection = TextSelection.create(view.state.doc, draggedNodePos, endSelection.$to.pos);
- view.dispatch(view.state.tr.setSelection(multiNodeSelection));
- } else {
- // TODO FIX ERROR
- const nodeSelection = NodeSelection.create(view.state.doc, draggedNodePos);
- view.dispatch(view.state.tr.setSelection(nodeSelection));
- }
-
- // If the selected node is a list item, we need to save the type of the wrapping list e.g. OL or UL
- if (view.state.selection instanceof NodeSelection && view.state.selection.node.type.name === "listItem") {
- listType = node.parentElement!.tagName;
- }
-
- if (node.matches("blockquote")) {
- let nodePosForBlockQuotes = nodePosAtDOMForBlockQuotes(node, view);
- if (nodePosForBlockQuotes === null || nodePosForBlockQuotes === undefined) return;
-
- const docSize = view.state.doc.content.size;
- nodePosForBlockQuotes = Math.max(0, Math.min(nodePosForBlockQuotes, docSize));
-
- if (nodePosForBlockQuotes >= 0 && nodePosForBlockQuotes <= docSize) {
- // TODO FIX ERROR
- const nodeSelection = NodeSelection.create(view.state.doc, nodePosForBlockQuotes);
- view.dispatch(view.state.tr.setSelection(nodeSelection));
- }
- }
-
- const slice = view.state.selection.content();
- const { dom, text } = __serializeForClipboard(view, slice);
-
- event.dataTransfer.clearData();
- event.dataTransfer.setData("text/html", dom.innerHTML);
- event.dataTransfer.setData("text/plain", text);
- event.dataTransfer.effectAllowed = "copyMove";
-
- event.dataTransfer.setDragImage(node, 0, 0);
-
- view.dragging = { slice, move: event.ctrlKey };
- };
+ let isDragging = false;
+ let lastClientY = 0;
+ let scrollAnimationFrame = null;
+ let isDraggedOutsideWindow: "top" | "bottom" | boolean = false;
+ let isMouseInsideWhileDragging = false;
+ let currentScrollSpeed = 0;
const handleClick = (event: MouseEvent, view: EditorView) => {
- view.focus();
+ handleNodeSelection(event, view, false, options);
+ };
- const node = nodeDOMAtCoords({
- x: event.clientX + 50 + options.dragHandleWidth,
- y: event.clientY,
- });
+ const handleDragStart = (event: DragEvent, view: EditorView) => {
+ const { listType: listTypeFromDragStart } = handleNodeSelection(event, view, true, options);
+ listType = listTypeFromDragStart;
+ isDragging = true;
+ lastClientY = event.clientY;
+ scroll();
+ };
- if (!(node instanceof Element)) return;
+ const handleDragEnd = (event: TEvent, view?: EditorView) => {
+ event.preventDefault();
+ isDragging = false;
+ isMouseInsideWhileDragging = false;
+ if (scrollAnimationFrame) {
+ cancelAnimationFrame(scrollAnimationFrame);
+ scrollAnimationFrame = null;
+ }
- if (node.matches("blockquote")) {
- let nodePosForBlockQuotes = nodePosAtDOMForBlockQuotes(node, view);
- if (nodePosForBlockQuotes === null || nodePosForBlockQuotes === undefined) return;
+ view?.dom.classList.remove("dragging");
+ };
- const docSize = view.state.doc.content.size;
- nodePosForBlockQuotes = Math.max(0, Math.min(nodePosForBlockQuotes, docSize));
-
- if (nodePosForBlockQuotes >= 0 && nodePosForBlockQuotes <= docSize) {
- // TODO FIX ERROR
- const nodeSelection = NodeSelection.create(view.state.doc, nodePosForBlockQuotes);
- view.dispatch(view.state.tr.setSelection(nodeSelection));
- }
+ function scroll() {
+ if (!isDragging) {
+ currentScrollSpeed = 0;
return;
}
- let nodePos = nodePosAtDOM(node, view, options);
+ const scrollableParent = getScrollParent(dragHandleElement);
+ if (!scrollableParent) return;
- if (nodePos === null || nodePos === undefined) return;
+ const scrollRegionUp = options.scrollThreshold.up;
+ const scrollRegionDown = window.innerHeight - options.scrollThreshold.down;
- // Adjust the nodePos to point to the start of the node, ensuring NodeSelection can be applied
- nodePos = calcNodePos(nodePos, view, node);
+ let targetScrollAmount = 0;
- // TODO FIX ERROR
- // Use NodeSelection to select the node at the calculated position
- const nodeSelection = NodeSelection.create(view.state.doc, nodePos);
+ if (isDraggedOutsideWindow === "top") {
+ targetScrollAmount = -maxScrollSpeed * 5;
+ } else if (isDraggedOutsideWindow === "bottom") {
+ targetScrollAmount = maxScrollSpeed * 5;
+ } else if (lastClientY < scrollRegionUp) {
+ const ratio = easeOutQuadAnimation((scrollRegionUp - lastClientY) / options.scrollThreshold.up);
+ targetScrollAmount = -maxScrollSpeed * ratio;
+ } else if (lastClientY > scrollRegionDown) {
+ const ratio = easeOutQuadAnimation((lastClientY - scrollRegionDown) / options.scrollThreshold.down);
+ targetScrollAmount = maxScrollSpeed * ratio;
+ }
- // Dispatch the transaction to update the selection
- view.dispatch(view.state.tr.setSelection(nodeSelection));
- };
+ currentScrollSpeed += (targetScrollAmount - currentScrollSpeed) * acceleration;
+
+ if (Math.abs(currentScrollSpeed) > 0.1) {
+ scrollableParent.scrollBy({ top: currentScrollSpeed });
+ }
+
+ scrollAnimationFrame = requestAnimationFrame(scroll);
+ }
let dragHandleElement: HTMLElement | null = null;
// drag handle view actions
@@ -231,51 +207,46 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
const view = (view: EditorView, sideMenu: HTMLDivElement | null) => {
dragHandleElement = createDragHandleElement();
dragHandleElement.addEventListener("dragstart", (e) => handleDragStart(e, view));
+ dragHandleElement.addEventListener("dragend", (e) => handleDragEnd(e, view));
dragHandleElement.addEventListener("click", (e) => handleClick(e, view));
dragHandleElement.addEventListener("contextmenu", (e) => handleClick(e, view));
- const isScrollable = (node: HTMLElement | SVGElement) => {
- if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
- return false;
+ const dragOverHandler = (e: DragEvent) => {
+ e.preventDefault();
+ if (isDragging) {
+ lastClientY = e.clientY;
}
- const style = getComputedStyle(node);
- return ["overflow", "overflow-y"].some((propertyName) => {
- const value = style.getPropertyValue(propertyName);
- return value === "auto" || value === "scroll";
- });
};
- const getScrollParent = (node: HTMLElement | SVGElement) => {
- let currentParent = node.parentElement;
- while (currentParent) {
- if (isScrollable(currentParent)) {
- return currentParent;
+ const mouseMoveHandler = (e: MouseEvent) => {
+ if (isMouseInsideWhileDragging) {
+ handleDragEnd(e, view);
+ }
+ };
+
+ const dragLeaveHandler = (e: DragEvent) => {
+ if (e.clientY <= 0 || e.clientX <= 0 || e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) {
+ isMouseInsideWhileDragging = true;
+
+ const windowMiddleY = window.innerHeight / 2;
+
+ if (lastClientY < windowMiddleY) {
+ isDraggedOutsideWindow = "top";
+ } else {
+ isDraggedOutsideWindow = "bottom";
}
- currentParent = currentParent.parentElement;
}
- return document.scrollingElement || document.documentElement;
};
- const maxScrollSpeed = 100;
+ const dragEnterHandler = () => {
+ isDraggedOutsideWindow = false;
+ };
- dragHandleElement.addEventListener("drag", (e) => {
- hideDragHandle();
- const scrollableParent = getScrollParent(dragHandleElement);
- if (!scrollableParent) return;
- const scrollThreshold = options.scrollThreshold;
+ window.addEventListener("dragleave", dragLeaveHandler);
+ window.addEventListener("dragenter", dragEnterHandler);
- if (e.clientY < scrollThreshold.up) {
- const overflow = scrollThreshold.up - e.clientY;
- const ratio = Math.min(overflow / scrollThreshold.up, 1);
- const scrollAmount = -maxScrollSpeed * ratio;
- scrollableParent.scrollBy({ top: scrollAmount });
- } else if (window.innerHeight - e.clientY < scrollThreshold.down) {
- const overflow = e.clientY - (window.innerHeight - scrollThreshold.down);
- const ratio = Math.min(overflow / scrollThreshold.down, 1);
- const scrollAmount = maxScrollSpeed * ratio;
- scrollableParent.scrollBy({ top: scrollAmount });
- }
- });
+ document.addEventListener("dragover", dragOverHandler);
+ document.addEventListener("mousemove", mouseMoveHandler);
hideDragHandle();
@@ -285,6 +256,15 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
destroy: () => {
dragHandleElement?.remove?.();
dragHandleElement = null;
+ isDragging = false;
+ if (scrollAnimationFrame) {
+ cancelAnimationFrame(scrollAnimationFrame);
+ scrollAnimationFrame = null;
+ }
+ window.removeEventListener("dragleave", dragLeaveHandler);
+ window.removeEventListener("dragenter", dragEnterHandler);
+ document.removeEventListener("dragover", dragOverHandler);
+ document.removeEventListener("mousemove", mouseMoveHandler);
},
};
};
@@ -313,29 +293,36 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
const resolvedPos = view.state.doc.resolve(dropPos.pos);
let isDroppedInsideList = false;
+ let dropDepth = 0;
// Traverse up the document tree to find if we're inside a list item
for (let i = resolvedPos.depth; i > 0; i--) {
if (resolvedPos.node(i).type.name === "listItem") {
isDroppedInsideList = true;
+ dropDepth = i;
break;
}
}
- // If the selected node is a list item and is not dropped inside a list, we need to wrap it inside tag otherwise ol list items will be transformed into ul list item when dropped
- if (
- view.state.selection instanceof NodeSelection &&
- view.state.selection.node.type.name === "listItem" &&
- !isDroppedInsideList &&
- listType == "OL"
- ) {
- const text = droppedNode.textContent;
- if (!text) return;
- const paragraph = view.state.schema.nodes.paragraph?.createAndFill({}, view.state.schema.text(text));
- const listItem = view.state.schema.nodes.listItem?.createAndFill({}, paragraph);
+ // Handle nested list items and task items
+ if (droppedNode.type.name === "listItem") {
+ let slice = view.state.selection.content();
+ let newFragment = slice.content;
- const newList = view.state.schema.nodes.orderedList?.createAndFill(null, listItem);
- const slice = new Slice(Fragment.from(newList), 0, 0);
+ // If dropping outside a list or at a different depth, adjust the structure
+ if (!isDroppedInsideList || dropDepth !== resolvedPos.depth) {
+ // Flatten the structure if needed
+ newFragment = flattenListStructure(newFragment, view.state.schema);
+ }
+
+ // Wrap in appropriate list type if dropped outside a list
+ if (!isDroppedInsideList) {
+ const listNodeType =
+ listType === "OL" ? view.state.schema.nodes.orderedList : view.state.schema.nodes.bulletList;
+ newFragment = Fragment.from(listNodeType.create(null, newFragment));
+ }
+
+ slice = new Slice(newFragment, slice.openStart, slice.openEnd);
view.dragging = { slice, move: event.ctrlKey };
}
},
@@ -349,3 +336,90 @@ export const DragHandlePlugin = (options: SideMenuPluginProps): SideMenuHandleOp
domEvents,
};
};
+
+// Helper function to flatten nested list structure
+function flattenListStructure(fragment: Fragment, schema: Schema): Fragment {
+ const result: Node[] = [];
+ fragment.forEach((node) => {
+ if (node.type === schema.nodes.listItem || node.type === schema.nodes.taskItem) {
+ result.push(node);
+ if (
+ node.content.firstChild &&
+ (node.content.firstChild.type === schema.nodes.bulletList ||
+ node.content.firstChild.type === schema.nodes.orderedList)
+ ) {
+ const sublist = node.content.firstChild;
+ const flattened = flattenListStructure(sublist.content, schema);
+ flattened.forEach((subNode) => result.push(subNode));
+ }
+ }
+ });
+ return Fragment.from(result);
+}
+
+const handleNodeSelection = (
+ event: MouseEvent | DragEvent,
+ view: EditorView,
+ isDragStart: boolean,
+ options: SideMenuPluginProps
+) => {
+ let listType = "";
+ view.focus();
+
+ const node = nodeDOMAtCoords({
+ x: event.clientX + 50 + options.dragHandleWidth,
+ y: event.clientY,
+ });
+
+ if (!(node instanceof Element)) return;
+
+ let draggedNodePos = nodePosAtDOM(node, view, options);
+ if (draggedNodePos == null || draggedNodePos < 0) return;
+
+ // Handle blockquotes separately
+ if (node.matches("blockquote")) {
+ draggedNodePos = nodePosAtDOMForBlockQuotes(node, view);
+ if (draggedNodePos === null || draggedNodePos === undefined) return;
+ } else {
+ // Resolve the position to get the parent node
+ const $pos = view.state.doc.resolve(draggedNodePos);
+
+ // If it's a nested list item or task item, move up to the item level
+ if (($pos.parent.type.name === "listItem" || $pos.parent.type.name === "taskItem") && $pos.depth > 1) {
+ draggedNodePos = $pos.before($pos.depth);
+ }
+ }
+
+ const docSize = view.state.doc.content.size;
+ draggedNodePos = Math.max(0, Math.min(draggedNodePos, docSize));
+
+ // Use NodeSelection to select the node at the calculated position
+ const nodeSelection = NodeSelection.create(view.state.doc, draggedNodePos);
+
+ // Dispatch the transaction to update the selection
+ view.dispatch(view.state.tr.setSelection(nodeSelection));
+
+ if (isDragStart) {
+ // Additional logic for drag start
+ if (event instanceof DragEvent && !event.dataTransfer) return;
+
+ if (nodeSelection.node.type.name === "listItem" || nodeSelection.node.type.name === "taskItem") {
+ listType = node.closest("ol, ul")?.tagName || "";
+ }
+
+ const slice = view.state.selection.content();
+ const { dom, text } = __serializeForClipboard(view, slice);
+
+ if (event instanceof DragEvent) {
+ event.dataTransfer.clearData();
+ event.dataTransfer.setData("text/html", dom.innerHTML);
+ event.dataTransfer.setData("text/plain", text);
+ event.dataTransfer.effectAllowed = "copyMove";
+ event.dataTransfer.setDragImage(node, 0, 0);
+ }
+
+ view.dragging = { slice, move: event.ctrlKey };
+ }
+
+ return { listType };
+};
diff --git a/packages/editor/src/core/plugins/image/utils/validate-file.ts b/packages/editor/src/core/plugins/image/utils/validate-file.ts
index db88f3f73..703bb2bf0 100644
--- a/packages/editor/src/core/plugins/image/utils/validate-file.ts
+++ b/packages/editor/src/core/plugins/image/utils/validate-file.ts
@@ -1,3 +1,6 @@
+// constants
+import { ACCEPTED_FILE_MIME_TYPES } from "@/constants/config";
+
type TArgs = {
file: File;
maxFileSize: number;
@@ -11,9 +14,8 @@ export const isFileValid = (args: TArgs): boolean => {
return false;
}
- const allowedTypes = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
- if (!allowedTypes.includes(file.type)) {
- alert("Invalid file type. Please select a JPEG, JPG, PNG, or WEBP image file.");
+ if (!ACCEPTED_FILE_MIME_TYPES.includes(file.type)) {
+ alert("Invalid file type. Please select a JPEG, JPG, PNG, WEBP or GIF file.");
return false;
}
diff --git a/packages/editor/src/core/props/props.tsx b/packages/editor/src/core/props/props.tsx
index 4bda3e51a..ee0b9e500 100644
--- a/packages/editor/src/core/props/props.tsx
+++ b/packages/editor/src/core/props/props.tsx
@@ -1,6 +1,6 @@
import { EditorProps } from "@tiptap/pm/view";
-// helpers
-import { cn } from "@/helpers/common";
+// plane utils
+import { cn } from "@plane/utils";
export type TCoreEditorProps = {
editorClassName: string;
diff --git a/packages/editor/src/core/props/read-only.tsx b/packages/editor/src/core/props/read-only.tsx
index aaa635a50..ea5bf09f3 100644
--- a/packages/editor/src/core/props/read-only.tsx
+++ b/packages/editor/src/core/props/read-only.tsx
@@ -1,6 +1,6 @@
import { EditorProps } from "@tiptap/pm/view";
-// helpers
-import { cn } from "@/helpers/common";
+// plane utils
+import { cn } from "@plane/utils";
// props
import { TCoreEditorProps } from "@/props";
diff --git a/packages/editor/src/core/types/collaboration.ts b/packages/editor/src/core/types/collaboration.ts
index 35fbdb996..82e2f81f9 100644
--- a/packages/editor/src/core/types/collaboration.ts
+++ b/packages/editor/src/core/types/collaboration.ts
@@ -6,10 +6,11 @@ import { TEmbedConfig } from "@/plane-editor/types";
import {
EditorReadOnlyRefApi,
EditorRefApi,
- IMentionHighlight,
- IMentionSuggestion,
TExtensions,
TFileHandler,
+ TMentionHandler,
+ TReadOnlyFileHandler,
+ TReadOnlyMentionHandler,
TRealtimeConfig,
TUserDetails,
} from "@/types";
@@ -21,15 +22,12 @@ export type TServerHandler = {
type TCollaborativeEditorHookProps = {
disabledExtensions: TExtensions[];
+ editable?: boolean;
editorClassName: string;
editorProps?: EditorProps;
extensions?: Extensions;
handleEditorReady?: (value: boolean) => void;
id: string;
- mentionHandler: {
- highlights: () => Promise;
- suggestions?: () => Promise;
- };
realtimeConfig: TRealtimeConfig;
serverHandler?: TServerHandler;
user: TUserDetails;
@@ -40,11 +38,13 @@ export type TCollaborativeEditorProps = TCollaborativeEditorHookProps & {
embedHandler?: TEmbedConfig;
fileHandler: TFileHandler;
forwardedRef?: React.MutableRefObject;
+ mentionHandler: TMentionHandler;
placeholder?: string | ((isFocused: boolean, value: string) => string);
tabIndex?: number;
};
export type TReadOnlyCollaborativeEditorProps = TCollaborativeEditorHookProps & {
- fileHandler: Pick;
+ fileHandler: TReadOnlyFileHandler;
forwardedRef?: React.MutableRefObject;
+ mentionHandler: TReadOnlyMentionHandler;
};
diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts
index 3bb4d1af2..d4d8ca901 100644
--- a/packages/editor/src/core/types/config.ts
+++ b/packages/editor/src/core/types/config.ts
@@ -1,11 +1,15 @@
import { DeleteImage, RestoreImage, UploadImage } from "@/types";
-export type TFileHandler = {
+export type TReadOnlyFileHandler = {
getAssetSrc: (path: string) => Promise;
+ restore: RestoreImage;
+};
+
+export type TFileHandler = TReadOnlyFileHandler & {
+ assetsUploadStatus: Record; // blockId => progress percentage
cancel: () => void;
delete: DeleteImage;
upload: UploadImage;
- restore: RestoreImage;
validation: {
/**
* @description max file size in bytes
diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts
index e91af8e49..edf696ab8 100644
--- a/packages/editor/src/core/types/editor.ts
+++ b/packages/editor/src/core/types/editor.ts
@@ -1,11 +1,13 @@
-import { JSONContent } from "@tiptap/core";
+import { Extensions, JSONContent } from "@tiptap/core";
import { Selection } from "@tiptap/pm/state";
+// plane types
+import { TWebhookConnectionQueryParams } from "@plane/types";
+// extension types
+import { TTextAlign } from "@/extensions";
// helpers
import { IMarking } from "@/helpers/scroll-to-node";
// types
import {
- IMentionHighlight,
- IMentionSuggestion,
TAIHandler,
TDisplayConfig,
TDocumentEventEmitter,
@@ -13,9 +15,11 @@ import {
TEmbedConfig,
TExtensions,
TFileHandler,
+ TMentionHandler,
+ TReadOnlyFileHandler,
+ TReadOnlyMentionHandler,
TServerHandler,
} from "@/types";
-import { TTextAlign } from "@/extensions";
export type TEditorCommands =
| "text"
@@ -28,7 +32,7 @@ export type TEditorCommands =
| "bold"
| "italic"
| "underline"
- | "strikethrough"
+ | "strike"
| "bulleted-list"
| "numbered-list"
| "to-do-list"
@@ -41,12 +45,16 @@ export type TEditorCommands =
| "text-color"
| "background-color"
| "text-align"
- | "callout";
+ | "callout"
+ | "attachment";
export type TCommandExtraProps = {
image: {
savedSelection: Selection | null;
};
+ attachment: {
+ savedSelection: Selection | null;
+ };
"text-color": {
color: string | undefined;
};
@@ -83,10 +91,6 @@ export type EditorReadOnlyRefApi = {
paragraphs: number;
words: number;
};
- onHeadingChange: (callback: (headings: IMarking[]) => void) => () => void;
- getHeadings: () => IMarking[];
- emitRealTimeUpdate: (action: TDocumentEventsServer) => void;
- listenToRealTimeUpdate: () => TDocumentEventEmitter | undefined;
};
export interface EditorRefApi extends EditorReadOnlyRefApi {
@@ -102,6 +106,10 @@ export interface EditorRefApi extends EditorReadOnlyRefApi {
getSelectedText: () => string | null;
insertText: (contentHTML: string, insertOnNextLine?: boolean) => void;
setProviderDocument: (value: Uint8Array) => void;
+ onHeadingChange: (callback: (headings: IMarking[]) => void) => () => void;
+ getHeadings: () => IMarking[];
+ emitRealTimeUpdate: (action: TDocumentEventsServer) => void;
+ listenToRealTimeUpdate: () => TDocumentEventEmitter | undefined;
}
// editor props
@@ -114,10 +122,7 @@ export interface IEditorProps {
forwardedRef?: React.MutableRefObject;
id: string;
initialValue: string;
- mentionHandler: {
- highlights: () => Promise;
- suggestions?: () => Promise;
- };
+ mentionHandler: TMentionHandler;
onChange?: (json: object, html: string) => void;
onTransaction?: () => void;
handleEditorReady?: (value: boolean) => void;
@@ -128,10 +133,10 @@ export interface IEditorProps {
value?: string | null;
}
export interface ILiteTextEditor extends IEditorProps {
- extensions?: any[];
+ extensions?: Extensions;
}
export interface IRichTextEditor extends IEditorProps {
- extensions?: any[];
+ extensions?: Extensions;
bubbleMenuEnabled?: boolean;
dragDropEnabled?: boolean;
}
@@ -139,6 +144,8 @@ export interface IRichTextEditor extends IEditorProps {
export interface ICollaborativeDocumentEditor
extends Omit {
aiHandler?: TAIHandler;
+ bubbleMenuEnabled?: boolean;
+ editable: boolean;
embedHandler: TEmbedConfig;
handleEditorReady?: (value: boolean) => void;
id: string;
@@ -153,13 +160,11 @@ export interface IReadOnlyEditorProps {
disabledExtensions: TExtensions[];
displayConfig?: TDisplayConfig;
editorClassName?: string;
- fileHandler: Pick;
+ fileHandler: TReadOnlyFileHandler;
forwardedRef?: React.MutableRefObject;
id: string;
initialValue: string;
- mentionHandler: {
- highlights: () => Promise;
- };
+ mentionHandler: TReadOnlyMentionHandler;
}
export type ILiteTextReadOnlyEditor = IReadOnlyEditorProps;
@@ -189,7 +194,5 @@ export type TUserDetails = {
export type TRealtimeConfig = {
url: string;
- queryParams: {
- [key: string]: string;
- };
+ queryParams: TWebhookConnectionQueryParams;
};
diff --git a/packages/editor/src/core/types/image.ts b/packages/editor/src/core/types/image.ts
index 5c707bf33..ca6f76fb1 100644
--- a/packages/editor/src/core/types/image.ts
+++ b/packages/editor/src/core/types/image.ts
@@ -2,4 +2,4 @@ export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise;
export type RestoreImage = (assetUrlWithWorkspaceId: string) => Promise;
-export type UploadImage = (file: File) => Promise;
+export type UploadImage = (blockId: string, file: File) => Promise;
diff --git a/packages/editor/src/core/types/index.ts b/packages/editor/src/core/types/index.ts
index 527264d39..e99a74b28 100644
--- a/packages/editor/src/core/types/index.ts
+++ b/packages/editor/src/core/types/index.ts
@@ -5,7 +5,7 @@ export * from "./editor";
export * from "./embed";
export * from "./extensions";
export * from "./image";
-export * from "./mention-suggestion";
+export * from "./mention";
export * from "./slash-commands-suggestion";
export * from "@/plane-editor/types";
export * from "./document-collaborative-events";
diff --git a/packages/editor/src/core/types/mention-suggestion.ts b/packages/editor/src/core/types/mention-suggestion.ts
deleted file mode 100644
index a51bed704..000000000
--- a/packages/editor/src/core/types/mention-suggestion.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export type IMentionSuggestion = {
- id: string;
- type: string;
- entity_name: string;
- entity_identifier: string;
- avatar: string;
- title: string;
- subtitle: string;
- redirect_uri: string;
-};
-
-export type IMentionHighlight = string;
diff --git a/packages/editor/src/core/types/mention.ts b/packages/editor/src/core/types/mention.ts
new file mode 100644
index 000000000..20f1ec0dc
--- /dev/null
+++ b/packages/editor/src/core/types/mention.ts
@@ -0,0 +1,27 @@
+// plane types
+import { TSearchEntities } from "@plane/types";
+
+export type TMentionSuggestion = {
+ entity_identifier: string;
+ entity_name: TSearchEntities;
+ icon: React.ReactNode;
+ id: string;
+ subTitle?: string;
+ title: string;
+};
+
+export type TMentionSection = {
+ key: string;
+ title?: string;
+ items: TMentionSuggestion[];
+};
+
+export type TMentionComponentProps = Pick;
+
+export type TReadOnlyMentionHandler = {
+ renderComponent: (props: TMentionComponentProps) => React.ReactNode;
+};
+
+export type TMentionHandler = TReadOnlyMentionHandler & {
+ searchCallback?: (query: string) => Promise;
+};
diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts
index ed7d91346..a2a9afaf9 100644
--- a/packages/editor/src/index.ts
+++ b/packages/editor/src/index.ts
@@ -9,7 +9,6 @@ import "./styles/drag-drop.css";
// editors
export {
CollaborativeDocumentEditorWithRef,
- CollaborativeDocumentReadOnlyEditorWithRef,
DocumentReadOnlyEditorWithRef,
LiteTextEditorWithRef,
LiteTextReadOnlyEditorWithRef,
@@ -25,7 +24,7 @@ export * from "@/constants/common";
// helpers
export * from "@/helpers/common";
export * from "@/helpers/editor-commands";
-export * from "@/helpers/yjs";
+export * from "@/helpers/yjs-utils";
export * from "@/extensions/table/table";
// components
diff --git a/packages/editor/src/lib.ts b/packages/editor/src/lib.ts
index e32fa0785..44388a00e 100644
--- a/packages/editor/src/lib.ts
+++ b/packages/editor/src/lib.ts
@@ -1,4 +1,5 @@
export * from "@/extensions/core-without-props";
export * from "@/constants/document-collaborative-events";
export * from "@/helpers/get-document-server-event";
+export * from "@/helpers/yjs-utils";
export * from "@/types/document-collaborative-events";
diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css
index db60c7cf5..69bb61961 100644
--- a/packages/editor/src/styles/editor.css
+++ b/packages/editor/src/styles/editor.css
@@ -25,7 +25,7 @@
.ProseMirror p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
float: left;
- color: rgb(var(--color-text-400));
+ color: var(--color-placeholder);
pointer-events: none;
height: 0;
}
@@ -34,7 +34,7 @@
.ProseMirror p.is-empty::before {
content: attr(data-placeholder);
float: left;
- color: rgb(var(--color-text-400));
+ color: var(--color-placeholder);
pointer-events: none;
height: 0;
}
@@ -105,23 +105,27 @@ ul[data-type="taskList"] li > div {
}
ul[data-type="taskList"] li > label input[type="checkbox"] {
- border: 1px solid rgba(var(--color-border-300)) !important;
+ border: 1px solid rgba(var(--color-text-100), 0.2) !important;
outline: none;
border-radius: 2px;
transform: scale(1.05);
}
-ul[data-type="taskList"] li > label input[type="checkbox"]:hover {
- background-color: rgba(var(--color-background-80)) !important;
+.ProseMirror[contenteditable="true"] input[type="checkbox"]:hover {
+ background-color: rgba(var(--color-text-100), 0.1);
}
-ul[data-type="taskList"] li > label input[type="checkbox"][checked] {
+.ProseMirror[contenteditable="false"] input[type="checkbox"] {
+ pointer-events: none;
+}
+
+ul[data-type="taskList"] li > label input[type="checkbox"]:checked {
background-color: rgba(var(--color-primary-100)) !important;
border-color: rgba(var(--color-primary-100)) !important;
color: white !important;
}
-ul[data-type="taskList"] li > label input[type="checkbox"][checked]:hover {
+ul[data-type="taskList"] li > label input[type="checkbox"]:checked:hover {
background-color: rgba(var(--color-primary-300)) !important;
border-color: rgba(var(--color-primary-300)) !important;
}
@@ -141,7 +145,7 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
position: relative;
-webkit-appearance: none;
appearance: none;
- background-color: rgb(var(--color-background-100));
+ background-color: transparent;
margin: 0;
cursor: pointer;
width: 0.8rem;
@@ -151,10 +155,6 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
margin-right: 0.2rem;
margin-top: 0.15rem;
- &:hover {
- background-color: rgb(var(--color-background-80));
- }
-
&:active {
background-color: rgb(var(--color-background-90));
}
@@ -174,7 +174,7 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
- &[checked]::before {
+ &:checked::before {
transform: scale(1) translate(-50%, -50%);
}
}
@@ -192,7 +192,7 @@ ul[data-type="taskList"] li > div {
ul[data-type="taskList"] li[data-checked="true"] {
& > div > p.editor-paragraph-block {
- color: rgb(var(--color-text-400));
+ color: var(--color-placeholder);
}
[data-text-color] {
@@ -408,12 +408,14 @@ p.editor-paragraph-block {
padding-top: 4px;
}
- &:last-child {
- padding-bottom: 4px;
- }
+ &:not(td p.editor-paragraph-block, th p.editor-paragraph-block) {
+ &:last-child {
+ padding-bottom: 4px;
+ }
- &:not(:last-child) {
- padding-bottom: 8px;
+ &:not(:last-child) {
+ padding-bottom: 8px;
+ }
}
font-size: var(--font-size-regular);
diff --git a/packages/editor/src/styles/table.css b/packages/editor/src/styles/table.css
index a0fbbe38f..b4a1ab820 100644
--- a/packages/editor/src/styles/table.css
+++ b/packages/editor/src/styles/table.css
@@ -16,7 +16,7 @@
.table-wrapper table th {
min-width: 1em;
border: 1px solid rgba(var(--color-border-200));
- padding: 10px 20px;
+ padding: 7px 10px;
vertical-align: top;
box-sizing: border-box;
position: relative;
@@ -48,7 +48,7 @@
/* table dropdown */
.table-wrapper table .column-resize-handle {
position: absolute;
- right: -2px;
+ right: 0;
top: 0;
width: 2px;
height: 100%;
diff --git a/packages/editor/src/styles/variables.css b/packages/editor/src/styles/variables.css
index d25500692..66880f156 100644
--- a/packages/editor/src/styles/variables.css
+++ b/packages/editor/src/styles/variables.css
@@ -1,42 +1,6 @@
-:root {
- /* text colors */
- --editor-colors-gray-text: #5c5e63;
- --editor-colors-peach-text: #ff5b59;
- --editor-colors-pink-text: #f65385;
- --editor-colors-orange-text: #fd9038;
- --editor-colors-green-text: #0fc27b;
- --editor-colors-light-blue-text: #17bee9;
- --editor-colors-dark-blue-text: #266df0;
- --editor-colors-purple-text: #9162f9;
- /* end text colors */
-}
-
-/* text background colors */
-[data-theme="light"],
-[data-theme="light-contrast"] {
- --editor-colors-gray-background: #d6d6d8;
- --editor-colors-peach-background: #ffd5d7;
- --editor-colors-pink-background: #fdd4e3;
- --editor-colors-orange-background: #ffe3cd;
- --editor-colors-green-background: #c3f0de;
- --editor-colors-light-blue-background: #c5eff9;
- --editor-colors-dark-blue-background: #c9dafb;
- --editor-colors-purple-background: #e3d8fd;
-}
-[data-theme="dark"],
-[data-theme="dark-contrast"] {
- --editor-colors-gray-background: #404144;
- --editor-colors-peach-background: #593032;
- --editor-colors-pink-background: #562e3d;
- --editor-colors-orange-background: #583e2a;
- --editor-colors-green-background: #1d4a3b;
- --editor-colors-light-blue-background: #1f495c;
- --editor-colors-dark-blue-background: #223558;
- --editor-colors-purple-background: #3d325a;
-}
-/* end text background colors */
-
.editor-container {
+ --color-placeholder: rgba(var(--color-text-100), 0.5);
+
/* font sizes and line heights */
&.large-font {
--font-size-h1: 1.75rem;
diff --git a/packages/editor/tailwind.config.js b/packages/editor/tailwind.config.js
index f32063158..de93a571f 100644
--- a/packages/editor/tailwind.config.js
+++ b/packages/editor/tailwind.config.js
@@ -1,4 +1,4 @@
-const sharedConfig = require("tailwind-config-custom/tailwind.config.js");
+const sharedConfig = require("@plane/tailwind-config/tailwind.config.js");
module.exports = {
// prefix ui lib classes to avoid conflicting with the app
diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json
index 8edd9106f..58b8640e6 100644
--- a/packages/editor/tsconfig.json
+++ b/packages/editor/tsconfig.json
@@ -12,6 +12,7 @@
"@/styles/*": ["src/styles/*"],
"@/plane-editor/*": ["src/ce/*"]
},
+ "strictNullChecks": true,
"allowSyntheticDefaultImports": true
},
"include": ["src/**/*", "index.d.ts"],
diff --git a/packages/eslint-config/library.js b/packages/eslint-config/library.js
index 283590693..790364230 100644
--- a/packages/eslint-config/library.js
+++ b/packages/eslint-config/library.js
@@ -44,6 +44,30 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-useless-empty-export": "error",
"@typescript-eslint/prefer-ts-expect-error": "warn",
+ "import/order": [
+ "warn",
+ {
+ groups: ["builtin", "external", "internal", "parent", "sibling"],
+ pathGroups: [
+ {
+ pattern: "@plane/**",
+ group: "external",
+ position: "after",
+ },
+ {
+ pattern: "@/**",
+ group: "internal",
+ position: "before",
+ },
+ ],
+ pathGroupsExcludedImportTypes: ["builtin", "internal", "react"],
+ alphabetize: {
+ order: "asc",
+ caseInsensitive: true,
+ },
+ },
+ ],
},
+
ignorePatterns: [".*.js", "node_modules/", "dist/"],
};
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index c07642907..11c33cfa4 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -1,2 +1,3 @@
export * from "./use-local-storage";
export * from "./use-outside-click-detector";
+export * from "./use-platform-os";
diff --git a/packages/hooks/src/use-outside-click-detector.tsx b/packages/hooks/src/use-outside-click-detector.tsx
index 9436b51bf..54979f96a 100644
--- a/packages/hooks/src/use-outside-click-detector.tsx
+++ b/packages/hooks/src/use-outside-click-detector.tsx
@@ -8,9 +8,9 @@ export const useOutsideClickDetector = (
const handleClick = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as any)) {
// check for the closest element with attribute name data-prevent-outside-click
- const preventOutsideClickElement = (
- event.target as unknown as HTMLElement | undefined
- )?.closest("[data-prevent-outside-click]");
+ const preventOutsideClickElement = (event.target as unknown as HTMLElement | undefined)?.closest(
+ "[data-prevent-outside-click]"
+ );
// if the closest element with attribute name data-prevent-outside-click is found, return
if (preventOutsideClickElement) {
return;
diff --git a/packages/hooks/src/use-platform-os.tsx b/packages/hooks/src/use-platform-os.tsx
new file mode 100644
index 000000000..3f62e1499
--- /dev/null
+++ b/packages/hooks/src/use-platform-os.tsx
@@ -0,0 +1,34 @@
+import { useState, useEffect } from "react";
+
+export const usePlatformOS = () => {
+ const [platformData, setPlatformData] = useState({
+ isMobile: false,
+ platform: "",
+ });
+
+ useEffect(() => {
+ const detectPlatform = () => {
+ const userAgent = window.navigator.userAgent;
+ const isMobile = /iPhone|iPad|iPod|Android/i.test(userAgent);
+ let platform = "";
+
+ if (!isMobile) {
+ if (userAgent.indexOf("Win") !== -1) {
+ platform = "Windows";
+ } else if (userAgent.indexOf("Mac") !== -1) {
+ platform = "MacOS";
+ } else if (userAgent.indexOf("Linux") !== -1) {
+ platform = "Linux";
+ } else {
+ platform = "Unknown";
+ }
+ }
+
+ setPlatformData({ isMobile, platform });
+ };
+
+ detectPlatform();
+ }, []);
+
+ return platformData;
+};
diff --git a/packages/i18n/.eslintignore b/packages/i18n/.eslintignore
new file mode 100644
index 000000000..6019047c3
--- /dev/null
+++ b/packages/i18n/.eslintignore
@@ -0,0 +1,3 @@
+build/*
+dist/*
+out/*
\ No newline at end of file
diff --git a/packages/i18n/.eslintrc.js b/packages/i18n/.eslintrc.js
new file mode 100644
index 000000000..558b8f76e
--- /dev/null
+++ b/packages/i18n/.eslintrc.js
@@ -0,0 +1,9 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@plane/eslint-config/library.js"],
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
+};
diff --git a/packages/i18n/.prettierignore b/packages/i18n/.prettierignore
new file mode 100644
index 000000000..d5be669c5
--- /dev/null
+++ b/packages/i18n/.prettierignore
@@ -0,0 +1,4 @@
+.turbo
+out/
+dist/
+build/
\ No newline at end of file
diff --git a/packages/i18n/.prettierrc b/packages/i18n/.prettierrc
new file mode 100644
index 000000000..87d988f1b
--- /dev/null
+++ b/packages/i18n/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "tabWidth": 2,
+ "trailingComma": "es5"
+}
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
new file mode 100644
index 000000000..96b488024
--- /dev/null
+++ b/packages/i18n/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@plane/i18n",
+ "version": "0.24.1",
+ "description": "I18n shared across multiple apps internally",
+ "private": true,
+ "main": "./src/index.ts",
+ "types": "./src/index.ts",
+ "scripts": {
+ "lint": "eslint src --ext .ts,.tsx",
+ "lint:errors": "eslint src --ext .ts,.tsx --quiet"
+ },
+ "dependencies": {
+ "@plane/utils": "*",
+ "intl-messageformat": "^10.7.11"
+ },
+ "devDependencies": {
+ "@plane/eslint-config": "*",
+ "@types/node": "^22.5.4",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/packages/i18n/src/constants/index.ts b/packages/i18n/src/constants/index.ts
new file mode 100644
index 000000000..1f0daf2fc
--- /dev/null
+++ b/packages/i18n/src/constants/index.ts
@@ -0,0 +1 @@
+export * from "./language";
diff --git a/packages/i18n/src/constants/language.ts b/packages/i18n/src/constants/language.ts
new file mode 100644
index 000000000..67b8e9d3f
--- /dev/null
+++ b/packages/i18n/src/constants/language.ts
@@ -0,0 +1,13 @@
+import { TLanguage, ILanguageOption } from "../types";
+
+export const FALLBACK_LANGUAGE: TLanguage = "en";
+
+export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
+ { label: "English", value: "en" },
+ { label: "Français", value: "fr" },
+ { label: "Español", value: "es" },
+ { label: "日本語", value: "ja" },
+ { label: "中文", value: "zh-CN" },
+];
+
+export const STORAGE_KEY = "userLanguage";
diff --git a/packages/i18n/src/context/index.tsx b/packages/i18n/src/context/index.tsx
new file mode 100644
index 000000000..cf8a960fe
--- /dev/null
+++ b/packages/i18n/src/context/index.tsx
@@ -0,0 +1,19 @@
+import { observer } from "mobx-react";
+import React, { createContext } from "react";
+// store
+import { TranslationStore } from "../store";
+
+export const TranslationContext = createContext(null);
+
+interface TranslationProviderProps {
+ children: React.ReactNode;
+}
+
+/**
+ * Provides the translation store to the application
+ */
+export const TranslationProvider: React.FC = observer(({ children }) => {
+ const [store] = React.useState(() => new TranslationStore());
+
+ return {children} ;
+});
diff --git a/packages/i18n/src/hooks/index.ts b/packages/i18n/src/hooks/index.ts
new file mode 100644
index 000000000..fb4e297e2
--- /dev/null
+++ b/packages/i18n/src/hooks/index.ts
@@ -0,0 +1 @@
+export * from "./use-translation";
diff --git a/packages/i18n/src/hooks/use-translation.ts b/packages/i18n/src/hooks/use-translation.ts
new file mode 100644
index 000000000..485c437c1
--- /dev/null
+++ b/packages/i18n/src/hooks/use-translation.ts
@@ -0,0 +1,35 @@
+import { useContext } from 'react';
+// context
+import { TranslationContext } from '../context';
+// types
+import { ILanguageOption, TLanguage } from '../types';
+
+export type TTranslationStore = {
+ t: (key: string, params?: Record) => string;
+ currentLocale: TLanguage;
+ changeLanguage: (lng: TLanguage) => void;
+ languages: ILanguageOption[];
+};
+
+/**
+ * Provides the translation store to the application
+ * @returns {TTranslationStore}
+ * @returns {(key: string, params?: Record) => string} t: method to translate the key with params
+ * @returns {TLanguage} currentLocale - current locale language
+ * @returns {(lng: TLanguage) => void} changeLanguage - method to change the language
+ * @returns {ILanguageOption[]} languages - available languages
+ * @throws {Error} if the TranslationProvider is not used
+ */
+export function useTranslation(): TTranslationStore {
+ const store = useContext(TranslationContext);
+ if (!store) {
+ throw new Error('useTranslation must be used within a TranslationProvider');
+ }
+
+ return {
+ t: store.t.bind(store),
+ currentLocale: store.currentLocale,
+ changeLanguage: (lng: TLanguage) => store.setLanguage(lng),
+ languages: store.availableLanguages,
+ };
+}
diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts
new file mode 100644
index 000000000..a93747a89
--- /dev/null
+++ b/packages/i18n/src/index.ts
@@ -0,0 +1,4 @@
+export * from "./constants";
+export * from "./context";
+export * from "./hooks";
+export * from "./types";
diff --git a/packages/i18n/src/locales/en/core.json b/packages/i18n/src/locales/en/core.json
new file mode 100644
index 000000000..14207c5a8
--- /dev/null
+++ b/packages/i18n/src/locales/en/core.json
@@ -0,0 +1,171 @@
+{
+ "sidebar": {
+ "projects": "Projects",
+ "pages": "Pages",
+ "new_work_item": "New work item",
+ "home": "Home",
+ "your_work": "Your work",
+ "inbox": "Inbox",
+ "workspace": "Workspace",
+ "views": "Views",
+ "analytics": "Analytics",
+ "work_items": "Work Items",
+ "cycles": "Cycles",
+ "modules": "Modules",
+ "intake": "Intake",
+ "drafts": "Drafts",
+ "favorites": "Favorites",
+ "pro": "Pro",
+ "upgrade": "Upgrade"
+ },
+
+ "auth": {
+ "common": {
+ "email": {
+ "label": "Email",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "Email is required",
+ "invalid": "Email is invalid"
+ }
+ },
+ "password": {
+ "label": "Password",
+ "set_password": "Set a password",
+ "placeholder": "Enter password",
+ "confirm_password": {
+ "label": "Confirm password",
+ "placeholder": "Confirm password"
+ },
+ "current_password": {
+ "label": "Current password"
+ },
+ "new_password": {
+ "label": "New password",
+ "placeholder": "Enter new password"
+ },
+ "change_password": {
+ "label": {
+ "default": "Change password",
+ "submitting": "Changing password"
+ }
+ },
+ "errors": {
+ "match": "Passwords don't match",
+ "empty": "Please enter your password",
+ "length": "Password length should me more than 8 characters",
+ "strength": {
+ "weak": "Password is weak",
+ "strong": "Password is strong"
+ }
+ },
+ "submit": "Set password",
+ "toast": {
+ "change_password": {
+ "success": {
+ "title": "Success!",
+ "message": "Password changed successfully."
+ },
+ "error": {
+ "title": "Error!",
+ "message": "Something went wrong. Please try again."
+ }
+ }
+ }
+ },
+ "unique_code": {
+ "label": "Unique code",
+ "placeholder": "gets-sets-flys",
+ "paste_code": "Paste the code sent to your email",
+ "requesting_new_code": "Requesting new code",
+ "sending_code": "Sending code"
+ },
+ "already_have_an_account": "Already have an account?",
+ "login": "Log in",
+ "create_account": "Create an account",
+ "new_to_plane": "New to Plane?",
+ "back_to_sign_in": "Back to sign in",
+ "resend_in": "Resend in {seconds} seconds",
+ "sign_in_with_unique_code": "Sign in with unique code",
+ "forgot_password": "Forgot your password?"
+ },
+ "sign_up": {
+ "header": {
+ "label": "Create an account to start managing work with your team.",
+ "step": {
+ "email": {
+ "header": "Sign up",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "Sign up",
+ "sub_header": "Sign up using an email-password combination."
+ },
+ "unique_code": {
+ "header": "Sign up",
+ "sub_header": "Sign up using a unique code sent to the email address above."
+ }
+ }
+ },
+ "errors": {
+ "password": {
+ "strength": "Try setting-up a strong password to proceed"
+ }
+ }
+ },
+ "sign_in": {
+ "header": {
+ "label": "Log in to start managing work with your team.",
+ "step": {
+ "email": {
+ "header": "Log in or sign up",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "Log in or sign up",
+ "sub_header": "Use your email-password combination to log in."
+ },
+ "unique_code": {
+ "header": "Log in or sign up",
+ "sub_header": "Log in using a unique code sent to the email address above."
+ }
+ }
+ }
+ },
+ "forgot_password": {
+ "title": "Reset your password",
+ "description": "Enter your user account's verified email address and we will send you a password reset link.",
+ "email_sent": "We sent the reset link to your email address",
+ "send_reset_link": "Send reset link",
+ "errors": {
+ "smtp_not_enabled": "We see that your god hasn't enabled SMTP, we will not be able to send a password reset link"
+ },
+ "toast": {
+ "success": {
+ "title": "Email sent",
+ "message": "Check your inbox for a link to reset your password. If it doesn't appear within a few minutes, check your spam folder."
+ },
+ "error": {
+ "title": "Error!",
+ "message": "Something went wrong. Please try again."
+ }
+ }
+ },
+ "reset_password": {
+ "title": "Set new password",
+ "description": "Secure your account with a strong password"
+ },
+ "set_password": {
+ "title": "Secure your account",
+ "description": "Setting password helps you login securely"
+ },
+ "sign_out": {
+ "toast": {
+ "error": {
+ "title": "Error!",
+ "message": "Failed to sign out. Please try again."
+ }
+ }
+ }
+ }
+}
diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json
new file mode 100644
index 000000000..7c74ff2ee
--- /dev/null
+++ b/packages/i18n/src/locales/en/translations.json
@@ -0,0 +1,2194 @@
+{
+ "submit": "Submit",
+ "cancel": "Cancel",
+ "loading": "Loading",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Info",
+ "close": "Close",
+ "yes": "Yes",
+ "no": "No",
+ "ok": "OK",
+ "name": "Name",
+ "description": "Description",
+ "search": "Search",
+ "add_member": "Add member",
+ "adding_members": "Adding members",
+ "remove_member": "Remove member",
+ "add_members": "Add members",
+ "adding_member": "Adding members",
+ "remove_members": "Remove members",
+ "add": "Add",
+ "adding": "Adding",
+ "remove": "Remove",
+ "add_new": "Add new",
+ "remove_selected": "Remove selected",
+ "first_name": "First name",
+ "last_name": "Last name",
+ "email": "Email",
+ "display_name": "Display name",
+ "role": "Role",
+ "timezone": "Timezone",
+ "avatar": "Avatar",
+ "cover_image": "Cover image",
+ "password": "Password",
+ "change_cover": "Change cover",
+ "language": "Language",
+ "saving": "Saving",
+ "save_changes": "Save changes",
+ "deactivate_account": "Deactivate account",
+ "deactivate_account_description": "When deactivating an account, all of the data and resources within that account will be permanently removed and cannot be recovered.",
+ "profile_settings": "Profile settings",
+ "your_account": "Your account",
+ "security": "Security",
+ "activity": "Activity",
+ "appearance": "Appearance",
+ "notifications": "Notifications",
+ "workspaces": "Workspaces",
+ "create_workspace": "Create workspace",
+ "invitations": "Invitations",
+ "summary": "Summary",
+ "assigned": "Assigned",
+ "created": "Created",
+ "subscribed": "Subscribed",
+ "you_do_not_have_the_permission_to_access_this_page": "You do not have the permission to access this page.",
+ "something_went_wrong_please_try_again": "Something went wrong. Please try again.",
+ "load_more": "Load more",
+ "select_or_customize_your_interface_color_scheme": "Select or customize your interface color scheme.",
+ "theme": "Theme",
+ "system_preference": "System preference",
+ "light": "Light",
+ "dark": "Dark",
+ "light_contrast": "Light high contrast",
+ "dark_contrast": "Dark high contrast",
+ "custom": "Custom theme",
+ "select_your_theme": "Select your theme",
+ "customize_your_theme": "Customize your theme",
+ "background_color": "Background color",
+ "text_color": "Text color",
+ "primary_color": "Primary(Theme) color",
+ "sidebar_background_color": "Sidebar background color",
+ "sidebar_text_color": "Sidebar text color",
+ "set_theme": "Set theme",
+ "enter_a_valid_hex_code_of_6_characters": "Enter a valid hex code of 6 characters",
+ "background_color_is_required": "Background color is required",
+ "text_color_is_required": "Text color is required",
+ "primary_color_is_required": "Primary color is required",
+ "sidebar_background_color_is_required": "Sidebar background color is required",
+ "sidebar_text_color_is_required": "Sidebar text color is required",
+ "updating_theme": "Updating theme",
+ "theme_updated_successfully": "Theme updated successfully",
+ "failed_to_update_the_theme": "Failed to update the theme",
+ "email_notifications": "Email notifications",
+ "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "Stay in the loop on Work items you are subscribed to. Enable this to get notified.",
+ "email_notification_setting_updated_successfully": "Email notification setting updated successfully",
+ "failed_to_update_email_notification_setting": "Failed to update email notification setting",
+ "notify_me_when": "Notify me when",
+ "property_changes": "Property changes",
+ "property_changes_description": "Notify me when work items' properties like assignees, priority, estimates or anything else changes.",
+ "state_change": "State change",
+ "state_change_description": "Notify me when the work items moves to a different state",
+ "issue_completed": "Work item completed",
+ "issue_completed_description": "Notify me only when a work item is completed",
+ "comments": "Comments",
+ "comments_description": "Notify me when someone leaves a comment on the work item",
+ "mentions": "Mentions",
+ "mentions_description": "Notify me only when someone mentions me in the comments or description",
+ "old_password": "Old password",
+ "general_settings": "General settings",
+ "sign_out": "Sign out",
+ "signing_out": "Signing out",
+ "active_cycles": "Active cycles",
+ "active_cycles_description": "Monitor cycles across projects, track high-priority work items, and zoom in cycles that need attention.",
+ "on_demand_snapshots_of_all_your_cycles": "On-demand snapshots of all your cycles",
+ "upgrade": "Upgrade",
+ "10000_feet_view": "10,000-feet view of all active cycles.",
+ "10000_feet_view_description": "Zoom out to see running cycles across all your projects at once instead of going from Cycle to Cycle in each project.",
+ "get_snapshot_of_each_active_cycle": "Get a snapshot of each active cycle.",
+ "get_snapshot_of_each_active_cycle_description": "Track high-level metrics for all active cycles, see their state of progress, and get a sense of scope against deadlines.",
+ "compare_burndowns": "Compare burndowns.",
+ "compare_burndowns_description": "Monitor how each of your teams are performing with a peek into each cycle's burndown report.",
+ "quickly_see_make_or_break_issues": "Quickly see make-or-break work items.",
+ "quickly_see_make_or_break_issues_description": "Preview high-priority work items for each cycle against due dates. See all of them per cycle in one click.",
+ "zoom_into_cycles_that_need_attention": "Zoom into cycles that need attention.",
+ "zoom_into_cycles_that_need_attention_description": "Investigate the state of any cycle that doesn't conform to expectations in one click.",
+ "stay_ahead_of_blockers": "Stay ahead of blockers.",
+ "stay_ahead_of_blockers_description": "Spot challenges from one project to another and see inter-cycle dependencies that aren't obvious from any other view.",
+ "analytics": "Analytics",
+ "workspace_invites": "Workspace invites",
+ "enter_god_mode": "Enter god mode",
+ "workspace_logo": "Workspace logo",
+ "new_issue": "New work item",
+ "your_work": "Your work",
+ "drafts": "Drafts",
+ "projects": "Projects",
+ "views": "Views",
+ "workspace": "Workspace",
+ "archives": "Archives",
+ "settings": "Settings",
+ "failed_to_move_favorite": "Failed to move favorite",
+ "favorites": "Favorites",
+ "no_favorites_yet": "No favorites yet",
+ "create_folder": "Create folder",
+ "new_folder": "New folder",
+ "favorite_updated_successfully": "Favorite updated successfully",
+ "favorite_created_successfully": "Favorite created successfully",
+ "folder_already_exists": "Folder already exists",
+ "folder_name_cannot_be_empty": "Folder name cannot be empty",
+ "something_went_wrong": "Something went wrong",
+ "failed_to_reorder_favorite": "Failed to reorder favorite",
+ "favorite_removed_successfully": "Favorite removed successfully",
+ "failed_to_create_favorite": "Failed to create favorite",
+ "failed_to_rename_favorite": "Failed to rename favorite",
+ "project_link_copied_to_clipboard": "Project link copied to clipboard",
+ "link_copied": "Link copied",
+ "add_project": "Add project",
+ "create_project": "Create project",
+ "failed_to_remove_project_from_favorites": "Couldn't remove the project from favorites. Please try again.",
+ "project_created_successfully": "Project created successfully",
+ "project_created_successfully_description": "Project created successfully. You can now start adding work items to it.",
+ "project_cover_image_alt": "Project cover image",
+ "name_is_required": "Name is required",
+ "title_should_be_less_than_255_characters": "Title should be less than 255 characters",
+ "project_name": "Project name",
+ "project_id_must_be_at_least_1_character": "Project ID must at least be of 1 character",
+ "project_id_must_be_at_most_5_characters": "Project ID must at most be of 5 characters",
+ "project_id": "Project ID",
+ "project_id_tooltip_content": "Helps you identify work items in the project uniquely. Max 5 characters.",
+ "description_placeholder": "Description",
+ "only_alphanumeric_non_latin_characters_allowed": "Only Alphanumeric & Non-latin characters are allowed.",
+ "project_id_is_required": "Project ID is required",
+ "project_id_allowed_char": "Only Alphanumeric & Non-latin characters are allowed.",
+ "project_id_min_char": "Project ID must at least be of 1 character",
+ "project_id_max_char": "Project ID must at most be of 5 characters",
+ "project_description_placeholder": "Enter project description",
+ "select_network": "Select network",
+ "lead": "Lead",
+ "date_range": "Date range",
+ "private": "Private",
+ "public": "Public",
+ "accessible_only_by_invite": "Accessible only by invite",
+ "anyone_in_the_workspace_except_guests_can_join": "Anyone in the workspace except Guests can join",
+ "creating": "Creating",
+ "creating_project": "Creating project",
+ "adding_project_to_favorites": "Adding project to favorites",
+ "project_added_to_favorites": "Project added to favorites",
+ "couldnt_add_the_project_to_favorites": "Couldn't add the project to favorites. Please try again.",
+ "removing_project_from_favorites": "Removing project from favorites",
+ "project_removed_from_favorites": "Project removed from favorites",
+ "couldnt_remove_the_project_from_favorites": "Couldn't remove the project from favorites. Please try again.",
+ "add_to_favorites": "Add to favorites",
+ "remove_from_favorites": "Remove from favorites",
+ "publish_settings": "Publish settings",
+ "publish": "Publish",
+ "copy_link": "Copy link",
+ "leave_project": "Leave project",
+ "join_the_project_to_rearrange": "Join the project to rearrange",
+ "drag_to_rearrange": "Drag to rearrange",
+ "congrats": "Congrats!",
+ "open_project": "Open project",
+ "issues": "Work items",
+ "cycles": "Cycles",
+ "modules": "Modules",
+ "pages": "Pages",
+ "intake": "Intake",
+ "time_tracking": "Time Tracking",
+ "work_management": "Work management",
+ "projects_and_issues": "Projects and work items",
+ "projects_and_issues_description": "Toggle these on or off this project.",
+ "cycles_description": "Timebox work as you see fit per project and change frequency from one period to the next.",
+ "modules_description": "Group work into sub-project-like set-ups with their own leads and assignees.",
+ "views_description": "Save sorts, filters, and display options for later or share them.",
+ "pages_description": "Write anything like you write anything.",
+ "intake_description": "Stay in the loop on Work items you are subscribed to. Enable this to get notified.",
+ "time_tracking_description": "Track time spent on work items and projects.",
+ "work_management_description": "Manage your work and projects with ease.",
+ "documentation": "Documentation",
+ "message_support": "Message support",
+ "contact_sales": "Contact sales",
+ "hyper_mode": "Hyper Mode",
+ "keyboard_shortcuts": "Keyboard shortcuts",
+ "whats_new": "What's new?",
+ "version": "Version",
+ "we_are_having_trouble_fetching_the_updates": "We are having trouble fetching the updates.",
+ "our_changelogs": "our changelogs",
+ "for_the_latest_updates": "for the latest updates.",
+ "please_visit": "Please visit",
+ "docs": "Docs",
+ "full_changelog": "Full changelog",
+ "support": "Support",
+ "discord": "Discord",
+ "powered_by_plane_pages": "Powered by Plane Pages",
+ "please_select_at_least_one_invitation": "Please select at least one invitation.",
+ "please_select_at_least_one_invitation_description": "Please select at least one invitation to join the workspace.",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace": "We see that someone has invited you to join a workspace",
+ "join_a_workspace": "Join a workspace",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "We see that someone has invited you to join a workspace",
+ "join_a_workspace_description": "Join a workspace",
+ "accept_and_join": "Accept & Join",
+ "go_home": "Go Home",
+ "no_pending_invites": "No pending invites",
+ "you_can_see_here_if_someone_invites_you_to_a_workspace": "You can see here if someone invites you to a workspace",
+ "back_to_home": "Back to home",
+ "workspace_name": "workspace-name",
+ "deactivate_your_account": "Deactivate your account",
+ "deactivate_your_account_description": "Once deactivated, you can't be assigned work items and be billed for your workspace. To reactivate your account, you will need an invite to a workspace at this email address.",
+ "deactivating": "Deactivating",
+ "confirm": "Confirm",
+ "confirming": "Confirming",
+ "draft_created": "Draft created",
+ "issue_created_successfully": "Work item created successfully",
+ "draft_creation_failed": "Draft creation failed",
+ "issue_creation_failed": "Work item creation failed",
+ "draft_issue": "Draft work item",
+ "issue_updated_successfully": "Work item updated successfully",
+ "issue_could_not_be_updated": "Work item could not be updated",
+ "create_a_draft": "Create a draft",
+ "save_to_drafts": "Save to Drafts",
+ "save": "Save",
+ "update": "Update",
+ "updating": "Updating",
+ "create_new_issue": "Create new work item",
+ "editor_is_not_ready_to_discard_changes": "Editor is not ready to discard changes",
+ "failed_to_move_issue_to_project": "Failed to move work item to project",
+ "create_more": "Create more",
+ "add_to_project": "Add to project",
+ "discard": "Discard",
+ "duplicate_issue_found": "Duplicate work item found",
+ "duplicate_issues_found": "Duplicate work items found",
+ "no_matching_results": "No matching results",
+ "title_is_required": "Title is required",
+ "title": "Title",
+ "state": "State",
+ "priority": "Priority",
+ "none": "None",
+ "urgent": "Urgent",
+ "high": "High",
+ "medium": "Medium",
+ "low": "Low",
+ "members": "Members",
+ "assignee": "Assignee",
+ "assignees": "Assignees",
+ "you": "You",
+ "labels": "Labels",
+ "create_new_label": "Create new label",
+ "start_date": "Start date",
+ "end_date": "End date",
+ "due_date": "Due date",
+ "estimate": "Estimate",
+ "change_parent_issue": "Change parent work item",
+ "remove_parent_issue": "Remove parent work item",
+ "add_parent": "Add parent",
+ "loading_members": "Loading members",
+ "view_link_copied_to_clipboard": "View link copied to clipboard.",
+ "required": "Required",
+ "optional": "Optional",
+ "Cancel": "Cancel",
+ "edit": "Edit",
+ "archive": "Archive",
+ "restore": "Restore",
+ "open_in_new_tab": "Open in new tab",
+ "delete": "Delete",
+ "deleting": "Deleting",
+ "make_a_copy": "Make a copy",
+ "move_to_project": "Move to project",
+ "good": "Good",
+ "morning": "morning",
+ "afternoon": "afternoon",
+ "evening": "evening",
+ "show_all": "Show all",
+ "show_less": "Show less",
+ "no_data_yet": "No Data yet",
+ "syncing": "Syncing",
+ "add_work_item": "Add work item",
+ "advanced_description_placeholder": "Press '/' for commands",
+ "create_work_item": "Create work item",
+ "attachments": "Attachments",
+ "declining": "Declining",
+ "declined": "Declined",
+ "decline": "Decline",
+ "unassigned": "Unassigned",
+ "work_items": "Work items",
+ "add_link": "Add link",
+ "points": "Points",
+ "no_assignee": "No assignee",
+ "no_assignees_yet": "No assignees yet",
+ "no_labels_yet": "No labels yet",
+ "ideal": "Ideal",
+ "current": "Current",
+ "no_matching_members": "No matching members",
+ "leaving": "Leaving",
+ "removing": "Removing",
+ "leave": "Leave",
+ "refresh": "Refresh",
+ "refreshing": "Refreshing",
+ "refresh_status": "Refresh status",
+ "prev": "Prev",
+ "next": "Next",
+ "re_generating": "Re-generating",
+ "re_generate": "Re-generate",
+ "re_generate_key": "Re-generate key",
+ "export": "Export",
+ "member": "{count, plural, one{# member} other{# members}}",
+
+ "project_view": {
+ "sort_by": {
+ "created_at": "Created at",
+ "updated_at": "Updated at",
+ "name": "Name"
+ }
+ },
+
+ "toast": {
+ "success": "Success!",
+ "error": "Error!"
+ },
+
+ "links": {
+ "toasts": {
+ "created": {
+ "title": "Link created",
+ "message": "The link has been successfully created"
+ },
+ "not_created": {
+ "title": "Link not created",
+ "message": "The link could not be created"
+ },
+ "updated": {
+ "title": "Link updated",
+ "message": "The link has been successfully updated"
+ },
+ "not_updated": {
+ "title": "Link not updated",
+ "message": "The link could not be updated"
+ },
+ "removed": {
+ "title": "Link removed",
+ "message": "The link has been successfully removed"
+ },
+ "not_removed": {
+ "title": "Link not removed",
+ "message": "The link could not be removed"
+ }
+ }
+ },
+
+ "home": {
+ "empty": {
+ "quickstart_guide": "Your quickstart guide",
+ "not_right_now": "Not right now",
+ "create_project": {
+ "title": "Create a project",
+ "description": "Most things start with a project in Plane.",
+ "cta": "Get started"
+ },
+ "invite_team": {
+ "title": "Invite your team",
+ "description": "Build, ship, and manage with coworkers.",
+ "cta": "Get them in"
+ },
+ "configure_workspace": {
+ "title": "Set up your workspace.",
+ "description": "Turn features on or off or go beyond that.",
+ "cta": "Configure this workspace"
+ },
+ "personalize_account": {
+ "title": "Make Plane yours.",
+ "description": "Choose your picture, colors, and more.",
+ "cta": "Personalize now"
+ },
+ "widgets": {
+ "title": "It's Quiet Without Widgets, Turn Them On",
+ "description": "It looks like all your widgets are turned off. Enable them\nnow to enhance your experience!",
+ "primary_button": {
+ "text": "Manage widgets"
+ }
+ }
+ },
+ "quick_links": {
+ "empty": "Save links to work things that you'd like handy.",
+ "add": "Add quick Link",
+ "title": "Quicklink",
+ "title_plural": "Quicklinks"
+ },
+ "recents": {
+ "title": "Recents",
+ "empty": {
+ "project": "Your recent projects will appear here once you visit one.",
+ "page": "Your recent pages will appear here once you visit one.",
+ "issue": "Your recent work items will appear here once you visit one.",
+ "default": "You don't have any recents yet."
+ },
+ "filters": {
+ "all": "All items",
+ "projects": "Projects",
+ "pages": "Pages",
+ "issues": "Work items"
+ }
+ },
+ "new_at_plane": {
+ "title": "New at Plane"
+ },
+ "quick_tutorial": {
+ "title": "Quick tutorial"
+ },
+ "widget": {
+ "reordered_successfully": "Widget reordered successfully.",
+ "reordering_failed": "Error occurred while reordering widget."
+ },
+ "manage_widgets": "Manage widgets",
+ "title": "Home",
+ "star_us_on_github": "Star us on GitHub"
+ },
+
+ "link": {
+ "modal": {
+ "url": {
+ "text": "URL",
+ "required": "URL is invalid",
+ "placeholder": "Type or paste a URL"
+ },
+ "title": {
+ "text": "Display title",
+ "placeholder": "What you'd like to see this link as"
+ }
+ }
+ },
+
+ "common": {
+ "all": "All",
+ "states": "States",
+ "state": "State",
+ "state_groups": "State groups",
+ "priority": "Priority",
+ "team_project": "Team project",
+ "project": "Project",
+ "cycle": "Cycle",
+ "cycles": "Cycles",
+ "module": "Module",
+ "modules": "Modules",
+ "labels": "Labels",
+ "assignees": "Assignees",
+ "assignee": "Assignee",
+ "created_by": "Created by",
+ "none": "None",
+ "link": "Link",
+ "estimate": "Estimate",
+ "layout": "Layout",
+ "filters": "Filters",
+ "display": "Display",
+ "load_more": "Load more",
+ "activity": "Activity",
+ "analytics": "Analytics",
+ "dates": "Dates",
+ "success": "Success!",
+ "something_went_wrong": "Something went wrong",
+ "error": {
+ "label": "Error!",
+ "message": "Some error occurred. Please try again."
+ },
+ "group_by": "Group by",
+ "epic": "Epic",
+ "epics": "Epics",
+ "work_item": "Work item",
+ "work_items": "Work items",
+ "sub_work_item": "Sub-work item",
+ "add": "Add",
+ "warning": "Warning",
+ "updating": "Updating",
+ "adding": "Adding",
+ "update": "Update",
+ "creating": "Creating",
+ "create": "Create",
+ "cancel": "Cancel",
+ "description": "Description",
+ "title": "Title",
+ "attachment": "Attachment",
+ "general": "General",
+ "features": "Features",
+ "automation": "Automation",
+ "project_name": "Project name",
+ "project_id": "Project ID",
+ "project_timezone": "Project Timezone",
+ "created_on": "Created on",
+ "update_project": "Update project",
+ "identifier_already_exists": "Identifier already exists",
+ "add_more": "Add more",
+ "defaults": "Defaults",
+ "add_label": "Add label",
+ "estimates": "Estimates",
+ "customize_time_range": "Customize time range",
+ "loading": "Loading",
+ "attachments": "Attachments",
+ "property": "Property",
+ "properties": "Properties",
+ "parent": "Parent",
+ "remove": "Remove",
+ "archiving": "Archiving",
+ "archive": "Archive",
+ "access": {
+ "public": "Public",
+ "private": "Private"
+ },
+ "done": "Done",
+ "sub_work_items": "Sub-work items",
+ "comment": "Comment",
+ "workspace_level": "Workspace level",
+ "order_by": {
+ "label": "Order by",
+ "manual": "Manual",
+ "last_created": "Last created",
+ "last_updated": "Last updated",
+ "start_date": "Start date",
+ "due_date": "Due date",
+ "asc": "Ascending",
+ "desc": "Descending",
+ "updated_on": "Updated on"
+ },
+ "sort": {
+ "asc": "Ascending",
+ "desc": "Descending",
+ "created_on": "Created on",
+ "updated_on": "Updated on"
+ },
+ "comments": "Comments",
+ "updates": "Updates",
+ "clear_all": "Clear all",
+ "copied": "Copied!",
+ "link_copied": "Link copied!",
+ "link_copied_to_clipboard": "Link copied to clipboard",
+ "copied_to_clipboard": "Work item link copied to clipboard",
+ "is_copied_to_clipboard": "Work item is copied to clipboard",
+ "no_links_added_yet": "No links added yet",
+ "add_link": "Add link",
+ "links": "Links",
+ "go_to_workspace": "Go to workspace",
+ "progress": "Progress",
+ "optional": "Optional",
+ "join": "Join",
+ "go_back": "Go back",
+ "continue": "Continue",
+ "resend": "Resend",
+ "relations": "Relations",
+ "errors": {
+ "default": {
+ "title": "Error!",
+ "message": "Something went wrong. Please try again."
+ },
+ "required": "This field is required",
+ "entity_required": "{entity} is required"
+ },
+ "update_link": "Update link",
+ "attach": "Attach",
+ "create_new": "Create new",
+ "add_existing": "Add existing",
+ "type_or_paste_a_url": "Type or paste a URL",
+ "url_is_invalid": "URL is invalid",
+ "display_title": "Display title",
+ "link_title_placeholder": "What you'd like to see this link as",
+ "url": "URL",
+ "side_peek": "Side Peek",
+ "modal": "Modal",
+ "full_screen": "Full Screen",
+ "close_peek_view": "Close the peek view",
+ "toggle_peek_view_layout": "Toggle peek view layout",
+ "options": "Options",
+ "duration": "Duration",
+ "today": "Today",
+ "week": "Week",
+ "month": "Month",
+ "quarter": "Quarter",
+ "press_for_commands": "Press '/' for commands",
+ "click_to_add_description": "Click to add description",
+ "search": {
+ "label": "Search",
+ "placeholder": "Type to search",
+ "no_matches_found": "No matches found",
+ "no_matching_results": "No matching results"
+ },
+ "actions": {
+ "edit": "Edit",
+ "make_a_copy": "Make a copy",
+ "open_in_new_tab": "Open in new tab",
+ "copy_link": "Copy link",
+ "archive": "Archive",
+ "restore": "Restore",
+ "delete": "Delete",
+ "remove_relation": "Remove relation",
+ "subscribe": "Subscribe",
+ "unsubscribe": "Unsubscribe",
+ "clear_sorting": "Clear sorting",
+ "show_weekends": "Show weekends",
+ "enable": "Enable",
+ "disable": "Disable"
+ },
+ "name": "Name",
+ "discard": "Discard",
+ "confirm": "Confirm",
+ "confirming": "Confirming",
+ "read_the_docs": "Read the docs",
+ "default": "Default",
+ "active": "Active",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "mandate": "Mandate",
+ "mandatory": "Mandatory",
+ "yes": "Yes",
+ "no": "No",
+ "please_wait": "Please wait",
+ "enabling": "Enabling",
+ "disabling": "Disabling",
+ "beta": "Beta",
+ "or": "or",
+ "next": "Next",
+ "back": "Back",
+ "cancelling": "Cancelling",
+ "configuring": "Configuring",
+ "clear": "Clear",
+ "import": "Import",
+ "connect": "Connect",
+ "authorizing": "Authorizing",
+ "processing": "Processing",
+ "no_data_available": "No data available",
+ "from": "from {name}",
+ "authenticated": "Authenticated",
+ "select": "Select",
+ "upgrade": "Upgrade",
+ "add_seats": "Add Seats",
+ "label": "Label",
+ "priorities": "Priorities",
+ "projects": "Projects",
+ "workspace": "Workspace",
+ "workspaces": "Workspaces",
+ "team": "Team",
+ "teams": "Teams",
+ "entity": "Entity",
+ "entities": "Entities",
+ "task": "Task",
+ "tasks": "Tasks",
+ "section": "Section",
+ "sections": "Sections",
+ "edit": "Edit",
+ "connecting": "Connecting",
+ "connected": "Connected",
+ "disconnect": "Disconnect",
+ "disconnecting": "Disconnecting",
+ "installing": "Installing",
+ "install": "Install",
+ "reset": "Reset",
+ "live": "Live",
+ "change_history": "Change History",
+ "coming_soon": "Coming soon",
+ "members": "Members",
+ "you": "You",
+ "upgrade_cta": {
+ "higher_subscription": "Upgrade to higher subscription",
+ "talk_to_sales": "Talk to sales"
+ },
+ "category": "Category",
+ "categories": "Categories",
+ "saving": "Saving",
+ "save_changes": "Save changes",
+ "delete": "Delete",
+ "deleting": "Deleting",
+ "pending": "Pending",
+ "invite": "Invite"
+ },
+
+ "chart": {
+ "x_axis": "X-axis",
+ "y_axis": "Y-axis",
+ "metric": "Metric"
+ },
+
+ "form": {
+ "title": {
+ "required": "Title is required",
+ "max_length": "Title should be less than {length} characters"
+ }
+ },
+
+ "entity": {
+ "grouping_title": "{entity} Grouping",
+ "priority": "{entity} Priority",
+ "all": "All {entity}",
+ "drop_here_to_move": "Drop here to move the {entity}",
+ "delete": {
+ "label": "Delete {entity}",
+ "success": "{entity} deleted successfully",
+ "failed": "{entity} delete failed"
+ },
+ "update": {
+ "failed": "{entity} update failed",
+ "success": "{entity} updated successfully"
+ },
+ "link_copied_to_clipboard": "{entity} link copied to clipboard",
+ "fetch": {
+ "failed": "Error fetching {entity}"
+ },
+ "add": {
+ "success": "{entity} added successfully",
+ "failed": "Error adding {entity}"
+ }
+ },
+
+ "epic": {
+ "all": "All Epics",
+ "label": "{count, plural, one {Epic} other {Epics}}",
+ "new": "New Epic",
+ "adding": "Adding epic",
+ "create": {
+ "success": "Epic created successfully"
+ },
+ "add": {
+ "press_enter": "Press 'Enter' to add another epic",
+ "label": "Add Epic"
+ },
+ "title": {
+ "label": "Epic Title",
+ "required": "Epic title is required."
+ }
+ },
+
+ "issue": {
+ "label": "{count, plural, one {Work item} other {Work items}}",
+ "all": "All Work items",
+ "edit": "Edit work item",
+ "title": {
+ "label": "Work item title",
+ "required": "Work item title is required."
+ },
+ "add": {
+ "press_enter": "Press 'Enter' to add another work item",
+ "label": "Add work item",
+ "cycle": {
+ "failed": "Work item could not be added to the cycle. Please try again.",
+ "success": "{count, plural, one {Work item} other {Work items}} added to the cycle successfully.",
+ "loading": "Adding {count, plural, one {work item} other {work items}} to the cycle"
+ },
+ "assignee": "Add assignees",
+ "start_date": "Add start date",
+ "due_date": "Add due date",
+ "parent": "Add parent work item",
+ "sub_issue": "Add sub-work item",
+ "relation": "Add relation",
+ "link": "Add link",
+ "existing": "Add existing work item"
+ },
+ "remove": {
+ "label": "Remove work item",
+ "cycle": {
+ "loading": "Removing work item from the cycle",
+ "success": "Work item removed from the cycle successfully.",
+ "failed": "Work item could not be removed from the cycle. Please try again."
+ },
+ "module": {
+ "loading": "Removing work item from the module",
+ "success": "Work item removed from the module successfully.",
+ "failed": "Work item could not be removed from the module. Please try again."
+ },
+ "parent": {
+ "label": "Remove parent work item"
+ }
+ },
+ "new": "New Work item",
+ "adding": "Adding work item",
+ "create": {
+ "success": "Work item created successfully"
+ },
+ "priority": {
+ "urgent": "Urgent",
+ "high": "High",
+ "medium": "Medium",
+ "low": "Low"
+ },
+ "display": {
+ "properties": {
+ "label": "Display Properties",
+ "id": "ID",
+ "issue_type": "Work item Type",
+ "sub_issue_count": "Sub-work item count",
+ "attachment_count": "Attachment count",
+ "created_on": "Created on",
+ "sub_issue": "Sub-work item"
+ },
+ "extra": {
+ "show_sub_issues": "Show sub-work items",
+ "show_empty_groups": "Show empty groups"
+ }
+ },
+ "layouts": {
+ "ordered_by_label": "This layout is ordered by",
+ "list": "List",
+ "kanban": "Board",
+ "calendar": "Calendar",
+ "spreadsheet": "Table",
+ "gantt": "Timeline",
+ "title": {
+ "list": "List Layout",
+ "kanban": "Board Layout",
+ "calendar": "Calendar Layout",
+ "spreadsheet": "Table Layout",
+ "gantt": "Timeline Layout"
+ }
+ },
+ "states": {
+ "active": "Active",
+ "backlog": "Backlog"
+ },
+ "comments": {
+ "placeholder": "Add comment",
+ "switch": {
+ "private": "Switch to private comment",
+ "public": "Switch to public comment"
+ },
+ "create": {
+ "success": "Comment created successfully",
+ "error": "Comment creation failed. Please try again later."
+ },
+ "update": {
+ "success": "Comment updated successfully",
+ "error": "Comment update failed. Please try again later."
+ },
+ "remove": {
+ "success": "Comment removed successfully",
+ "error": "Comment remove failed. Please try again later."
+ },
+ "upload": {
+ "error": "Asset upload failed. Please try again later."
+ }
+ },
+ "empty_state": {
+ "issue_detail": {
+ "title": "Work item does not exist",
+ "description": "The work item you are looking for does not exist, has been archived, or has been deleted.",
+ "primary_button": {
+ "text": "View other work items"
+ }
+ }
+ },
+ "sibling": {
+ "label": "Sibling work items"
+ },
+ "archive": {
+ "description": "Only completed or canceled\nwork items can be archived",
+ "label": "Archive Work item",
+ "confirm_message": "Are you sure you want to archive the work item? All your archived work items can be restored later.",
+ "success": {
+ "label": "Archive success",
+ "message": "Your archives can be found in project archives."
+ },
+ "failed": {
+ "message": "Work item could not be archived. Please try again."
+ }
+ },
+ "restore": {
+ "success": {
+ "title": "Restore success",
+ "message": "Your work item can be found in project work items."
+ },
+ "failed": {
+ "message": "Work item could not be restored. Please try again."
+ }
+ },
+ "relation": {
+ "relates_to": "Relates to",
+ "duplicate": "Duplicate of",
+ "blocked_by": "Blocked by",
+ "blocking": "Blocking"
+ },
+ "copy_link": "Copy work item link",
+ "delete": {
+ "label": "Delete work item",
+ "error": "Error deleting work item"
+ },
+ "subscription": {
+ "actions": {
+ "subscribed": "Work item subscribed successfully",
+ "unsubscribed": "Work item unsubscribed successfully"
+ }
+ },
+ "select": {
+ "error": "Please select at least one work item",
+ "empty": "No work items selected",
+ "add_selected": "Add selected work items"
+ },
+ "open_in_full_screen": "Open work item in full screen"
+ },
+
+ "attachment": {
+ "error": "File could not be attached. Try uploading again.",
+ "only_one_file_allowed": "Only one file can be uploaded at a time.",
+ "file_size_limit": "File must be of {size}MB or less in size.",
+ "drag_and_drop": "Drag and drop anywhere to upload",
+ "delete": "Delete attachment"
+ },
+
+ "label": {
+ "select": "Select label",
+ "create": {
+ "success": "Label created successfully",
+ "failed": "Label creation failed",
+ "already_exists": "Label already exists",
+ "type": "Type to add a new label"
+ }
+ },
+
+ "sub_work_item": {
+ "update": {
+ "success": "Sub-work item updated successfully",
+ "error": "Error updating sub-work item"
+ },
+ "remove": {
+ "success": "Sub-work item removed successfully",
+ "error": "Error removing sub-work item"
+ }
+ },
+
+ "view": {
+ "label": "{count, plural, one {View} other {Views}}",
+ "create": {
+ "label": "Create View"
+ },
+ "update": {
+ "label": "Update View"
+ }
+ },
+
+ "inbox_issue": {
+ "status": {
+ "pending": {
+ "title": "Pending",
+ "description": "Pending"
+ },
+ "declined": {
+ "title": "Declined",
+ "description": "Declined"
+ },
+ "snoozed": {
+ "title": "Snoozed",
+ "description": "{days, plural, one{# day} other{# days}} to go"
+ },
+ "accepted": {
+ "title": "Accepted",
+ "description": "Accepted"
+ },
+ "duplicate": {
+ "title": "Duplicate",
+ "description": "Duplicate"
+ }
+ },
+ "modals": {
+ "decline": {
+ "title": "Decline work item",
+ "content": "Are you sure you want to decline work item {value}?"
+ },
+ "delete": {
+ "title": "Delete work item",
+ "content": "Are you sure you want to delete work item {value}?",
+ "success": "Work item deleted successfully"
+ }
+ },
+ "errors": {
+ "snooze_permission": "Only project admins can snooze/Un-snooze work items",
+ "accept_permission": "Only project admins can accept work items",
+ "decline_permission": "Only project admins can deny work items"
+ },
+ "actions": {
+ "accept": "Accept",
+ "decline": "Decline",
+ "snooze": "Snooze",
+ "unsnooze": "Un snooze",
+ "copy": "Copy work item link",
+ "delete": "Delete",
+ "open": "Open work item",
+ "mark_as_duplicate": "Mark as duplicate",
+ "move": "Move {value} to project work items"
+ },
+ "source": {
+ "in-app": "in-app"
+ },
+ "order_by": {
+ "created_at": "Created at",
+ "updated_at": "Updated at",
+ "id": "ID"
+ },
+ "label": "Intake",
+ "page_label": "{workspace} - Intake",
+ "modal": {
+ "title": "Create intake work item"
+ },
+ "tabs": {
+ "open": "Open",
+ "closed": "Closed"
+ },
+ "empty_state": {
+ "sidebar_open_tab": {
+ "title": "No open work items",
+ "description": "Find open work items here. Create new work item."
+ },
+ "sidebar_closed_tab": {
+ "title": "No closed work items",
+ "description": "All the work items whether accepted or declined can be found here."
+ },
+ "sidebar_filter": {
+ "title": "No matching work items",
+ "description": "No work item matches filter applied in intake. Create a new work item."
+ },
+ "detail": {
+ "title": "Select a work item to view its details."
+ }
+ }
+ },
+
+ "workspace_creation": {
+ "heading": "Create your workspace",
+ "subheading": "To start using Plane, you need to create or join a workspace.",
+ "form": {
+ "name": {
+ "label": "Name your workspace",
+ "placeholder": "Something familiar and recognizable is always best."
+ },
+ "url": {
+ "label": "Set your workspace's URL",
+ "placeholder": "Type or paste a URL",
+ "edit_slug": "You can only edit the slug of the URL"
+ },
+ "organization_size": {
+ "label": "How many people will use this workspace?",
+ "placeholder": "Select a range"
+ }
+ },
+ "errors": {
+ "creation_disabled": {
+ "title": "Only your instance admin can create workspaces",
+ "description": "If you know your instance admin's email address, click the button below to get in touch with them.",
+ "request_button": "Request instance admin"
+ },
+ "validation": {
+ "name_alphanumeric": "Workspaces names can contain only (' '), ('-'), ('_') and alphanumeric characters.",
+ "name_length": "Limit your name to 80 characters.",
+ "url_alphanumeric": "URLs can contain only ('-') and alphanumeric characters.",
+ "url_length": "Limit your URL to 48 characters.",
+ "url_already_taken": "Workspace URL is already taken!"
+ }
+ },
+ "request_email": {
+ "subject": "Requesting a new workspace",
+ "body": "Hi instance admin(s),\n\nPlease create a new workspace with the URL [/workspace-name] for [purpose of creating the workspace].\n\nThanks,\n{firstName} {lastName}\n{email}"
+ },
+ "button": {
+ "default": "Create workspace",
+ "loading": "Creating workspace"
+ },
+ "toast": {
+ "success": {
+ "title": "Success",
+ "message": "Workspace created successfully"
+ },
+ "error": {
+ "title": "Error",
+ "message": "Workspace could not be created. Please try again."
+ }
+ }
+ },
+
+ "workspace_dashboard": {
+ "empty_state": {
+ "general": {
+ "title": "Overview of your projects, activity, and metrics",
+ "description": "Welcome to Plane, we are excited to have you here. Create your first project and track your work items, and this page will transform into a space that helps you progress. Admins will also see items which help their team progress.",
+ "primary_button": {
+ "text": "Build your first project",
+ "comic": {
+ "title": "Everything starts with a project in Plane",
+ "description": "A project could be a product's roadmap, a marketing campaign, or launching a new car."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_analytics": {
+ "label": "Analytics",
+ "page_label": "{workspace} - Analytics",
+ "open_tasks": "Total open tasks",
+ "error": "There was some error in fetching the data.",
+ "work_items_closed_in": "Work items closed in",
+ "selected_projects": "Selected projects",
+ "total_members": "Total members",
+ "total_cycles": "Total cycles",
+ "total_modules": "Total modules",
+ "pending_work_items": {
+ "title": "Pending work items",
+ "empty_state": "Analysis of pending work items by co-workers appears here."
+ },
+ "work_items_closed_in_a_year": {
+ "title": "Work items closed in a year",
+ "empty_state": "Close work items to view analysis of the same in the form of a graph."
+ },
+ "most_work_items_created": {
+ "title": "Most work items created",
+ "empty_state": "Co-workers and the number of work items created by them appears here."
+ },
+ "most_work_items_closed": {
+ "title": "Most work items closed",
+ "empty_state": "Co-workers and the number of work items closed by them appears here."
+ },
+ "tabs": {
+ "scope_and_demand": "Scope and Demand",
+ "custom": "Custom Analytics"
+ },
+ "empty_state": {
+ "general": {
+ "title": "Track progress, workloads, and allocations. Spot trends, remove blockers, and move work faster",
+ "description": "See scope versus demand, estimates, and scope creep. Get performance by team members and teams, and make sure your project runs on time.",
+ "primary_button": {
+ "text": "Start your first project",
+ "comic": {
+ "title": "Analytics works best with Cycles + Modules",
+ "description": "First, timebox your work items into Cycles and, if you can, group work items that span more than a cycle into Modules. Check out both on the left nav."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_projects": {
+ "label": "{count, plural, one {Project} other {Projects}}",
+ "create": {
+ "label": "Add Project"
+ },
+ "network": {
+ "label": "Network",
+ "private": {
+ "title": "Private",
+ "description": "Accessible only by invite"
+ },
+ "public": {
+ "title": "Public",
+ "description": "Anyone in the workspace except Guests can join"
+ }
+ },
+ "error": {
+ "permission": "You don't have permission to perform this action.",
+ "cycle_delete": "Failed to delete cycle",
+ "module_delete": "Failed to delete module",
+ "issue_delete": "Failed to delete work item"
+ },
+ "state": {
+ "backlog": "Backlog",
+ "unstarted": "Unstarted",
+ "started": "Started",
+ "completed": "Completed",
+ "cancelled": "Cancelled"
+ },
+ "sort": {
+ "manual": "Manual",
+ "name": "Name",
+ "created_at": "Created date",
+ "members_length": "Number of members"
+ },
+ "scope": {
+ "my_projects": "My projects",
+ "archived_projects": "Archived"
+ },
+ "common": {
+ "months_count": "{months, plural, one{# month} other{# months}}"
+ },
+ "empty_state": {
+ "general": {
+ "title": "No active projects",
+ "description": "Think of each project as the parent for goal-oriented work. Projects are where Jobs, Cycles, and Modules live and, along with your colleagues, help you achieve that goal. Create a new project or filter for archived projects.",
+ "primary_button": {
+ "text": "Start your first project",
+ "comic": {
+ "title": "Everything starts with a project in Plane",
+ "description": "A project could be a product's roadmap, a marketing campaign, or launching a new car."
+ }
+ }
+ },
+ "no_projects": {
+ "title": "No project",
+ "description": "To create work items or manage your work, you need to create a project or be a part of one.",
+ "primary_button": {
+ "text": "Start your first project",
+ "comic": {
+ "title": "Everything starts with a project in Plane",
+ "description": "A project could be a product's roadmap, a marketing campaign, or launching a new car."
+ }
+ }
+ },
+ "filter": {
+ "title": "No matching projects",
+ "description": "No projects detected with the matching criteria. \n Create a new project instead."
+ },
+ "search": {
+ "description": "No projects detected with the matching criteria.\nCreate a new project instead"
+ }
+ }
+ },
+
+ "workspace_views": {
+ "add_view": "Add view",
+ "empty_state": {
+ "all-issues": {
+ "title": "No work items in the project",
+ "description": "First project done! Now, slice your work into trackable pieces with work items. Let's go!",
+ "primary_button": {
+ "text": "Create new work item"
+ }
+ },
+ "assigned": {
+ "title": "No work items yet",
+ "description": "Work items assigned to you can be tracked from here.",
+ "primary_button": {
+ "text": "Create new work item"
+ }
+ },
+ "created": {
+ "title": "No work items yet",
+ "description": "All work items created by you come here, track them here directly.",
+ "primary_button": {
+ "text": "Create new work item"
+ }
+ },
+ "subscribed": {
+ "title": "No work items yet",
+ "description": "Subscribe to work items you are interested in, track all of them here."
+ },
+ "custom-view": {
+ "title": "No work items yet",
+ "description": "Work items that applies to the filters, track all of them here."
+ }
+ }
+ },
+
+ "workspace_settings": {
+ "label": "Workspace settings",
+ "page_label": "{workspace} - General settings",
+ "key_created": "Key created",
+ "copy_key": "Copy and save this secret key in Plane Pages. You can't see this key after you hit Close. A CSV file containing the key has been downloaded.",
+ "token_copied": "Token copied to clipboard.",
+ "settings": {
+ "general": {
+ "title": "General",
+ "upload_logo": "Upload logo",
+ "edit_logo": "Edit logo",
+ "name": "Workspace name",
+ "company_size": "Company size",
+ "url": "Workspace URL",
+ "update_workspace": "Update workspace",
+ "delete_workspace": "Delete this workspace",
+ "delete_workspace_description": "When deleting a workspace, all of the data and resources within that workspace will be permanently removed and cannot be recovered.",
+ "delete_btn": "Delete this workspace",
+ "delete_modal": {
+ "title": "Are you sure you want to delete this workspace?",
+ "description": "You have an active trial to one of our paid plans. Please cancel it first to proceed.",
+ "dismiss": "Dismiss",
+ "cancel": "Cancel trial",
+ "success_title": "Workspace deleted.",
+ "success_message": "You will soon go to your profile page.",
+ "error_title": "That didn't work.",
+ "error_message": "Try again, please."
+ },
+ "errors": {
+ "name": {
+ "required": "Name is required",
+ "max_length": "Workspace name should not exceed 80 characters"
+ },
+ "company_size": {
+ "required": "Company size is required"
+ }
+ }
+ },
+ "members": {
+ "title": "Members",
+ "add_member": "Add member",
+ "pending_invites": "Pending invites",
+ "invitations_sent_successfully": "Invitations sent successfully",
+ "leave_confirmation": "Are you sure you want to leave the workspace? You will no longer have access to this workspace. This action cannot be undone.",
+ "details": {
+ "full_name": "Full name",
+ "display_name": "Display name",
+ "email_address": "Email address",
+ "account_type": "Account type",
+ "authentication": "Authentication",
+ "joining_date": "Joining date"
+ },
+ "modal": {
+ "title": "Invite people to collaborate",
+ "description": "Invite people to collaborate on your workspace.",
+ "button": "Send invitations",
+ "button_loading": "Sending invitations",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "We need an email address to invite them.",
+ "invalid": "Email is invalid"
+ }
+ }
+ },
+ "billing_and_plans": {
+ "title": "Billing & Plans",
+ "current_plan": "Current plan",
+ "free_plan": "You are currently using the free plan",
+ "view_plans": "View plans"
+ },
+ "exports": {
+ "title": "Exports",
+ "exporting": "Exporting",
+ "previous_exports": "Previous exports",
+ "export_separate_files": "Export the data into separate files",
+ "modal": {
+ "title": "Export to",
+ "toasts": {
+ "success": {
+ "title": "Export successful",
+ "message": "You will be able to download the exported {entity} from the previous export."
+ },
+ "error": {
+ "title": "Export failed",
+ "message": "Export was unsuccessful. Please try again."
+ }
+ }
+ }
+ },
+ "webhooks": {
+ "title": "Webhooks",
+ "add_webhook": "Add webhook",
+ "modal": {
+ "title": "Create webhook",
+ "details": "Webhook details",
+ "payload": "Payload URL",
+ "question": "Which events would you like to trigger this webhook?",
+ "error": "URL is required"
+ },
+ "secret_key": {
+ "title": "Secret key",
+ "message": "Generate a token to sign-in to the webhook payload"
+ },
+ "options": {
+ "all": "Send me everything",
+ "individual": "Select individual events"
+ },
+ "toasts": {
+ "created": {
+ "title": "Webhook created",
+ "message": "The webhook has been successfully created"
+ },
+ "not_created": {
+ "title": "Webhook not created",
+ "message": "The webhook could not be created"
+ },
+ "updated": {
+ "title": "Webhook updated",
+ "message": "The webhook has been successfully updated"
+ },
+ "not_updated": {
+ "title": "Webhook not updated",
+ "message": "The webhook could not be updated"
+ },
+ "removed": {
+ "title": "Webhook removed",
+ "message": "The webhook has been successfully removed"
+ },
+ "not_removed": {
+ "title": "Webhook not removed",
+ "message": "The webhook could not be removed"
+ },
+ "secret_key_copied": {
+ "message": "Secret key copied to clipboard."
+ },
+ "secret_key_not_copied": {
+ "message": "Error occurred while copying secret key."
+ }
+ }
+ },
+ "api_tokens": {
+ "title": "API Tokens",
+ "add_token": "Add API token",
+ "create_token": "Create token",
+ "never_expires": "Never expires",
+ "generate_token": "Generate token",
+ "generating": "Generating",
+ "delete": {
+ "title": "Delete API token",
+ "description": "Any application using this token will no longer have the access to Plane data. This action cannot be undone.",
+ "success": {
+ "title": "Success!",
+ "message": "The API token has been successfully deleted"
+ },
+ "error": {
+ "title": "Error!",
+ "message": "The API token could not be deleted"
+ }
+ }
+ }
+ },
+ "empty_state": {
+ "api_tokens": {
+ "title": "No API tokens created",
+ "description": "Plane APIs can be used to integrate your data in Plane with any external system. Create a token to get started."
+ },
+ "webhooks": {
+ "title": "No webhooks added",
+ "description": "Create webhooks to receive real-time updates and automate actions."
+ },
+ "exports": {
+ "title": "No exports yet",
+ "description": "Anytime you export, you will also have a copy here for reference."
+ },
+ "imports": {
+ "title": "No imports yet",
+ "description": "Find all your previous imports here and download them."
+ }
+ }
+ },
+
+ "profile": {
+ "label": "Profile",
+ "page_label": "Your work",
+ "work": "Work",
+ "details": {
+ "joined_on": "Joined on",
+ "time_zone": "Timezone"
+ },
+ "stats": {
+ "workload": "Workload",
+ "overview": "Overview",
+ "created": "Work items created",
+ "assigned": "Work items assigned",
+ "subscribed": "Work items subscribed",
+ "state_distribution": {
+ "title": "Work items by state",
+ "empty": "Create work items to view the them by states in the graph for better analysis."
+ },
+ "priority_distribution": {
+ "title": "Work items by Priority",
+ "empty": "Create work items to view the them by priority in the graph for better analysis."
+ },
+ "recent_activity": {
+ "title": "Recent activity",
+ "empty": "We couldn't find data. Kindly view your inputs",
+ "button": "Download today's activity",
+ "button_loading": "Downloading"
+ }
+ },
+ "actions": {
+ "profile": "Profile",
+ "security": "Security",
+ "activity": "Activity",
+ "appearance": "Appearance",
+ "notifications": "Notifications"
+ },
+ "tabs": {
+ "summary": "Summary",
+ "assigned": "Assigned",
+ "created": "Created",
+ "subscribed": "Subscribed",
+ "activity": "Activity"
+ },
+ "empty_state": {
+ "activity": {
+ "title": "No activities yet",
+ "description": "Get started by creating a new work item! Add details and properties to it. Explore more in Plane to see your activity."
+ },
+ "assigned": {
+ "title": "No work items are assigned to you",
+ "description": "Work items assigned to you can be tracked from here."
+ },
+ "created": {
+ "title": "No work items yet",
+ "description": "All work items created by you come here, track them here directly."
+ },
+ "subscribed": {
+ "title": "No work items yet",
+ "description": "Subscribe to work items you are interested in, track all of them here."
+ }
+ }
+ },
+
+ "project_settings": {
+ "general": {
+ "enter_project_id": "Enter project ID",
+ "please_select_a_timezone": "Please select a timezone",
+ "archive_project": {
+ "title": "Archive project",
+ "description": "Archiving a project will unlist your project from your side navigation although you will still be able to access it from your projects page. You can restore the project or delete it whenever you want.",
+ "button": "Archive project"
+ },
+ "delete_project": {
+ "title": "Delete project",
+ "description": "When deleting a project, all of the data and resources within that project will be permanently removed and cannot be recovered.",
+ "button": "Delete my project"
+ },
+ "toast": {
+ "success": "Project updated successfully",
+ "error": "Project could not be updated. Please try again."
+ }
+ },
+ "members": {
+ "label": "Members",
+ "project_lead": "Project lead",
+ "default_assignee": "Default assignee",
+ "guest_super_permissions": {
+ "title": "Grant view access to all work items for guest users:",
+ "sub_heading": "This will allow guests to have view access to all the project work items."
+ },
+ "invite_members": {
+ "title": "Invite members",
+ "sub_heading": "Invite members to work on your project.",
+ "select_co_worker": "Select co-worker"
+ }
+ },
+ "states": {
+ "describe_this_state_for_your_members": "Describe this state for your members.",
+ "empty_state": {
+ "title": "No states available for the {groupKey} group",
+ "description": "Please create a new state"
+ }
+ },
+ "labels": {
+ "label_title": "Label title",
+ "label_title_is_required": "Label title is required",
+ "label_max_char": "Label name should not exceed 255 characters",
+ "toast": {
+ "error": "Error while updating the label"
+ }
+ },
+ "estimates": {
+ "title": "Enable estimates for my project",
+ "description": "They help you in communicating complexity and workload of the team."
+ },
+ "automations": {
+ "label": "Automations",
+ "auto-archive": {
+ "title": "Auto-archive closed work items",
+ "description": "Plane will auto archive work items that have been completed or canceled.",
+ "duration": "Auto-archive work items that are closed for"
+ },
+ "auto-close": {
+ "title": "Auto-close work items",
+ "description": "Plane will automatically close work items that haven't been completed or canceled.",
+ "duration": "Auto-close work items that are inactive for",
+ "auto_close_status": "Auto-close status"
+ }
+ },
+
+ "empty_state": {
+ "labels": {
+ "title": "No labels yet",
+ "description": "Create labels to help organize and filter work items in you project."
+ },
+ "estimates": {
+ "title": "No estimate systems yet",
+ "description": "Create a set of estimates to communicate the amount of work per work item.",
+ "primary_button": "Add estimate system"
+ }
+ }
+ },
+
+ "project_cycles": {
+ "add_cycle": "Add cycle",
+ "more_details": "More details",
+ "cycle": "Cycle",
+ "update_cycle": "Update cycle",
+ "create_cycle": "Create cycle",
+ "no_matching_cycles": "No matching cycles",
+ "remove_filters_to_see_all_cycles": "Remove the filters to see all cycles",
+ "remove_search_criteria_to_see_all_cycles": "Remove the search criteria to see all cycles",
+ "only_completed_cycles_can_be_archived": "Only completed cycles can be archived",
+ "active_cycle": {
+ "label": "Active cycle",
+ "progress": "Progress",
+ "chart": "Burndown chart",
+ "priority_issue": "Priority work items",
+ "assignees": "Assignees",
+ "issue_burndown": "Work item burndown",
+ "ideal": "Ideal",
+ "current": "Current",
+ "labels": "Labels"
+ },
+ "upcoming_cycle": {
+ "label": "Upcoming cycle"
+ },
+ "completed_cycle": {
+ "label": "Completed cycle"
+ },
+ "status": {
+ "days_left": "Days left",
+ "completed": "Completed",
+ "yet_to_start": "Yet to start",
+ "in_progress": "In progress",
+ "draft": "Draft"
+ },
+ "action": {
+ "restore": {
+ "title": "Restore cycle",
+ "success": {
+ "title": "Cycle restored",
+ "description": "The cycle has been restored."
+ },
+ "failed": {
+ "title": "Cycle restore failed",
+ "description": "The cycle could not be restored. Please try again."
+ }
+ },
+ "favorite": {
+ "loading": "Adding cycle to favorites",
+ "success": {
+ "description": "Cycle added to favorites.",
+ "title": "Success!"
+ },
+ "failed": {
+ "description": "Couldn't add the cycle to favorites. Please try again.",
+ "title": "Error!"
+ }
+ },
+ "unfavorite": {
+ "loading": "Removing cycle from favorites",
+ "success": {
+ "description": "Cycle removed from favorites.",
+ "title": "Success!"
+ },
+ "failed": {
+ "description": "Couldn't remove the cycle from favorites. Please try again.",
+ "title": "Error!"
+ }
+ },
+ "update": {
+ "loading": "Updating cycle",
+ "success": {
+ "description": "Cycle updated successfully.",
+ "title": "Success!"
+ },
+ "failed": {
+ "description": "Error updating the cycle. Please try again.",
+ "title": "Error!"
+ },
+ "error": {
+ "already_exists": "You already have a cycle on the given dates, if you want to create a draft cycle, you can do that by removing both the dates."
+ }
+ }
+ },
+ "empty_state": {
+ "general": {
+ "title": "Group and timebox your work in Cycles.",
+ "description": "Break work down by timeboxed chunks, work backwards from your project deadline to set dates, and make tangible progress as a team.",
+ "primary_button": {
+ "text": "Set your first cycle",
+ "comic": {
+ "title": "Cycles are repetitive time-boxes.",
+ "description": "A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "No work items added to the cycle",
+ "description": "Add or create work items you wish to timebox and deliver within this cycle",
+ "primary_button": {
+ "text": "Create new work item"
+ },
+ "secondary_button": {
+ "text": "Add existing work item"
+ }
+ },
+ "completed_no_issues": {
+ "title": "No work items in the cycle",
+ "description": "No work items in the cycle. Work items are either transferred or hidden. To see hidden work items if any, update your display properties accordingly."
+ },
+ "active": {
+ "title": "No active cycle",
+ "description": "An active cycle includes any period that encompasses today's date within its range. Find the progress and details of the active cycle here."
+ },
+ "archived": {
+ "title": "No archived cycles yet",
+ "description": "To tidy up your project, archive completed cycles. Find them here once archived."
+ }
+ }
+ },
+
+ "project_issues": {
+ "empty_state": {
+ "no_issues": {
+ "title": "Create a work item and assign it to someone, even yourself",
+ "description": "Think of work items as jobs, tasks, work, or JTBD. Which we like. A work item and its sub-work items are usually time-based actionables assigned to members of your team. Your team creates, assigns, and completes work items to move your project towards its goal.",
+ "primary_button": {
+ "text": "Create your first work item",
+ "comic": {
+ "title": "Work items are building blocks in Plane.",
+ "description": "Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of work items that likely have sub-work items."
+ }
+ }
+ },
+ "no_archived_issues": {
+ "title": "No archived work items yet",
+ "description": "Manually or through automation, you can archive work items that are completed or cancelled. Find them here once archived.",
+ "primary_button": {
+ "text": "Set automation"
+ }
+ },
+ "issues_empty_filter": {
+ "title": "No work items found matching the filters applied",
+ "secondary_button": {
+ "text": "Clear all filters"
+ }
+ }
+ }
+ },
+
+ "project_module": {
+ "add_module": "Add Module",
+ "update_module": "Update Module",
+ "create_module": "Create Module",
+ "archive_module": "Archive Module",
+ "restore_module": "Restore Module",
+ "delete_module": "Delete module",
+ "empty_state": {
+ "general": {
+ "title": "Map your project milestones to Modules and track aggregated work easily.",
+ "description": "A group of work items that belong to a logical, hierarchical parent form a module. Think of them as a way to track work by project milestones. They have their own periods and deadlines as well as analytics to help you see how close or far you are from a milestone.",
+ "primary_button": {
+ "text": "Build your first module",
+ "comic": {
+ "title": "Modules help group work by hierarchy.",
+ "description": "A cart module, a chassis module, and a warehouse module are all good example of this grouping."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "No work items in the module",
+ "description": "Create or add work items which you want to accomplish as part of this module",
+ "primary_button": {
+ "text": "Create new work items"
+ },
+ "secondary_button": {
+ "text": "Add an existing work item"
+ }
+ },
+ "archived": {
+ "title": "No archived Modules yet",
+ "description": "To tidy up your project, archive completed or cancelled modules. Find them here once archived."
+ },
+ "sidebar": {
+ "in_active": "This module isn't active yet.",
+ "invalid_date": "Invalid date. Please enter valid date."
+ }
+ },
+ "quick_actions": {
+ "archive_module": "Archive module",
+ "archive_module_description": "Only completed or canceled\nmodule can be archived.",
+ "delete_module": "Delete module"
+ },
+ "toast": {
+ "copy": {
+ "success": "Module link copied to clipboard"
+ },
+ "delete": {
+ "success": "Module deleted successfully",
+ "error": "Failed to delete module"
+ }
+ }
+ },
+
+ "project_views": {
+ "empty_state": {
+ "general": {
+ "title": "Save filtered views for your project. Create as many as you need",
+ "description": "Views are a set of saved filters that you use frequently or want easy access to. All your colleagues in a project can see everyone’s views and choose whichever suits their needs best.",
+ "primary_button": {
+ "text": "Create your first view",
+ "comic": {
+ "title": "Views work atop Work item properties.",
+ "description": "You can create a view from here with as many properties as filters as you see fit."
+ }
+ }
+ },
+ "filter": {
+ "title": "No matching views",
+ "description": "No views match the search criteria. \n Create a new view instead."
+ }
+ }
+ },
+
+ "project_page": {
+ "empty_state": {
+ "general": {
+ "title": "Write a note, a doc, or a full knowledge base. Get Galileo, Plane's AI assistant, to help you get started",
+ "description": "Pages are thoughts potting space in Plane. Take down meeting notes, format them easily, embed work items, lay them out using a library of components, and keep them all in your project's context. To make short work of any doc, invoke Galileo, Plane's AI, with a shortcut or the click of a button.",
+ "primary_button": {
+ "text": "Create your first page"
+ }
+ },
+ "private": {
+ "title": "No private pages yet",
+ "description": "Keep your private thoughts here. When you're ready to share, the team's just a click away.",
+ "primary_button": {
+ "text": "Create your first page"
+ }
+ },
+ "public": {
+ "title": "No public pages yet",
+ "description": "See pages shared with everyone in your project right here.",
+ "primary_button": {
+ "text": "Create your first page"
+ }
+ },
+ "archived": {
+ "title": "No archived pages yet",
+ "description": "Archive pages not on your radar. Access them here when needed."
+ }
+ }
+ },
+
+ "command_k": {
+ "empty_state": {
+ "search": {
+ "title": "No results found"
+ }
+ }
+ },
+
+ "issue_relation": {
+ "empty_state": {
+ "search": {
+ "title": "No matching work items found"
+ },
+ "no_issues": {
+ "title": "No work items found"
+ }
+ }
+ },
+
+ "issue_comment": {
+ "empty_state": {
+ "general": {
+ "title": "No comments yet",
+ "description": "Comments can be used as a discussion and follow-up space for the work items"
+ }
+ }
+ },
+
+ "notification": {
+ "label": "Inbox",
+ "page_label": "{workspace} - Inbox",
+ "options": {
+ "mark_all_as_read": "Mark all as read",
+ "mark_read": "Mark as read",
+ "mark_unread": "Mark as unread",
+ "refresh": "Refresh",
+ "filters": "Inbox Filters",
+ "show_unread": "Show unread",
+ "show_snoozed": "Show snoozed",
+ "show_archived": "Show archived",
+ "mark_archive": "Archive",
+ "mark_unarchive": "Un archive",
+ "mark_snooze": "Snooze",
+ "mark_unsnooze": "Un snooze"
+ },
+ "toasts": {
+ "read": "Notification marked as read",
+ "unread": "Notification marked as unread",
+ "archived": "Notification marked as archived",
+ "unarchived": "Notification marked as un archived",
+ "snoozed": "Notification snoozed",
+ "unsnoozed": "Notification un snoozed"
+ },
+ "empty_state": {
+ "detail": {
+ "title": "Select to view details."
+ },
+ "all": {
+ "title": "No work items assigned",
+ "description": "Updates for work items assigned to you can be \n seen here"
+ },
+ "mentions": {
+ "title": "No work items assigned",
+ "description": "Updates for work items assigned to you can be \n seen here"
+ }
+ },
+ "tabs": {
+ "all": "All",
+ "mentions": "Mentions"
+ },
+ "filter": {
+ "assigned": "Assigned to me",
+ "created": "Created by me",
+ "subscribed": "Subscribed by me"
+ },
+ "snooze": {
+ "1_day": "1 day",
+ "3_days": "3 days",
+ "5_days": "5 days",
+ "1_week": "1 week",
+ "2_weeks": "2 weeks",
+ "custom": "Custom"
+ }
+ },
+
+ "active_cycle": {
+ "empty_state": {
+ "progress": {
+ "title": "Add work items to the cycle to view it's progress"
+ },
+ "chart": {
+ "title": "Add work items to the cycle to view the burndown chart."
+ },
+ "priority_issue": {
+ "title": "Observe high priority work items tackled in the cycle at a glance."
+ },
+ "assignee": {
+ "title": "Add assignees to work items to see a breakdown of work by assignees."
+ },
+ "label": {
+ "title": "Add labels to work items to see the breakdown of work by labels."
+ }
+ }
+ },
+
+ "disabled_project": {
+ "empty_state": {
+ "inbox": {
+ "title": "Intake is not enabled for the project.",
+ "description": "Intake helps you manage incoming requests to your project and add them as work items in your workflow. Enable intake from project settings to manage requests.",
+ "primary_button": {
+ "text": "Manage features"
+ }
+ },
+ "cycle": {
+ "title": "Cycles is not enabled for this project.",
+ "description": "Break work down by timeboxed chunks, work backwards from your project deadline to set dates, and make tangible progress as a team. Enable the cycles feature for your project to start using them.",
+ "primary_button": {
+ "text": "Manage features"
+ }
+ },
+ "module": {
+ "title": "Modules are not enabled for the project.",
+ "description": "Modules are the building blocks of your project. Enable modules from project settings to start using them.",
+ "primary_button": {
+ "text": "Manage features"
+ }
+ },
+ "page": {
+ "title": "Pages are not enabled for the project.",
+ "description": "Pages are the building blocks of your project. Enable pages from project settings to start using them.",
+ "primary_button": {
+ "text": "Manage features"
+ }
+ },
+ "view": {
+ "title": "Views are not enabled for the project.",
+ "description": "Views are the building blocks of your project. Enable views from project settings to start using them.",
+ "primary_button": {
+ "text": "Manage features"
+ }
+ }
+ }
+ },
+ "workspace_draft_issues": {
+ "draft_an_issue": "Draft a work item",
+ "empty_state": {
+ "title": "Half-written work items, and soon, comments will show up here.",
+ "description": "To try this out, start adding a work item and leave it mid-way or create your first draft below. 😉",
+ "primary_button": {
+ "text": "Create your first draft"
+ }
+ },
+ "delete_modal": {
+ "title": "Delete draft",
+ "description": "Are you sure you want to delete this draft? This can't be undone."
+ },
+ "toasts": {
+ "created": {
+ "success": "Draft created",
+ "error": "Work item could not be created. Please try again."
+ },
+ "deleted": {
+ "success": "Draft deleted"
+ }
+ }
+ },
+
+ "stickies": {
+ "title": "Your stickies",
+ "placeholder": "click to type here",
+ "all": "All stickies",
+ "no-data": "Jot down an idea, capture an aha, or record a brainwave. Add a sticky to get started.",
+ "add": "Add sticky",
+ "search_placeholder": "Search by title",
+ "delete": "Delete sticky",
+ "delete_confirmation": "Are you sure you want to delete this sticky?",
+ "empty_state": {
+ "simple": "Jot down an idea, capture an aha, or record a brainwave. Add a sticky to get started.",
+ "general": {
+ "title": "Stickies are quick notes and to-dos you take down on the fly.",
+ "description": "Capture your thoughts and ideas effortlessly by creating stickies that you can access anytime and from anywhere.",
+ "primary_button": {
+ "text": "Add sticky"
+ }
+ },
+ "search": {
+ "title": "That doesn't match any of your stickies.",
+ "description": "Try a different term or let us know\nif you are sure your search is right. ",
+ "primary_button": {
+ "text": "Add sticky"
+ }
+ }
+ },
+ "toasts": {
+ "errors": {
+ "wrong_name": "The sticky name cannot be longer than 100 characters.",
+ "already_exists": "There already exists a sticky with no description"
+ },
+ "created": {
+ "title": "Sticky created",
+ "message": "The sticky has been successfully created"
+ },
+ "not_created": {
+ "title": "Sticky not created",
+ "message": "The sticky could not be created"
+ },
+ "updated": {
+ "title": "Sticky updated",
+ "message": "The sticky has been successfully updated"
+ },
+ "not_updated": {
+ "title": "Sticky not updated",
+ "message": "The sticky could not be updated"
+ },
+ "removed": {
+ "title": "Sticky removed",
+ "message": "The sticky has been successfully removed"
+ },
+ "not_removed": {
+ "title": "Sticky not removed",
+ "message": "The sticky could not be removed"
+ }
+ }
+ },
+
+ "role_details": {
+ "guest": {
+ "title": "Guest",
+ "description": "External members of organizations can be invited as guests."
+ },
+ "member": {
+ "title": "Member",
+ "description": "Ability to read, write, edit, and delete entities inside projects, cycles, and modules"
+ },
+ "admin": {
+ "title": "Admin",
+ "description": "All permissions set to true within the workspace."
+ }
+ },
+
+ "user_roles": {
+ "product_or_project_manager": "Product / Project Manager",
+ "development_or_engineering": "Development / Engineering",
+ "founder_or_executive": "Founder / Executive",
+ "freelancer_or_consultant": "Freelancer / Consultant",
+ "marketing_or_growth": "Marketing / Growth",
+ "sales_or_business_development": "Sales / Business Development",
+ "support_or_operations": "Support / Operations",
+ "student_or_professor": "Student / Professor",
+ "human_resources": "Human / Resources",
+ "other": "Other"
+ },
+
+ "importer": {
+ "github": {
+ "title": "Github",
+ "description": "Import work items from GitHub repositories and sync them."
+ },
+ "jira": {
+ "title": "Jira",
+ "description": "Import work items and epics from Jira projects and epics."
+ }
+ },
+
+ "exporter": {
+ "csv": {
+ "title": "CSV",
+ "description": "Export work items to a CSV file.",
+ "short_description": "Export as csv"
+ },
+ "excel": {
+ "title": "Excel",
+ "description": "Export work items to a Excel file.",
+ "short_description": "Export as excel"
+ },
+ "xlsx": {
+ "title": "Excel",
+ "description": "Export work items to a Excel file.",
+ "short_description": "Export as excel"
+ },
+ "json": {
+ "title": "JSON",
+ "description": "Export work items to a JSON file.",
+ "short_description": "Export as json"
+ }
+ },
+ "default_global_view": {
+ "all_issues": "All work items",
+ "assigned": "Assigned",
+ "created": "Created",
+ "subscribed": "Subscribed"
+ },
+
+ "themes": {
+ "theme_options": {
+ "system_preference": {
+ "label": "System preference"
+ },
+ "light": {
+ "label": "Light"
+ },
+ "dark": {
+ "label": "Dark"
+ },
+ "light_contrast": {
+ "label": "Light high contrast"
+ },
+ "dark_contrast": {
+ "label": "Dark high contrast"
+ },
+ "custom": {
+ "label": "Custom theme"
+ }
+ }
+ },
+ "project_modules": {
+ "status": {
+ "backlog": "Backlog",
+ "planned": "Planned",
+ "in_progress": "In Progress",
+ "paused": "Paused",
+ "completed": "Completed",
+ "cancelled": "Cancelled"
+ },
+ "layout": {
+ "list": "List layout",
+ "board": "Gallery layout",
+ "timeline": "Timeline layout"
+ },
+ "order_by": {
+ "name": "Name",
+ "progress": "Progress",
+ "issues": "Number of work items",
+ "due_date": "Due date",
+ "created_at": "Created date",
+ "manual": "Manual"
+ }
+ },
+
+ "cycle": {
+ "label": "{count, plural, one {Cycle} other {Cycles}}",
+ "no_cycle": "No cycle"
+ },
+
+ "module": {
+ "label": "{count, plural, one {Module} other {Modules}}",
+ "no_module": "No module"
+ }
+}
diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json
new file mode 100644
index 000000000..bc57dba1d
--- /dev/null
+++ b/packages/i18n/src/locales/es/translations.json
@@ -0,0 +1,2364 @@
+{
+ "sidebar": {
+ "projects": "Proyectos",
+ "pages": "Páginas",
+ "new_work_item": "Nuevo elemento de trabajo",
+ "home": "Inicio",
+ "your_work": "Tu trabajo",
+ "inbox": "Bandeja de entrada",
+ "workspace": "Espacio de trabajo",
+ "views": "Vistas",
+ "analytics": "Análisis",
+ "work_items": "Elementos de trabajo",
+ "cycles": "Ciclos",
+ "modules": "Módulos",
+ "intake": "Entrada",
+ "drafts": "Borradores",
+ "favorites": "Favoritos",
+ "pro": "Pro",
+ "upgrade": "Mejorar"
+ },
+
+ "auth": {
+ "common": {
+ "email": {
+ "label": "Correo electrónico",
+ "placeholder": "nombre@empresa.com",
+ "errors": {
+ "required": "El correo electrónico es obligatorio",
+ "invalid": "El correo electrónico no es válido"
+ }
+ },
+ "password": {
+ "label": "Contraseña",
+ "set_password": "Establecer una contraseña",
+ "placeholder": "Ingresa la contraseña",
+ "confirm_password": {
+ "label": "Confirmar contraseña",
+ "placeholder": "Confirmar contraseña"
+ },
+ "current_password": {
+ "label": "Contraseña actual"
+ },
+ "new_password": {
+ "label": "Nueva contraseña",
+ "placeholder": "Ingresa nueva contraseña"
+ },
+ "change_password": {
+ "label": {
+ "default": "Cambiar contraseña",
+ "submitting": "Cambiando contraseña"
+ }
+ },
+ "errors": {
+ "match": "Las contraseñas no coinciden",
+ "empty": "Por favor ingresa tu contraseña",
+ "length": "La contraseña debe tener más de 8 caracteres",
+ "strength": {
+ "weak": "La contraseña es débil",
+ "strong": "La contraseña es fuerte"
+ }
+ },
+ "submit": "Establecer contraseña",
+ "toast": {
+ "change_password": {
+ "success": {
+ "title": "¡Éxito!",
+ "message": "Contraseña cambiada exitosamente."
+ },
+ "error": {
+ "title": "¡Error!",
+ "message": "Algo salió mal. Por favor intenta de nuevo."
+ }
+ }
+ }
+ },
+ "unique_code": {
+ "label": "Código único",
+ "placeholder": "obtiene-establece-vuela",
+ "paste_code": "Pega el código enviado a tu correo electrónico",
+ "requesting_new_code": "Solicitando nuevo código",
+ "sending_code": "Enviando código"
+ },
+ "already_have_an_account": "¿Ya tienes una cuenta?",
+ "login": "Iniciar sesión",
+ "create_account": "Crear una cuenta",
+ "new_to_plane": "¿Nuevo en Plane?",
+ "back_to_sign_in": "Volver a iniciar sesión",
+ "resend_in": "Reenviar en {seconds} segundos",
+ "sign_in_with_unique_code": "Iniciar sesión con código único",
+ "forgot_password": "¿Olvidaste tu contraseña?"
+ },
+ "sign_up": {
+ "header": {
+ "label": "Crea una cuenta para comenzar a gestionar el trabajo con tu equipo.",
+ "step": {
+ "email": {
+ "header": "Registrarse",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "Registrarse",
+ "sub_header": "Regístrate usando una combinación de correo electrónico y contraseña."
+ },
+ "unique_code": {
+ "header": "Registrarse",
+ "sub_header": "Regístrate usando un código único enviado a la dirección de correo electrónico anterior."
+ }
+ }
+ },
+ "errors": {
+ "password": {
+ "strength": "Intenta establecer una contraseña fuerte para continuar"
+ }
+ }
+ },
+ "sign_in": {
+ "header": {
+ "label": "Inicia sesión para comenzar a gestionar el trabajo con tu equipo.",
+ "step": {
+ "email": {
+ "header": "Iniciar sesión o registrarse",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "Iniciar sesión o registrarse",
+ "sub_header": "Usa tu combinación de correo electrónico y contraseña para iniciar sesión."
+ },
+ "unique_code": {
+ "header": "Iniciar sesión o registrarse",
+ "sub_header": "Inicia sesión usando un código único enviado a la dirección de correo electrónico anterior."
+ }
+ }
+ }
+ },
+ "forgot_password": {
+ "title": "Restablecer tu contraseña",
+ "description": "Ingresa la dirección de correo electrónico verificada de tu cuenta de usuario y te enviaremos un enlace para restablecer la contraseña.",
+ "email_sent": "Enviamos el enlace de restablecimiento a tu dirección de correo electrónico",
+ "send_reset_link": "Enviar enlace de restablecimiento",
+ "errors": {
+ "smtp_not_enabled": "Vemos que tu administrador no ha habilitado SMTP, no podremos enviar un enlace para restablecer la contraseña"
+ },
+ "toast": {
+ "success": {
+ "title": "Correo enviado",
+ "message": "Revisa tu bandeja de entrada para encontrar un enlace para restablecer tu contraseña. Si no aparece en unos minutos, revisa tu carpeta de spam."
+ },
+ "error": {
+ "title": "¡Error!",
+ "message": "Algo salió mal. Por favor intenta de nuevo."
+ }
+ }
+ },
+ "reset_password": {
+ "title": "Establecer nueva contraseña",
+ "description": "Asegura tu cuenta con una contraseña fuerte"
+ },
+ "set_password": {
+ "title": "Asegura tu cuenta",
+ "description": "Establecer una contraseña te ayuda a iniciar sesión de forma segura"
+ },
+ "sign_out": {
+ "toast": {
+ "error": {
+ "title": "¡Error!",
+ "message": "Error al cerrar sesión. Por favor intenta de nuevo."
+ }
+ }
+ }
+ },
+
+ "submit": "Enviar",
+ "cancel": "Cancelar",
+ "loading": "Cargando",
+ "error": "Error",
+ "success": "Éxito",
+ "warning": "Advertencia",
+ "info": "Información",
+ "close": "Cerrar",
+ "yes": "Sí",
+ "no": "No",
+ "ok": "Aceptar",
+ "name": "Nombre",
+ "description": "Descripción",
+ "search": "Buscar",
+ "add_member": "Agregar miembro",
+ "adding_members": "Agregando miembros",
+ "remove_member": "Eliminar miembro",
+ "add_members": "Agregar miembros",
+ "adding_member": "Agregando miembros",
+ "remove_members": "Eliminar miembros",
+ "add": "Agregar",
+ "adding": "Agregando",
+ "remove": "Eliminar",
+ "add_new": "Agregar nuevo",
+ "remove_selected": "Eliminar seleccionados",
+ "first_name": "Nombre",
+ "last_name": "Apellido",
+ "email": "Correo electrónico",
+ "display_name": "Nombre para mostrar",
+ "role": "Rol",
+ "timezone": "Zona horaria",
+ "avatar": "Avatar",
+ "cover_image": "Imagen de portada",
+ "password": "Contraseña",
+ "change_cover": "Cambiar portada",
+ "language": "Idioma",
+ "saving": "Guardando",
+ "save_changes": "Guardar cambios",
+ "deactivate_account": "Desactivar cuenta",
+ "deactivate_account_description": "Al desactivar una cuenta, todos los datos y recursos dentro de esa cuenta se eliminarán permanentemente y no se podrán recuperar.",
+ "profile_settings": "Configuración del perfil",
+ "your_account": "Tu cuenta",
+ "security": "Seguridad",
+ "activity": "Actividad",
+ "appearance": "Apariencia",
+ "notifications": "Notificaciones",
+ "connections": "Conexiones",
+ "workspaces": "Espacios de trabajo",
+ "create_workspace": "Crear espacio de trabajo",
+ "invitations": "Invitaciones",
+ "summary": "Resumen",
+ "assigned": "Asignado",
+ "created": "Creado",
+ "subscribed": "Suscrito",
+ "you_do_not_have_the_permission_to_access_this_page": "No tienes permiso para acceder a esta página.",
+ "something_went_wrong_please_try_again": "Algo salió mal. Por favor, inténtalo de nuevo.",
+ "load_more": "Cargar más",
+ "select_or_customize_your_interface_color_scheme": "Selecciona o personaliza el esquema de colores de tu interfaz.",
+ "theme": "Tema",
+ "system_preference": "Preferencia del sistema",
+ "light": "Claro",
+ "dark": "Oscuro",
+ "light_contrast": "Alto contraste claro",
+ "dark_contrast": "Alto contraste oscuro",
+ "custom": "Tema personalizado",
+ "select_your_theme": "Selecciona tu tema",
+ "customize_your_theme": "Personaliza tu tema",
+ "background_color": "Color de fondo",
+ "text_color": "Color del texto",
+ "primary_color": "Color primario (Tema)",
+ "sidebar_background_color": "Color de fondo de la barra lateral",
+ "sidebar_text_color": "Color del texto de la barra lateral",
+ "set_theme": "Establecer tema",
+ "enter_a_valid_hex_code_of_6_characters": "Ingresa un código hexadecimal válido de 6 caracteres",
+ "background_color_is_required": "El color de fondo es requerido",
+ "text_color_is_required": "El color del texto es requerido",
+ "primary_color_is_required": "El color primario es requerido",
+ "sidebar_background_color_is_required": "El color de fondo de la barra lateral es requerido",
+ "sidebar_text_color_is_required": "El color del texto de la barra lateral es requerido",
+ "updating_theme": "Actualizando tema",
+ "theme_updated_successfully": "Tema actualizado exitosamente",
+ "failed_to_update_the_theme": "Error al actualizar el tema",
+ "email_notifications": "Notificaciones por correo electrónico",
+ "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "Mantente al tanto de los elementos de trabajo a los que estás suscrito. Activa esto para recibir notificaciones.",
+ "email_notification_setting_updated_successfully": "Configuración de notificaciones por correo electrónico actualizada exitosamente",
+ "failed_to_update_email_notification_setting": "Error al actualizar la configuración de notificaciones por correo electrónico",
+ "notify_me_when": "Notificarme cuando",
+ "property_changes": "Cambios de propiedades",
+ "property_changes_description": "Notificarme cuando cambien las propiedades de los elementos de trabajo como asignados, prioridad, estimaciones o cualquier otra cosa.",
+ "state_change": "Cambio de estado",
+ "state_change_description": "Notificarme cuando los elementos de trabajo se muevan a un estado diferente",
+ "issue_completed": "Elemento de trabajo completado",
+ "issue_completed_description": "Notificarme solo cuando se complete un elemento de trabajo",
+ "comments": "Comentarios",
+ "comments_description": "Notificarme cuando alguien deje un comentario en el elemento de trabajo",
+ "mentions": "Menciones",
+ "mentions_description": "Notificarme solo cuando alguien me mencione en los comentarios o descripción",
+ "old_password": "Contraseña anterior",
+ "general_settings": "Configuración general",
+ "sign_out": "Cerrar sesión",
+ "signing_out": "Cerrando sesión",
+ "active_cycles": "Ciclos activos",
+ "active_cycles_description": "Monitorea ciclos en todos los proyectos, rastrea elementos de trabajo de alta prioridad y enfócate en los ciclos que necesitan atención.",
+ "on_demand_snapshots_of_all_your_cycles": "Instantáneas bajo demanda de todos tus ciclos",
+ "upgrade": "Actualizar",
+ "10000_feet_view": "Vista panorámica de todos los ciclos activos.",
+ "10000_feet_view_description": "Aléjate para ver los ciclos en ejecución en todos tus proyectos a la vez en lugar de ir de Ciclo en Ciclo en cada proyecto.",
+ "get_snapshot_of_each_active_cycle": "Obtén una instantánea de cada ciclo activo.",
+ "get_snapshot_of_each_active_cycle_description": "Rastrea métricas de alto nivel para todos los ciclos activos, ve su estado de progreso y obtén una idea del alcance contra los plazos.",
+ "compare_burndowns": "Compara los burndowns.",
+ "compare_burndowns_description": "Monitorea cómo se está desempeñando cada uno de tus equipos con un vistazo al informe de burndown de cada ciclo.",
+ "quickly_see_make_or_break_issues": "Ve rápidamente los elementos de trabajo críticos.",
+ "quickly_see_make_or_break_issues_description": "Previsualiza elementos de trabajo de alta prioridad para cada ciclo contra fechas de vencimiento. Vélos todos por ciclo con un clic.",
+ "zoom_into_cycles_that_need_attention": "Enfócate en los ciclos que necesitan atención.",
+ "zoom_into_cycles_that_need_attention_description": "Investiga el estado de cualquier ciclo que no se ajuste a las expectativas con un clic.",
+ "stay_ahead_of_blockers": "Mantente adelante de los bloqueadores.",
+ "stay_ahead_of_blockers_description": "Detecta desafíos de un proyecto a otro y ve dependencias entre ciclos que no son obvias desde ninguna otra vista.",
+ "analytics": "Análisis",
+ "workspace_invites": "Invitaciones al espacio de trabajo",
+ "enter_god_mode": "Entrar en modo dios",
+ "workspace_logo": "Logo del espacio de trabajo",
+ "new_issue": "Nuevo elemento de trabajo",
+ "your_work": "Tu trabajo",
+ "workspace_dashboards": "Paneles de control",
+ "drafts": "Borradores",
+ "projects": "Proyectos",
+ "views": "Vistas",
+ "workspace": "Espacio de trabajo",
+ "archives": "Archivos",
+ "settings": "Configuración",
+ "failed_to_move_favorite": "Error al mover favorito",
+ "favorites": "Favoritos",
+ "no_favorites_yet": "Aún no hay favoritos",
+ "create_folder": "Crear carpeta",
+ "new_folder": "Nueva carpeta",
+ "favorite_updated_successfully": "Favorito actualizado exitosamente",
+ "favorite_created_successfully": "Favorito creado exitosamente",
+ "folder_already_exists": "La carpeta ya existe",
+ "folder_name_cannot_be_empty": "El nombre de la carpeta no puede estar vacío",
+ "something_went_wrong": "Algo salió mal",
+ "failed_to_reorder_favorite": "Error al reordenar favorito",
+ "favorite_removed_successfully": "Favorito eliminado exitosamente",
+ "failed_to_create_favorite": "Error al crear favorito",
+ "failed_to_rename_favorite": "Error al renombrar favorito",
+ "project_link_copied_to_clipboard": "Enlace del proyecto copiado al portapapeles",
+ "link_copied": "Enlace copiado",
+ "add_project": "Agregar proyecto",
+ "create_project": "Crear proyecto",
+ "failed_to_remove_project_from_favorites": "No se pudo eliminar el proyecto de favoritos. Por favor, inténtalo de nuevo.",
+ "project_created_successfully": "Proyecto creado exitosamente",
+ "project_created_successfully_description": "Proyecto creado exitosamente. Ahora puedes comenzar a agregar elementos de trabajo.",
+ "project_cover_image_alt": "Imagen de portada del proyecto",
+ "name_is_required": "El nombre es requerido",
+ "title_should_be_less_than_255_characters": "El título debe tener menos de 255 caracteres",
+ "project_name": "Nombre del proyecto",
+ "project_id_must_be_at_least_1_character": "El ID del proyecto debe tener al menos 1 carácter",
+ "project_id_must_be_at_most_5_characters": "El ID del proyecto debe tener como máximo 5 caracteres",
+ "project_id": "ID del proyecto",
+ "project_id_tooltip_content": "Te ayuda a identificar elementos de trabajo en el proyecto de manera única. Máximo 5 caracteres.",
+ "description_placeholder": "Descripción",
+ "only_alphanumeric_non_latin_characters_allowed": "Solo se permiten caracteres alfanuméricos y no latinos.",
+ "project_id_is_required": "El ID del proyecto es requerido",
+ "project_id_allowed_char": "Solo se permiten caracteres alfanuméricos y no latinos.",
+ "project_id_min_char": "El ID del proyecto debe tener al menos 1 carácter",
+ "project_id_max_char": "El ID del proyecto debe tener como máximo 5 caracteres",
+ "project_description_placeholder": "Ingresa la descripción del proyecto",
+ "select_network": "Seleccionar red",
+ "lead": "Líder",
+ "date_range": "Rango de fechas",
+ "private": "Privado",
+ "public": "Público",
+ "accessible_only_by_invite": "Accesible solo por invitación",
+ "anyone_in_the_workspace_except_guests_can_join": "Cualquiera en el espacio de trabajo excepto invitados puede unirse",
+ "creating": "Creando",
+ "creating_project": "Creando proyecto",
+ "adding_project_to_favorites": "Agregando proyecto a favoritos",
+ "project_added_to_favorites": "Proyecto agregado a favoritos",
+ "couldnt_add_the_project_to_favorites": "No se pudo agregar el proyecto a favoritos. Por favor, inténtalo de nuevo.",
+ "removing_project_from_favorites": "Eliminando proyecto de favoritos",
+ "project_removed_from_favorites": "Proyecto eliminado de favoritos",
+ "couldnt_remove_the_project_from_favorites": "No se pudo eliminar el proyecto de favoritos. Por favor, inténtalo de nuevo.",
+ "add_to_favorites": "Agregar a favoritos",
+ "remove_from_favorites": "Eliminar de favoritos",
+ "publish_settings": "Configuración de publicación",
+ "publish": "Publicar",
+ "copy_link": "Copiar enlace",
+ "leave_project": "Abandonar proyecto",
+ "join_the_project_to_rearrange": "Únete al proyecto para reorganizar",
+ "drag_to_rearrange": "Arrastra para reorganizar",
+ "congrats": "¡Felicitaciones!",
+ "open_project": "Abrir proyecto",
+ "issues": "Elementos de trabajo",
+ "cycles": "Ciclos",
+ "modules": "Módulos",
+ "pages": "Páginas",
+ "intake": "Entrada",
+ "time_tracking": "Seguimiento de tiempo",
+ "work_management": "Gestión del trabajo",
+ "projects_and_issues": "Proyectos y elementos de trabajo",
+ "projects_and_issues_description": "Activa o desactiva estos en este proyecto.",
+ "cycles_description": "Organiza el trabajo en períodos de tiempo según lo consideres conveniente por proyecto y cambia la frecuencia de un período a otro.",
+ "modules_description": "Agrupa el trabajo en configuraciones similares a subproyectos con sus propios líderes y asignados.",
+ "views_description": "Guarda ordenamientos, filtros y opciones de visualización para más tarde o compártelos.",
+ "pages_description": "Escribe cualquier cosa como escribirías cualquier cosa.",
+ "intake_description": "Mantente al tanto de los elementos de trabajo a los que estás suscrito. Activa esto para recibir notificaciones.",
+ "time_tracking_description": "Rastrea el tiempo dedicado a elementos de trabajo y proyectos.",
+ "work_management_description": "Gestiona tu trabajo y proyectos con facilidad.",
+ "documentation": "Documentación",
+ "message_support": "Mensaje al soporte",
+ "contact_sales": "Contactar ventas",
+ "hyper_mode": "Modo Hyper",
+ "keyboard_shortcuts": "Atajos de teclado",
+ "whats_new": "¿Qué hay de nuevo?",
+ "version": "Versión",
+ "we_are_having_trouble_fetching_the_updates": "Estamos teniendo problemas para obtener las actualizaciones.",
+ "our_changelogs": "nuestros registros de cambios",
+ "for_the_latest_updates": "para las últimas actualizaciones.",
+ "please_visit": "Por favor visita",
+ "docs": "Documentación",
+ "full_changelog": "Registro de cambios completo",
+ "support": "Soporte",
+ "discord": "Discord",
+ "powered_by_plane_pages": "Desarrollado por Plane Pages",
+ "please_select_at_least_one_invitation": "Por favor selecciona al menos una invitación.",
+ "please_select_at_least_one_invitation_description": "Por favor selecciona al menos una invitación para unirte al espacio de trabajo.",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace": "Vemos que alguien te ha invitado a unirte a un espacio de trabajo",
+ "join_a_workspace": "Únete a un espacio de trabajo",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "Vemos que alguien te ha invitado a unirte a un espacio de trabajo",
+ "join_a_workspace_description": "Únete a un espacio de trabajo",
+ "accept_and_join": "Aceptar y unirse",
+ "go_home": "Ir a inicio",
+ "no_pending_invites": "No hay invitaciones pendientes",
+ "you_can_see_here_if_someone_invites_you_to_a_workspace": "Puedes ver aquí si alguien te invita a un espacio de trabajo",
+ "back_to_home": "Volver a inicio",
+ "workspace_name": "nombre-del-espacio-de-trabajo",
+ "deactivate_your_account": "Desactivar tu cuenta",
+ "deactivate_your_account_description": "Una vez desactivada, no se te podrán asignar elementos de trabajo ni se te facturará por tu espacio de trabajo. Para reactivar tu cuenta, necesitarás una invitación a un espacio de trabajo con esta dirección de correo electrónico.",
+ "deactivating": "Desactivando",
+ "confirm": "Confirmar",
+ "confirming": "Confirmando",
+ "draft_created": "Borrador creado",
+ "issue_created_successfully": "Elemento de trabajo creado exitosamente",
+ "draft_creation_failed": "Error al crear borrador",
+ "issue_creation_failed": "Error al crear elemento de trabajo",
+ "draft_issue": "Borrador de elemento de trabajo",
+ "issue_updated_successfully": "Elemento de trabajo actualizado exitosamente",
+ "issue_could_not_be_updated": "El elemento de trabajo no pudo ser actualizado",
+ "create_a_draft": "Crear un borrador",
+ "save_to_drafts": "Guardar en borradores",
+ "save": "Guardar",
+ "update": "Actualizar",
+ "updating": "Actualizando",
+ "create_new_issue": "Crear nuevo elemento de trabajo",
+ "editor_is_not_ready_to_discard_changes": "El editor no está listo para descartar cambios",
+ "failed_to_move_issue_to_project": "Error al mover elemento de trabajo al proyecto",
+ "create_more": "Crear más",
+ "add_to_project": "Agregar al proyecto",
+ "discard": "Descartar",
+ "duplicate_issue_found": "Se encontró un elemento de trabajo duplicado",
+ "duplicate_issues_found": "Se encontraron elementos de trabajo duplicados",
+ "no_matching_results": "No hay resultados coincidentes",
+ "title_is_required": "El título es requerido",
+ "title": "Título",
+ "state": "Estado",
+ "priority": "Prioridad",
+ "none": "Ninguno",
+ "urgent": "Urgente",
+ "high": "Alta",
+ "medium": "Media",
+ "low": "Baja",
+ "members": "Miembros",
+ "assignee": "Asignado",
+ "assignees": "Asignados",
+ "you": "Tú",
+ "labels": "Etiquetas",
+ "create_new_label": "Crear nueva etiqueta",
+ "start_date": "Fecha de inicio",
+ "end_date": "Fecha de fin",
+ "due_date": "Fecha de vencimiento",
+ "estimate": "Estimación",
+ "change_parent_issue": "Cambiar elemento de trabajo padre",
+ "remove_parent_issue": "Eliminar elemento de trabajo padre",
+ "add_parent": "Agregar padre",
+ "loading_members": "Cargando miembros",
+ "view_link_copied_to_clipboard": "Enlace de vista copiado al portapapeles.",
+ "required": "Requerido",
+ "optional": "Opcional",
+ "Cancel": "Cancelar",
+ "edit": "Editar",
+ "archive": "Archivar",
+ "restore": "Restaurar",
+ "open_in_new_tab": "Abrir en nueva pestaña",
+ "delete": "Eliminar",
+ "deleting": "Eliminando",
+ "make_a_copy": "Hacer una copia",
+ "move_to_project": "Mover al proyecto",
+ "good": "Buenos",
+ "morning": "días",
+ "afternoon": "tardes",
+ "evening": "noches",
+ "show_all": "Mostrar todo",
+ "show_less": "Mostrar menos",
+ "no_data_yet": "Aún no hay datos",
+ "syncing": "Sincronizando",
+ "add_work_item": "Agregar elemento de trabajo",
+ "advanced_description_placeholder": "Presiona '/' para comandos",
+ "create_work_item": "Crear elemento de trabajo",
+ "attachments": "Archivos adjuntos",
+ "declining": "Rechazando",
+ "declined": "Rechazado",
+ "decline": "Rechazar",
+ "unassigned": "Sin asignar",
+ "work_items": "Elementos de trabajo",
+ "add_link": "Agregar enlace",
+ "points": "Puntos",
+ "no_assignee": "Sin asignado",
+ "no_assignees_yet": "Aún no hay asignados",
+ "no_labels_yet": "Aún no hay etiquetas",
+ "ideal": "Ideal",
+ "current": "Actual",
+ "no_matching_members": "No hay miembros coincidentes",
+ "leaving": "Abandonando",
+ "removing": "Eliminando",
+ "leave": "Abandonar",
+ "refresh": "Actualizar",
+ "refreshing": "Actualizando",
+ "refresh_status": "Actualizar estado",
+ "prev": "Anterior",
+ "next": "Siguiente",
+ "re_generating": "Regenerando",
+ "re_generate": "Regenerar",
+ "re_generate_key": "Regenerar clave",
+ "export": "Exportar",
+ "member": "{count, plural, one{# miembro} other{# miembros}}",
+
+ "project_view": {
+ "sort_by": {
+ "created_at": "Creado el",
+ "updated_at": "Actualizado el",
+ "name": "Nombre"
+ }
+ },
+
+ "toast": {
+ "success": "¡Éxito!",
+ "error": "¡Error!"
+ },
+
+ "links": {
+ "toasts": {
+ "created": {
+ "title": "Enlace creado",
+ "message": "El enlace se ha creado correctamente"
+ },
+ "not_created": {
+ "title": "Enlace no creado",
+ "message": "No se pudo crear el enlace"
+ },
+ "updated": {
+ "title": "Enlace actualizado",
+ "message": "El enlace se ha actualizado correctamente"
+ },
+ "not_updated": {
+ "title": "Enlace no actualizado",
+ "message": "No se pudo actualizar el enlace"
+ },
+ "removed": {
+ "title": "Enlace eliminado",
+ "message": "El enlace se ha eliminado correctamente"
+ },
+ "not_removed": {
+ "title": "Enlace no eliminado",
+ "message": "No se pudo eliminar el enlace"
+ }
+ }
+ },
+
+ "home": {
+ "empty": {
+ "quickstart_guide": "Guía de inicio rápido",
+ "not_right_now": "Ahora no",
+ "create_project": {
+ "title": "Crear un proyecto",
+ "description": "La mayoría de las cosas comienzan con un proyecto en Plane.",
+ "cta": "Comenzar"
+ },
+ "invite_team": {
+ "title": "Invita a tu equipo",
+ "description": "Construye, implementa y gestiona con compañeros de trabajo.",
+ "cta": "Hazlos entrar"
+ },
+ "configure_workspace": {
+ "title": "Configura tu espacio de trabajo.",
+ "description": "Activa o desactiva funciones o ve más allá.",
+ "cta": "Configurar este espacio de trabajo"
+ },
+ "personalize_account": {
+ "title": "Haz Plane tuyo.",
+ "description": "Elige tu foto, colores y más.",
+ "cta": "Personalizar ahora"
+ },
+ "widgets": {
+ "title": "Está Silencioso Sin Widgets, Actívalos",
+ "description": "Parece que todos tus widgets están desactivados. ¡Actívalos\nahora para mejorar tu experiencia!",
+ "primary_button": {
+ "text": "Gestionar widgets"
+ }
+ }
+ },
+ "quick_links": {
+ "empty": "Guarda enlaces a cosas de trabajo que te gustaría tener a mano.",
+ "add": "Agregar enlace rápido",
+ "title": "Enlace rápido",
+ "title_plural": "Enlaces rápidos"
+ },
+ "recents": {
+ "title": "Recientes",
+ "empty": {
+ "project": "Tus proyectos recientes aparecerán aquí una vez que visites uno.",
+ "page": "Tus páginas recientes aparecerán aquí una vez que visites una.",
+ "issue": "Tus elementos de trabajo recientes aparecerán aquí una vez que visites uno.",
+ "default": "Aún no tienes elementos recientes."
+ },
+ "filters": {
+ "all": "Todos los elementos",
+ "projects": "Proyectos",
+ "pages": "Páginas",
+ "issues": "Elementos de trabajo"
+ }
+ },
+ "new_at_plane": {
+ "title": "Nuevo en Plane"
+ },
+ "quick_tutorial": {
+ "title": "Tutorial rápido"
+ },
+ "widget": {
+ "reordered_successfully": "Widget reordenado correctamente.",
+ "reordering_failed": "Ocurrió un error al reordenar el widget."
+ },
+ "manage_widgets": "Gestionar widgets",
+ "title": "Inicio",
+ "star_us_on_github": "Danos una estrella en GitHub"
+ },
+
+ "link": {
+ "modal": {
+ "url": {
+ "text": "URL",
+ "required": "La URL no es válida",
+ "placeholder": "Escribe o pega una URL"
+ },
+ "title": {
+ "text": "Título a mostrar",
+ "placeholder": "Cómo te gustaría ver este enlace"
+ }
+ }
+ },
+
+ "common": {
+ "all": "Todo",
+ "states": "Estados",
+ "state": "Estado",
+ "state_groups": "Grupos de estados",
+ "priority": "Prioridad",
+ "team_project": "Proyecto de equipo",
+ "project": "Proyecto",
+ "cycle": "Ciclo",
+ "cycles": "Ciclos",
+ "module": "Módulo",
+ "modules": "Módulos",
+ "labels": "Etiquetas",
+ "assignees": "Asignados",
+ "assignee": "Asignado",
+ "created_by": "Creado por",
+ "none": "Ninguno",
+ "link": "Enlace",
+ "estimate": "Estimación",
+ "layout": "Diseño",
+ "filters": "Filtros",
+ "display": "Mostrar",
+ "load_more": "Cargar más",
+ "activity": "Actividad",
+ "analytics": "Análisis",
+ "dates": "Fechas",
+ "success": "¡Éxito!",
+ "something_went_wrong": "Algo salió mal",
+ "error": {
+ "label": "¡Error!",
+ "message": "Ocurrió un error. Por favor, inténtalo de nuevo."
+ },
+ "group_by": "Agrupar por",
+ "epic": "Epic",
+ "epics": "Epics",
+ "work_item": "Elemento de trabajo",
+ "work_items": "Elementos de trabajo",
+ "sub_work_item": "Sub-elemento de trabajo",
+ "add": "Agregar",
+ "warning": "Advertencia",
+ "updating": "Actualizando",
+ "adding": "Agregando",
+ "update": "Actualizar",
+ "creating": "Creando",
+ "create": "Crear",
+ "cancel": "Cancelar",
+ "description": "Descripción",
+ "title": "Título",
+ "attachment": "Archivo adjunto",
+ "general": "General",
+ "features": "Características",
+ "automation": "Automatización",
+ "project_name": "Nombre del proyecto",
+ "project_id": "ID del proyecto",
+ "project_timezone": "Zona horaria del proyecto",
+ "created_on": "Creado el",
+ "update_project": "Actualizar proyecto",
+ "identifier_already_exists": "El identificador ya existe",
+ "add_more": "Agregar más",
+ "defaults": "Valores predeterminados",
+ "add_label": "Agregar etiqueta",
+ "estimates": "Estimaciones",
+ "customize_time_range": "Personalizar rango de tiempo",
+ "loading": "Cargando",
+ "attachments": "Archivos adjuntos",
+ "property": "Propiedad",
+ "properties": "Propiedades",
+ "parent": "Padre",
+ "remove": "Eliminar",
+ "archiving": "Archivando",
+ "archive": "Archivar",
+ "access": {
+ "public": "Público",
+ "private": "Privado"
+ },
+ "done": "Hecho",
+ "sub_work_items": "Sub-elementos de trabajo",
+ "comment": "Comentario",
+ "workspace_level": "Nivel de espacio de trabajo",
+ "order_by": {
+ "label": "Ordenar por",
+ "manual": "Manual",
+ "last_created": "Último creado",
+ "last_updated": "Última actualización",
+ "start_date": "Fecha de inicio",
+ "due_date": "Fecha de vencimiento",
+ "asc": "Ascendente",
+ "desc": "Descendente",
+ "updated_on": "Actualizado el"
+ },
+ "sort": {
+ "asc": "Ascendente",
+ "desc": "Descendente",
+ "created_on": "Creado el",
+ "updated_on": "Actualizado el"
+ },
+ "comments": "Comentarios",
+ "updates": "Actualizaciones",
+ "clear_all": "Limpiar todo",
+ "copied": "¡Copiado!",
+ "link_copied": "¡Enlace copiado!",
+ "link_copied_to_clipboard": "Enlace copiado al portapapeles",
+ "copied_to_clipboard": "Enlace del elemento de trabajo copiado al portapapeles",
+ "is_copied_to_clipboard": "El elemento de trabajo está copiado al portapapeles",
+ "no_links_added_yet": "Aún no se han agregado enlaces",
+ "add_link": "Agregar enlace",
+ "links": "Enlaces",
+ "go_to_workspace": "Ir al espacio de trabajo",
+ "progress": "Progreso",
+ "optional": "Opcional",
+ "join": "Unirse",
+ "go_back": "Volver",
+ "continue": "Continuar",
+ "resend": "Reenviar",
+ "relations": "Relaciones",
+ "errors": {
+ "default": {
+ "title": "¡Error!",
+ "message": "Algo salió mal. Por favor, inténtalo de nuevo."
+ },
+ "required": "Este campo es obligatorio",
+ "entity_required": "{entity} es obligatorio"
+ },
+ "update_link": "Actualizar enlace",
+ "attach": "Adjuntar",
+ "create_new": "Crear nuevo",
+ "add_existing": "Agregar existente",
+ "type_or_paste_a_url": "Escribe o pega una URL",
+ "url_is_invalid": "La URL no es válida",
+ "display_title": "Título a mostrar",
+ "link_title_placeholder": "Cómo te gustaría ver este enlace",
+ "url": "URL",
+ "side_peek": "Vista lateral",
+ "modal": "Modal",
+ "full_screen": "Pantalla completa",
+ "close_peek_view": "Cerrar la vista previa",
+ "toggle_peek_view_layout": "Alternar diseño de vista previa",
+ "options": "Opciones",
+ "duration": "Duración",
+ "today": "Hoy",
+ "week": "Semana",
+ "month": "Mes",
+ "quarter": "Trimestre",
+ "press_for_commands": "Presiona '/' para comandos",
+ "click_to_add_description": "Haz clic para agregar descripción",
+ "search": {
+ "label": "Buscar",
+ "placeholder": "Escribe para buscar",
+ "no_matches_found": "No se encontraron coincidencias",
+ "no_matching_results": "No hay resultados coincidentes"
+ },
+ "actions": {
+ "edit": "Editar",
+ "make_a_copy": "Hacer una copia",
+ "open_in_new_tab": "Abrir en nueva pestaña",
+ "copy_link": "Copiar enlace",
+ "archive": "Archivar",
+ "delete": "Eliminar",
+ "remove_relation": "Eliminar relación",
+ "subscribe": "Suscribirse",
+ "unsubscribe": "Cancelar suscripción",
+ "clear_sorting": "Limpiar ordenamiento",
+ "show_weekends": "Mostrar fines de semana",
+ "enable": "Habilitar",
+ "disable": "Deshabilitar"
+ },
+ "name": "Nombre",
+ "discard": "Descartar",
+ "confirm": "Confirmar",
+ "confirming": "Confirmando",
+ "read_the_docs": "Leer la documentación",
+ "default": "Predeterminado",
+ "active": "Activo",
+ "enabled": "Habilitado",
+ "disabled": "Deshabilitado",
+ "mandate": "Mandato",
+ "mandatory": "Obligatorio",
+ "yes": "Sí",
+ "no": "No",
+ "please_wait": "Por favor espera",
+ "enabling": "Habilitando",
+ "disabling": "Deshabilitando",
+ "beta": "Beta",
+ "or": "o",
+ "next": "Siguiente",
+ "back": "Atrás",
+ "cancelling": "Cancelando",
+ "configuring": "Configurando",
+ "clear": "Limpiar",
+ "import": "Importar",
+ "connect": "Conectar",
+ "authorizing": "Autorizando",
+ "processing": "Procesando",
+ "no_data_available": "No hay datos disponibles",
+ "from": "de {name}",
+ "authenticated": "Autenticado",
+ "select": "Seleccionar",
+ "upgrade": "Mejorar",
+ "add_seats": "Agregar asientos",
+ "label": "Etiqueta",
+ "priorities": "Prioridades",
+ "projects": "Proyectos",
+ "workspace": "Espacio de trabajo",
+ "workspaces": "Espacios de trabajo",
+ "team": "Equipo",
+ "teams": "Equipos",
+ "entity": "Entidad",
+ "entities": "Entidades",
+ "task": "Tarea",
+ "tasks": "Tareas",
+ "section": "Sección",
+ "sections": "Secciones",
+ "edit": "Editar",
+ "connecting": "Conectando",
+ "connected": "Conectado",
+ "disconnect": "Desconectar",
+ "disconnecting": "Desconectando",
+ "installing": "Instalando",
+ "install": "Instalar",
+ "reset": "Reiniciar",
+ "live": "En vivo",
+ "change_history": "Historial de cambios",
+ "coming_soon": "Próximamente",
+ "members": "Miembros",
+ "you": "Tú",
+ "upgrade_cta": {
+ "higher_subscription": "Mejorar a una suscripción más alta",
+ "talk_to_sales": "Hablar con ventas"
+ },
+ "category": "Categoría",
+ "categories": "Categorías",
+ "saving": "Guardando",
+ "save_changes": "Guardar cambios",
+ "delete": "Eliminar",
+ "deleting": "Eliminando",
+ "pending": "Pendiente",
+ "invite": "Invitar"
+ },
+
+ "chart": {
+ "x_axis": "Eje X",
+ "y_axis": "Eje Y",
+ "metric": "Métrica"
+ },
+
+ "form": {
+ "title": {
+ "required": "El título es obligatorio",
+ "max_length": "El título debe tener menos de {length} caracteres"
+ }
+ },
+
+ "entity": {
+ "grouping_title": "Agrupación de {entity}",
+ "priority": "Prioridad de {entity}",
+ "all": "Todos los {entity}",
+ "drop_here_to_move": "Suelta aquí para mover el {entity}",
+ "delete": {
+ "label": "Eliminar {entity}",
+ "success": "{entity} eliminado correctamente",
+ "failed": "Error al eliminar {entity}"
+ },
+ "update": {
+ "failed": "Error al actualizar {entity}",
+ "success": "{entity} actualizado correctamente"
+ },
+ "link_copied_to_clipboard": "Enlace de {entity} copiado al portapapeles",
+ "fetch": {
+ "failed": "Error al obtener {entity}"
+ },
+ "add": {
+ "success": "{entity} agregado correctamente",
+ "failed": "Error al agregar {entity}"
+ }
+ },
+
+ "epic": {
+ "all": "Todos los Epics",
+ "label": "{count, plural, one {Epic} other {Epics}}",
+ "new": "Nuevo Epic",
+ "adding": "Agregando epic",
+ "create": {
+ "success": "Epic creado correctamente"
+ },
+ "add": {
+ "press_enter": "Presiona 'Enter' para agregar otro epic",
+ "label": "Agregar Epic"
+ },
+ "title": {
+ "label": "Título del Epic",
+ "required": "El título del epic es obligatorio."
+ }
+ },
+
+ "issue": {
+ "label": "{count, plural, one {Elemento de trabajo} other {Elementos de trabajo}}",
+ "all": "Todos los elementos de trabajo",
+ "edit": "Editar elemento de trabajo",
+ "title": {
+ "label": "Título del elemento de trabajo",
+ "required": "El título del elemento de trabajo es obligatorio."
+ },
+ "add": {
+ "press_enter": "Presiona 'Enter' para agregar otro elemento de trabajo",
+ "label": "Agregar elemento de trabajo",
+ "cycle": {
+ "failed": "No se pudo agregar el elemento de trabajo al ciclo. Por favor, inténtalo de nuevo.",
+ "success": "{count, plural, one {Elemento de trabajo agregado} other {Elementos de trabajo agregados}} al ciclo correctamente.",
+ "loading": "Agregando {count, plural, one {elemento de trabajo} other {elementos de trabajo}} al ciclo"
+ },
+ "assignee": "Agregar asignados",
+ "start_date": "Agregar fecha de inicio",
+ "due_date": "Agregar fecha de vencimiento",
+ "parent": "Agregar elemento de trabajo padre",
+ "sub_issue": "Agregar sub-elemento de trabajo",
+ "relation": "Agregar relación",
+ "link": "Agregar enlace",
+ "existing": "Agregar elemento de trabajo existente"
+ },
+ "remove": {
+ "label": "Eliminar elemento de trabajo",
+ "cycle": {
+ "loading": "Eliminando elemento de trabajo del ciclo",
+ "success": "Elemento de trabajo eliminado del ciclo correctamente.",
+ "failed": "No se pudo eliminar el elemento de trabajo del ciclo. Por favor, inténtalo de nuevo."
+ },
+ "module": {
+ "loading": "Eliminando elemento de trabajo del módulo",
+ "success": "Elemento de trabajo eliminado del módulo correctamente.",
+ "failed": "No se pudo eliminar el elemento de trabajo del módulo. Por favor, inténtalo de nuevo."
+ },
+ "parent": {
+ "label": "Eliminar elemento de trabajo padre"
+ }
+ },
+ "new": "Nuevo elemento de trabajo",
+ "adding": "Agregando elemento de trabajo",
+ "create": {
+ "success": "Elemento de trabajo creado correctamente"
+ },
+ "priority": {
+ "urgent": "Urgente",
+ "high": "Alta",
+ "medium": "Media",
+ "low": "Baja"
+ },
+ "display": {
+ "properties": {
+ "label": "Mostrar propiedades",
+ "id": "ID",
+ "issue_type": "Tipo de elemento de trabajo",
+ "sub_issue_count": "Cantidad de sub-elementos",
+ "attachment_count": "Cantidad de archivos adjuntos",
+ "created_on": "Creado el",
+ "sub_issue": "Sub-elemento de trabajo"
+ },
+ "extra": {
+ "show_sub_issues": "Mostrar sub-elementos",
+ "show_empty_groups": "Mostrar grupos vacíos"
+ }
+ },
+ "layouts": {
+ "ordered_by_label": "Este diseño está ordenado por",
+ "list": "Lista",
+ "kanban": "Tablero",
+ "calendar": "Calendario",
+ "spreadsheet": "Tabla",
+ "gantt": "Línea de tiempo",
+ "title": {
+ "list": "Diseño de lista",
+ "kanban": "Diseño de tablero",
+ "calendar": "Diseño de calendario",
+ "spreadsheet": "Diseño de tabla",
+ "gantt": "Diseño de línea de tiempo"
+ }
+ },
+ "states": {
+ "active": "Activo",
+ "backlog": "Pendientes"
+ },
+ "comments": {
+ "placeholder": "Agregar comentario",
+ "switch": {
+ "private": "Cambiar a comentario privado",
+ "public": "Cambiar a comentario público"
+ },
+ "create": {
+ "success": "Comentario creado correctamente",
+ "error": "Error al crear el comentario. Por favor, inténtalo más tarde."
+ },
+ "update": {
+ "success": "Comentario actualizado correctamente",
+ "error": "Error al actualizar el comentario. Por favor, inténtalo más tarde."
+ },
+ "remove": {
+ "success": "Comentario eliminado correctamente",
+ "error": "Error al eliminar el comentario. Por favor, inténtalo más tarde."
+ },
+ "upload": {
+ "error": "Error al subir el archivo. Por favor, inténtalo más tarde."
+ }
+ },
+ "empty_state": {
+ "issue_detail": {
+ "title": "El elemento de trabajo no existe",
+ "description": "El elemento de trabajo que buscas no existe, ha sido archivado o ha sido eliminado.",
+ "primary_button": {
+ "text": "Ver otros elementos de trabajo"
+ }
+ }
+ },
+ "sibling": {
+ "label": "Elementos de trabajo hermanos"
+ },
+ "archive": {
+ "description": "Solo los elementos de trabajo completados\no cancelados pueden ser archivados",
+ "label": "Archivar elemento de trabajo",
+ "confirm_message": "¿Estás seguro de que quieres archivar el elemento de trabajo? Todos tus elementos archivados pueden ser restaurados más tarde.",
+ "success": {
+ "label": "Archivo exitoso",
+ "message": "Tus archivos se pueden encontrar en los archivos del proyecto."
+ },
+ "failed": {
+ "message": "No se pudo archivar el elemento de trabajo. Por favor, inténtalo de nuevo."
+ }
+ },
+ "restore": {
+ "success": {
+ "title": "Restauración exitosa",
+ "message": "Tu elemento de trabajo se puede encontrar en los elementos de trabajo del proyecto."
+ },
+ "failed": {
+ "message": "No se pudo restaurar el elemento de trabajo. Por favor, inténtalo de nuevo."
+ }
+ },
+ "relation": {
+ "relates_to": "Se relaciona con",
+ "duplicate": "Duplicado de",
+ "blocked_by": "Bloqueado por",
+ "blocking": "Bloqueando"
+ },
+ "copy_link": "Copiar enlace del elemento de trabajo",
+ "delete": {
+ "label": "Eliminar elemento de trabajo",
+ "error": "Error al eliminar el elemento de trabajo"
+ },
+ "subscription": {
+ "actions": {
+ "subscribed": "Suscrito al elemento de trabajo correctamente",
+ "unsubscribed": "Desuscrito del elemento de trabajo correctamente"
+ }
+ },
+ "select": {
+ "error": "Por favor selecciona al menos un elemento de trabajo",
+ "empty": "No hay elementos de trabajo seleccionados",
+ "add_selected": "Agregar elementos seleccionados"
+ },
+ "open_in_full_screen": "Abrir elemento de trabajo en pantalla completa"
+ },
+
+ "attachment": {
+ "error": "No se pudo adjuntar el archivo. Intenta subirlo de nuevo.",
+ "only_one_file_allowed": "Solo se puede subir un archivo a la vez.",
+ "file_size_limit": "El archivo debe tener {size}MB o menos de tamaño.",
+ "drag_and_drop": "Arrastra y suelta en cualquier lugar para subir",
+ "delete": "Eliminar archivo adjunto"
+ },
+
+ "label": {
+ "select": "Seleccionar etiqueta",
+ "create": {
+ "success": "Etiqueta creada correctamente",
+ "failed": "Error al crear la etiqueta",
+ "already_exists": "La etiqueta ya existe",
+ "type": "Escribe para agregar una nueva etiqueta"
+ }
+ },
+
+ "sub_work_item": {
+ "update": {
+ "success": "Sub-elemento actualizado correctamente",
+ "error": "Error al actualizar el sub-elemento"
+ },
+ "remove": {
+ "success": "Sub-elemento eliminado correctamente",
+ "error": "Error al eliminar el sub-elemento"
+ }
+ },
+
+ "view": {
+ "label": "{count, plural, one {Vista} other {Vistas}}",
+ "create": {
+ "label": "Crear vista"
+ },
+ "update": {
+ "label": "Actualizar vista"
+ }
+ },
+
+ "inbox_issue": {
+ "status": {
+ "pending": {
+ "title": "Pendiente",
+ "description": "Pendiente"
+ },
+ "declined": {
+ "title": "Rechazado",
+ "description": "Rechazado"
+ },
+ "snoozed": {
+ "title": "Pospuesto",
+ "description": "Faltan {days, plural, one{# día} other{# días}}"
+ },
+ "accepted": {
+ "title": "Aceptado",
+ "description": "Aceptado"
+ },
+ "duplicate": {
+ "title": "Duplicado",
+ "description": "Duplicado"
+ }
+ },
+ "modals": {
+ "decline": {
+ "title": "Rechazar elemento de trabajo",
+ "content": "¿Estás seguro de que quieres rechazar el elemento de trabajo {value}?"
+ },
+ "delete": {
+ "title": "Eliminar elemento de trabajo",
+ "content": "¿Estás seguro de que quieres eliminar el elemento de trabajo {value}?",
+ "success": "Elemento de trabajo eliminado correctamente"
+ }
+ },
+ "errors": {
+ "snooze_permission": "Solo los administradores del proyecto pueden posponer/desposponer elementos de trabajo",
+ "accept_permission": "Solo los administradores del proyecto pueden aceptar elementos de trabajo",
+ "decline_permission": "Solo los administradores del proyecto pueden rechazar elementos de trabajo"
+ },
+ "actions": {
+ "accept": "Aceptar",
+ "decline": "Rechazar",
+ "snooze": "Posponer",
+ "unsnooze": "Desposponer",
+ "copy": "Copiar enlace del elemento de trabajo",
+ "delete": "Eliminar",
+ "open": "Abrir elemento de trabajo",
+ "mark_as_duplicate": "Marcar como duplicado",
+ "move": "Mover {value} a elementos de trabajo del proyecto"
+ },
+ "source": {
+ "in-app": "en-app"
+ },
+ "order_by": {
+ "created_at": "Creado el",
+ "updated_at": "Actualizado el",
+ "id": "ID"
+ },
+ "label": "Intake",
+ "page_label": "{workspace} - Intake",
+ "modal": {
+ "title": "Crear elemento de trabajo de intake"
+ },
+ "tabs": {
+ "open": "Abiertos",
+ "closed": "Cerrados"
+ },
+ "empty_state": {
+ "sidebar_open_tab": {
+ "title": "No hay elementos de trabajo abiertos",
+ "description": "Encuentra elementos de trabajo abiertos aquí. Crea un nuevo elemento de trabajo."
+ },
+ "sidebar_closed_tab": {
+ "title": "No hay elementos de trabajo cerrados",
+ "description": "Todos los elementos de trabajo, ya sean aceptados o rechazados, se pueden encontrar aquí."
+ },
+ "sidebar_filter": {
+ "title": "No hay elementos de trabajo coincidentes",
+ "description": "Ningún elemento de trabajo coincide con el filtro aplicado en intake. Crea un nuevo elemento de trabajo."
+ },
+ "detail": {
+ "title": "Selecciona un elemento de trabajo para ver sus detalles."
+ }
+ }
+ },
+
+ "workspace_creation": {
+ "heading": "Crea tu espacio de trabajo",
+ "subheading": "Para comenzar a usar Plane, necesitas crear o unirte a un espacio de trabajo.",
+ "form": {
+ "name": {
+ "label": "Nombra tu espacio de trabajo",
+ "placeholder": "Algo familiar y reconocible es siempre lo mejor."
+ },
+ "url": {
+ "label": "Establece la URL de tu espacio de trabajo",
+ "placeholder": "Escribe o pega una URL",
+ "edit_slug": "Solo puedes editar el slug de la URL"
+ },
+ "organization_size": {
+ "label": "¿Cuántas personas usarán este espacio de trabajo?",
+ "placeholder": "Selecciona un rango"
+ }
+ },
+ "errors": {
+ "creation_disabled": {
+ "title": "Solo el administrador de tu instancia puede crear espacios de trabajo",
+ "description": "Si conoces la dirección de correo electrónico del administrador de tu instancia, haz clic en el botón de abajo para ponerte en contacto con él.",
+ "request_button": "Solicitar administrador de instancia"
+ },
+ "validation": {
+ "name_alphanumeric": "Los nombres de espacios de trabajo solo pueden contener (' '), ('-'), ('_') y caracteres alfanuméricos.",
+ "name_length": "Limita tu nombre a 80 caracteres.",
+ "url_alphanumeric": "Las URLs solo pueden contener ('-') y caracteres alfanuméricos.",
+ "url_length": "Limita tu URL a 48 caracteres.",
+ "url_already_taken": "¡La URL del espacio de trabajo ya está en uso!"
+ }
+ },
+ "request_email": {
+ "subject": "Solicitando un nuevo espacio de trabajo",
+ "body": "Hola administrador(es) de instancia,\n\nPor favor, crea un nuevo espacio de trabajo con la URL [/nombre-espacio-trabajo] para [propósito de crear el espacio de trabajo].\n\nGracias,\n{firstName} {lastName}\n{email}"
+ },
+ "button": {
+ "default": "Crear espacio de trabajo",
+ "loading": "Creando espacio de trabajo"
+ },
+ "toast": {
+ "success": {
+ "title": "Éxito",
+ "message": "Espacio de trabajo creado correctamente"
+ },
+ "error": {
+ "title": "Error",
+ "message": "No se pudo crear el espacio de trabajo. Por favor, inténtalo de nuevo."
+ }
+ }
+ },
+
+ "workspace_dashboard": {
+ "empty_state": {
+ "general": {
+ "title": "Resumen de tus proyectos, actividad y métricas",
+ "description": "Bienvenido a Plane, estamos emocionados de tenerte aquí. Crea tu primer proyecto y rastrea tus elementos de trabajo, y esta página se transformará en un espacio que te ayuda a progresar. Los administradores también verán elementos que ayudan a su equipo a progresar.",
+ "primary_button": {
+ "text": "Construye tu primer proyecto",
+ "comic": {
+ "title": "Todo comienza con un proyecto en Plane",
+ "description": "Un proyecto podría ser la hoja de ruta de un producto, una campaña de marketing o el lanzamiento de un nuevo automóvil."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_analytics": {
+ "label": "Análisis",
+ "page_label": "{workspace} - Análisis",
+ "open_tasks": "Total de tareas abiertas",
+ "error": "Hubo un error al obtener los datos.",
+ "work_items_closed_in": "Elementos de trabajo cerrados en",
+ "selected_projects": "Proyectos seleccionados",
+ "total_members": "Total de miembros",
+ "total_cycles": "Total de Ciclos",
+ "total_modules": "Total de Módulos",
+ "pending_work_items": {
+ "title": "Elementos de trabajo pendientes",
+ "empty_state": "El análisis de elementos de trabajo pendientes por compañeros aparece aquí."
+ },
+ "work_items_closed_in_a_year": {
+ "title": "Elementos de trabajo cerrados en un año",
+ "empty_state": "Cierra elementos de trabajo para ver su análisis en forma de gráfico."
+ },
+ "most_work_items_created": {
+ "title": "Más elementos de trabajo creados",
+ "empty_state": "Los compañeros y el número de elementos de trabajo creados por ellos aparecen aquí."
+ },
+ "most_work_items_closed": {
+ "title": "Más elementos de trabajo cerrados",
+ "empty_state": "Los compañeros y el número de elementos de trabajo cerrados por ellos aparecen aquí."
+ },
+ "tabs": {
+ "scope_and_demand": "Alcance y Demanda",
+ "custom": "Análisis Personalizado"
+ },
+ "empty_state": {
+ "general": {
+ "title": "Rastrea el progreso, cargas de trabajo y asignaciones. Identifica tendencias, elimina bloqueos y mueve el trabajo más rápido",
+ "description": "Observa el alcance versus la demanda, estimaciones y el aumento del alcance. Obtén el rendimiento por miembros del equipo y equipos, y asegúrate de que tu proyecto se ejecute a tiempo.",
+ "primary_button": {
+ "text": "Inicia tu primer proyecto",
+ "comic": {
+ "title": "El análisis funciona mejor con Ciclos + Módulos",
+ "description": "Primero, organiza tus elementos de trabajo en Ciclos y, si puedes, agrupa los elementos de trabajo que abarcan más de un ciclo en Módulos. Revisa ambos en la navegación izquierda."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_projects": {
+ "label": "{count, plural, one {Proyecto} other {Proyectos}}",
+ "create": {
+ "label": "Agregar Proyecto"
+ },
+ "network": {
+ "private": {
+ "title": "Privado",
+ "description": "Accesible solo por invitación"
+ },
+ "public": {
+ "title": "Público",
+ "description": "Cualquiera en el espacio de trabajo excepto Invitados puede unirse"
+ }
+ },
+ "error": {
+ "permission": "No tienes permiso para realizar esta acción.",
+ "cycle_delete": "Error al eliminar el ciclo",
+ "module_delete": "Error al eliminar el módulo",
+ "issue_delete": "Error al eliminar el elemento de trabajo"
+ },
+ "state": {
+ "backlog": "Pendiente",
+ "unstarted": "Sin iniciar",
+ "started": "Iniciado",
+ "completed": "Completado",
+ "cancelled": "Cancelado"
+ },
+ "sort": {
+ "manual": "Manual",
+ "name": "Nombre",
+ "created_at": "Fecha de creación",
+ "members_length": "Número de miembros"
+ },
+ "scope": {
+ "my_projects": "Mis proyectos",
+ "archived_projects": "Archivados"
+ },
+ "common": {
+ "months_count": "{months, plural, one{# mes} other{# meses}}"
+ },
+ "empty_state": {
+ "general": {
+ "title": "No hay proyectos activos",
+ "description": "Piensa en cada proyecto como el padre para el trabajo orientado a objetivos. Los proyectos son donde viven las Tareas, Ciclos y Módulos y, junto con tus colegas, te ayudan a alcanzar ese objetivo. Crea un nuevo proyecto o filtra por proyectos archivados.",
+ "primary_button": {
+ "text": "Inicia tu primer proyecto",
+ "comic": {
+ "title": "Todo comienza con un proyecto en Plane",
+ "description": "Un proyecto podría ser la hoja de ruta de un producto, una campaña de marketing o el lanzamiento de un nuevo automóvil."
+ }
+ }
+ },
+ "no_projects": {
+ "title": "Sin proyecto",
+ "description": "Para crear elementos de trabajo o gestionar tu trabajo, necesitas crear un proyecto o ser parte de uno.",
+ "primary_button": {
+ "text": "Inicia tu primer proyecto",
+ "comic": {
+ "title": "Todo comienza con un proyecto en Plane",
+ "description": "Un proyecto podría ser la hoja de ruta de un producto, una campaña de marketing o el lanzamiento de un nuevo automóvil."
+ }
+ }
+ },
+ "filter": {
+ "title": "No hay proyectos coincidentes",
+ "description": "No se detectaron proyectos con los criterios coincidentes. \n Crea un nuevo proyecto en su lugar."
+ },
+ "search": {
+ "description": "No se detectaron proyectos con los criterios coincidentes.\nCrea un nuevo proyecto en su lugar"
+ }
+ }
+ },
+
+ "workspace_views": {
+ "add_view": "Agregar vista",
+ "empty_state": {
+ "all-issues": {
+ "title": "No hay elementos de trabajo en el proyecto",
+ "description": "¡Primer proyecto completado! Ahora, divide tu trabajo en piezas rastreables con elementos de trabajo. ¡Vamos!",
+ "primary_button": {
+ "text": "Crear nuevo elemento de trabajo"
+ }
+ },
+ "assigned": {
+ "title": "No hay elementos de trabajo aún",
+ "description": "Los elementos de trabajo asignados a ti se pueden rastrear desde aquí.",
+ "primary_button": {
+ "text": "Crear nuevo elemento de trabajo"
+ }
+ },
+ "created": {
+ "title": "No hay elementos de trabajo aún",
+ "description": "Todos los elementos de trabajo creados por ti vienen aquí, rastréalos aquí directamente.",
+ "primary_button": {
+ "text": "Crear nuevo elemento de trabajo"
+ }
+ },
+ "subscribed": {
+ "title": "No hay elementos de trabajo aún",
+ "description": "Suscríbete a los elementos de trabajo que te interesan, rastréalos todos aquí."
+ },
+ "custom-view": {
+ "title": "No hay elementos de trabajo aún",
+ "description": "Elementos de trabajo que aplican a los filtros, rastréalos todos aquí."
+ }
+ }
+ },
+
+ "workspace_settings": {
+ "label": "Configuración del espacio de trabajo",
+ "page_label": "{workspace} - Configuración general",
+ "key_created": "Clave creada",
+ "copy_key": "Copia y guarda esta clave secreta en Plane Pages. No podrás ver esta clave después de hacer clic en Cerrar. Se ha descargado un archivo CSV que contiene la clave.",
+ "token_copied": "Token copiado al portapapeles.",
+ "settings": {
+ "general": {
+ "title": "General",
+ "upload_logo": "Subir logo",
+ "edit_logo": "Editar logo",
+ "name": "Nombre del espacio de trabajo",
+ "company_size": "Tamaño de la empresa",
+ "url": "URL del espacio de trabajo",
+ "update_workspace": "Actualizar espacio de trabajo",
+ "delete_workspace": "Eliminar este espacio de trabajo",
+ "delete_workspace_description": "Al eliminar un espacio de trabajo, todos los datos y recursos dentro de ese espacio se eliminarán permanentemente y no podrán recuperarse.",
+ "delete_btn": "Eliminar este espacio de trabajo",
+ "delete_modal": {
+ "title": "¿Está seguro de que desea eliminar este espacio de trabajo?",
+ "description": "Tiene una prueba activa de uno de nuestros planes de pago. Por favor, cancelela primero para continuar.",
+ "dismiss": "Descartar",
+ "cancel": "Cancelar prueba",
+ "success_title": "Espacio de trabajo eliminado.",
+ "success_message": "Pronto irá a su página de perfil.",
+ "error_title": "Eso no funcionó.",
+ "error_message": "Por favor, inténtelo de nuevo."
+ },
+ "errors": {
+ "name": {
+ "required": "El nombre es obligatorio",
+ "max_length": "El nombre del espacio de trabajo no debe exceder los 80 caracteres"
+ },
+ "company_size": {
+ "required": "El tamaño de la empresa es obligatorio"
+ }
+ }
+ },
+ "members": {
+ "title": "Miembros",
+ "add_member": "Agregar miembro",
+ "pending_invites": "Invitaciones pendientes",
+ "invitations_sent_successfully": "Invitaciones enviadas exitosamente",
+ "leave_confirmation": "¿Estás seguro de que quieres abandonar el espacio de trabajo? Ya no tendrás acceso a este espacio de trabajo. Esta acción no se puede deshacer.",
+ "details": {
+ "full_name": "Nombre completo",
+ "display_name": "Nombre para mostrar",
+ "email_address": "Dirección de correo electrónico",
+ "account_type": "Tipo de cuenta",
+ "authentication": "Autenticación",
+ "joining_date": "Fecha de incorporación"
+ },
+ "modal": {
+ "title": "Invitar personas a colaborar",
+ "description": "Invita personas a colaborar en tu espacio de trabajo.",
+ "button": "Enviar invitaciones",
+ "button_loading": "Enviando invitaciones",
+ "placeholder": "nombre@empresa.com",
+ "errors": {
+ "required": "Necesitamos una dirección de correo electrónico para invitarlos.",
+ "invalid": "El correo electrónico no es válido"
+ }
+ }
+ },
+ "billing_and_plans": {
+ "title": "Facturación y Planes",
+ "current_plan": "Plan actual",
+ "free_plan": "Actualmente estás usando el plan gratuito",
+ "view_plans": "Ver planes"
+ },
+ "exports": {
+ "title": "Exportaciones",
+ "exporting": "Exportando",
+ "previous_exports": "Exportaciones anteriores",
+ "export_separate_files": "Exportar los datos en archivos separados",
+ "modal": {
+ "title": "Exportar a",
+ "toasts": {
+ "success": {
+ "title": "Exportación exitosa",
+ "message": "Podrás descargar el {entity} exportado desde la exportación anterior."
+ },
+ "error": {
+ "title": "Exportación fallida",
+ "message": "La exportación no tuvo éxito. Por favor, inténtalo de nuevo."
+ }
+ }
+ }
+ },
+ "webhooks": {
+ "title": "Webhooks",
+ "add_webhook": "Agregar webhook",
+ "modal": {
+ "title": "Crear webhook",
+ "details": "Detalles del webhook",
+ "payload": "URL del payload",
+ "question": "¿Qué eventos te gustaría que activaran este webhook?",
+ "error": "La URL es obligatoria"
+ },
+ "secret_key": {
+ "title": "Clave secreta",
+ "message": "Genera un token para iniciar sesión en el payload del webhook"
+ },
+ "options": {
+ "all": "Envíame todo",
+ "individual": "Seleccionar eventos individuales"
+ },
+ "toasts": {
+ "created": {
+ "title": "Webhook creado",
+ "message": "El webhook se ha creado exitosamente"
+ },
+ "not_created": {
+ "title": "Webhook no creado",
+ "message": "No se pudo crear el webhook"
+ },
+ "updated": {
+ "title": "Webhook actualizado",
+ "message": "El webhook se ha actualizado exitosamente"
+ },
+ "not_updated": {
+ "title": "Webhook no actualizado",
+ "message": "No se pudo actualizar el webhook"
+ },
+ "removed": {
+ "title": "Webhook eliminado",
+ "message": "El webhook se ha eliminado exitosamente"
+ },
+ "not_removed": {
+ "title": "Webhook no eliminado",
+ "message": "No se pudo eliminar el webhook"
+ },
+ "secret_key_copied": {
+ "message": "Clave secreta copiada al portapapeles."
+ },
+ "secret_key_not_copied": {
+ "message": "Ocurrió un error al copiar la clave secreta."
+ }
+ }
+ },
+ "api_tokens": {
+ "title": "Tokens de API",
+ "add_token": "Agregar token de API",
+ "create_token": "Crear token",
+ "never_expires": "Nunca expira",
+ "generate_token": "Generar token",
+ "generating": "Generando",
+ "delete": {
+ "title": "Eliminar token de API",
+ "description": "Cualquier aplicación que use este token ya no tendrá acceso a los datos de Plane. Esta acción no se puede deshacer.",
+ "success": {
+ "title": "¡Éxito!",
+ "message": "El token de API se ha eliminado exitosamente"
+ },
+ "error": {
+ "title": "¡Error!",
+ "message": "No se pudo eliminar el token de API"
+ }
+ }
+ }
+ },
+ "empty_state": {
+ "api_tokens": {
+ "title": "No se han creado tokens de API",
+ "description": "Las APIs de Plane se pueden usar para integrar tus datos en Plane con cualquier sistema externo. Crea un token para comenzar."
+ },
+ "webhooks": {
+ "title": "No se han agregado webhooks",
+ "description": "Crea webhooks para recibir actualizaciones en tiempo real y automatizar acciones."
+ },
+ "exports": {
+ "title": "No hay exportaciones aún",
+ "description": "Cada vez que exportes, también tendrás una copia aquí para referencia."
+ },
+ "imports": {
+ "title": "No hay importaciones aún",
+ "description": "Encuentra todas tus importaciones anteriores aquí y descárgalas."
+ }
+ }
+ },
+
+ "profile": {
+ "label": "Perfil",
+ "page_label": "Tu trabajo",
+ "work": "Trabajo",
+ "details": {
+ "joined_on": "Se unió el",
+ "time_zone": "Zona horaria"
+ },
+ "stats": {
+ "workload": "Carga de trabajo",
+ "overview": "Resumen",
+ "created": "Elementos de trabajo creados",
+ "assigned": "Elementos de trabajo asignados",
+ "subscribed": "Elementos de trabajo suscritos",
+ "state_distribution": {
+ "title": "Elementos de trabajo por estado",
+ "empty": "Crea elementos de trabajo para verlos por estados en el gráfico para un mejor análisis."
+ },
+ "priority_distribution": {
+ "title": "Elementos de trabajo por Prioridad",
+ "empty": "Crea elementos de trabajo para verlos por prioridad en el gráfico para un mejor análisis."
+ },
+ "recent_activity": {
+ "title": "Actividad reciente",
+ "empty": "No pudimos encontrar datos. Por favor revisa tus entradas",
+ "button": "Descargar actividad de hoy",
+ "button_loading": "Descargando"
+ }
+ },
+ "actions": {
+ "profile": "Perfil",
+ "security": "Seguridad",
+ "activity": "Actividad",
+ "appearance": "Apariencia",
+ "notifications": "Notificaciones"
+ },
+ "tabs": {
+ "summary": "Resumen",
+ "assigned": "Asignado",
+ "created": "Creado",
+ "subscribed": "Suscrito",
+ "activity": "Actividad"
+ },
+ "empty_state": {
+ "activity": {
+ "title": "Aún no hay actividades",
+ "description": "¡Comienza creando un nuevo elemento de trabajo! Agrégale detalles y propiedades. Explora más en Plane para ver tu actividad."
+ },
+ "assigned": {
+ "title": "No hay elementos de trabajo asignados a ti",
+ "description": "Los elementos de trabajo asignados a ti se pueden rastrear desde aquí."
+ },
+ "created": {
+ "title": "Aún no hay elementos de trabajo",
+ "description": "Todos los elementos de trabajo creados por ti aparecen aquí, rastréalos directamente aquí."
+ },
+ "subscribed": {
+ "title": "Aún no hay elementos de trabajo",
+ "description": "Suscríbete a los elementos de trabajo que te interesen, rastréalos todos aquí."
+ }
+ }
+ },
+
+ "project_settings": {
+ "general": {
+ "enter_project_id": "Ingresa el ID del proyecto",
+ "please_select_a_timezone": "Por favor selecciona una zona horaria",
+ "archive_project": {
+ "title": "Archivar proyecto",
+ "description": "Archivar un proyecto lo eliminará de tu navegación lateral aunque aún podrás acceder a él desde tu página de proyectos. Puedes restaurar el proyecto o eliminarlo cuando quieras.",
+ "button": "Archivar proyecto"
+ },
+ "delete_project": {
+ "title": "Eliminar proyecto",
+ "description": "Al eliminar un proyecto, todos los datos y recursos dentro de ese proyecto se eliminarán permanentemente y no podrán recuperarse.",
+ "button": "Eliminar mi proyecto"
+ },
+ "toast": {
+ "success": "Proyecto actualizado exitosamente",
+ "error": "No se pudo actualizar el proyecto. Por favor intenta de nuevo."
+ }
+ },
+ "members": {
+ "label": "Miembros",
+ "project_lead": "Líder del proyecto",
+ "default_assignee": "Asignado por defecto",
+ "guest_super_permissions": {
+ "title": "Otorgar acceso de visualización a todos los elementos de trabajo para usuarios invitados:",
+ "sub_heading": "Esto permitirá a los invitados tener acceso de visualización a todos los elementos de trabajo del proyecto."
+ },
+ "invite_members": {
+ "title": "Invitar miembros",
+ "sub_heading": "Invita miembros para trabajar en tu proyecto.",
+ "select_co_worker": "Seleccionar compañero de trabajo"
+ }
+ },
+ "states": {
+ "describe_this_state_for_your_members": "Describe este estado para tus miembros.",
+ "empty_state": {
+ "title": "No estados disponibles para el grupo {groupKey}",
+ "description": "Por favor, crea un nuevo estado"
+ }
+ },
+ "labels": {
+ "label_title": "Título de la etiqueta",
+ "label_title_is_required": "El título de la etiqueta es requerido",
+ "label_max_char": "El nombre de la etiqueta no debe exceder 255 caracteres",
+ "toast": {
+ "error": "Error al actualizar la etiqueta"
+ }
+ },
+ "estimates": {
+ "title": "Habilitar estimaciones para mi proyecto",
+ "description": "Ayudan a comunicar la complejidad y la carga de trabajo del equipo."
+ },
+ "automations": {
+ "label": "Automatizaciones",
+ "auto-archive": {
+ "title": "Auto-archivar elementos de trabajo cerrados",
+ "description": "Plane archivará automáticamente los elementos de trabajo que se hayan completado o cancelado.",
+ "duration": "Auto-archivar elementos de trabajo que están cerrados por"
+ },
+ "auto-close": {
+ "title": "Auto-cerrar elementos de trabajo",
+ "description": "Plane cerrará automáticamente los elementos de trabajo que no se hayan completado o cancelado.",
+ "duration": "Auto-cerrar elementos de trabajo que están inactivos por",
+ "auto_close_status": "Estado de auto-cierre"
+ }
+ },
+
+ "empty_state": {
+ "labels": {
+ "title": "Aún no hay etiquetas",
+ "description": "Crea etiquetas para ayudar a organizar y filtrar elementos de trabajo en tu proyecto."
+ },
+ "estimates": {
+ "title": "Aún no hay sistemas de estimación",
+ "description": "Crea un conjunto de estimaciones para comunicar la cantidad de trabajo por elemento de trabajo.",
+ "primary_button": "Agregar sistema de estimación"
+ }
+ }
+ },
+
+ "project_cycles": {
+ "add_cycle": "Agregar ciclo",
+ "more_details": "Más detalles",
+ "cycle": "Ciclo",
+ "update_cycle": "Actualizar ciclo",
+ "create_cycle": "Crear ciclo",
+ "no_matching_cycles": "No hay ciclos coincidentes",
+ "remove_filters_to_see_all_cycles": "Elimina los filtros para ver todos los ciclos",
+ "remove_search_criteria_to_see_all_cycles": "Elimina los criterios de búsqueda para ver todos los ciclos",
+ "only_completed_cycles_can_be_archived": "Solo los ciclos completados pueden ser archivados",
+ "active_cycle": {
+ "label": "Ciclo activo",
+ "progress": "Progreso",
+ "chart": "Gráfico de avance",
+ "priority_issue": "Elementos de trabajo prioritarios",
+ "assignees": "Asignados",
+ "issue_burndown": "Avance de elementos de trabajo",
+ "ideal": "Ideal",
+ "current": "Actual",
+ "labels": "Etiquetas"
+ },
+ "upcoming_cycle": {
+ "label": "Ciclo próximo"
+ },
+ "completed_cycle": {
+ "label": "Ciclo completado"
+ },
+ "status": {
+ "days_left": "Días restantes",
+ "completed": "Completado",
+ "yet_to_start": "Por comenzar",
+ "in_progress": "En progreso",
+ "draft": "Borrador"
+ },
+ "action": {
+ "restore": {
+ "title": "Restaurar ciclo",
+ "success": {
+ "title": "Ciclo restaurado",
+ "description": "El ciclo ha sido restaurado."
+ },
+ "failed": {
+ "title": "Falló la restauración del ciclo",
+ "description": "No se pudo restaurar el ciclo. Por favor intenta de nuevo."
+ }
+ },
+ "favorite": {
+ "loading": "Agregando ciclo a favoritos",
+ "success": {
+ "description": "Ciclo agregado a favoritos.",
+ "title": "¡Éxito!"
+ },
+ "failed": {
+ "description": "No se pudo agregar el ciclo a favoritos. Por favor intenta de nuevo.",
+ "title": "¡Error!"
+ }
+ },
+ "unfavorite": {
+ "loading": "Eliminando ciclo de favoritos",
+ "success": {
+ "description": "Ciclo eliminado de favoritos.",
+ "title": "¡Éxito!"
+ },
+ "failed": {
+ "description": "No se pudo eliminar el ciclo de favoritos. Por favor intenta de nuevo.",
+ "title": "¡Error!"
+ }
+ },
+ "update": {
+ "loading": "Actualizando ciclo",
+ "success": {
+ "description": "Ciclo actualizado exitosamente.",
+ "title": "¡Éxito!"
+ },
+ "failed": {
+ "description": "Error al actualizar el ciclo. Por favor intenta de nuevo.",
+ "title": "¡Error!"
+ },
+ "error": {
+ "already_exists": "Ya tienes un ciclo en las fechas dadas, si quieres crear un ciclo en borrador, puedes hacerlo eliminando ambas fechas."
+ }
+ }
+ },
+ "empty_state": {
+ "general": {
+ "title": "Agrupa y delimita tu trabajo en Ciclos.",
+ "description": "Divide el trabajo en bloques de tiempo, trabaja hacia atrás desde la fecha límite de tu proyecto para establecer fechas, y haz un progreso tangible como equipo.",
+ "primary_button": {
+ "text": "Establece tu primer ciclo",
+ "comic": {
+ "title": "Los ciclos son bloques de tiempo repetitivos.",
+ "description": "Un sprint, una iteración, o cualquier otro término que uses para el seguimiento semanal o quincenal del trabajo es un ciclo."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "No hay elementos de trabajo agregados al ciclo",
+ "description": "Agrega o crea elementos de trabajo que desees delimitar y entregar dentro de este ciclo",
+ "primary_button": {
+ "text": "Crear nuevo elemento de trabajo"
+ },
+ "secondary_button": {
+ "text": "Agregar elemento de trabajo existente"
+ }
+ },
+ "completed_no_issues": {
+ "title": "No hay elementos de trabajo en el ciclo",
+ "description": "No hay elementos de trabajo en el ciclo. Los elementos de trabajo están transferidos u ocultos. Para ver elementos de trabajo ocultos si los hay, actualiza tus propiedades de visualización según corresponda."
+ },
+ "active": {
+ "title": "No hay ciclo activo",
+ "description": "Un ciclo activo incluye cualquier período que abarque la fecha de hoy dentro de su rango. Encuentra el progreso y los detalles del ciclo activo aquí."
+ },
+ "archived": {
+ "title": "Aún no hay ciclos archivados",
+ "description": "Para mantener ordenado tu proyecto, archiva los ciclos completados. Encuéntralos aquí una vez archivados."
+ }
+ }
+ },
+
+ "project_issues": {
+ "empty_state": {
+ "no_issues": {
+ "title": "Crea un elemento de trabajo y asígnalo a alguien, incluso a ti mismo",
+ "description": "Piensa en los elementos de trabajo como trabajos, tareas, trabajo o JTBD. Los cuales nos gustan. Un elemento de trabajo y sus sub-elementos de trabajo son generalmente acciones basadas en tiempo asignadas a miembros de tu equipo. Tu equipo crea, asigna y completa elementos de trabajo para mover tu proyecto hacia su objetivo.",
+ "primary_button": {
+ "text": "Crea tu primer elemento de trabajo",
+ "comic": {
+ "title": "Los elementos de trabajo son bloques de construcción en Plane.",
+ "description": "Rediseñar la interfaz de Plane, Cambiar la marca de la empresa o Lanzar el nuevo sistema de inyección de combustible son ejemplos de elementos de trabajo que probablemente tienen sub-elementos de trabajo."
+ }
+ }
+ },
+ "no_archived_issues": {
+ "title": "Aún no hay elementos de trabajo archivados",
+ "description": "Manualmente o a través de automatización, puedes archivar elementos de trabajo que estén completados o cancelados. Encuéntralos aquí una vez archivados.",
+ "primary_button": {
+ "text": "Establecer automatización"
+ }
+ },
+ "issues_empty_filter": {
+ "title": "No se encontraron elementos de trabajo que coincidan con los filtros aplicados",
+ "secondary_button": {
+ "text": "Limpiar todos los filtros"
+ }
+ }
+ }
+ },
+
+ "project_module": {
+ "add_module": "Agregar Módulo",
+ "update_module": "Actualizar Módulo",
+ "create_module": "Crear Módulo",
+ "archive_module": "Archivar Módulo",
+ "restore_module": "Restaurar Módulo",
+ "delete_module": "Eliminar módulo",
+ "empty_state": {
+ "general": {
+ "title": "Mapea los hitos de tu proyecto a Módulos y rastrea el trabajo agregado fácilmente.",
+ "description": "Un grupo de elementos de trabajo que pertenecen a un padre lógico y jerárquico forman un módulo. Piensa en ellos como una forma de rastrear el trabajo por hitos del proyecto. Tienen sus propios períodos y fechas límite, así como análisis para ayudarte a ver qué tan cerca o lejos estás de un hito.",
+ "primary_button": {
+ "text": "Construye tu primer módulo",
+ "comic": {
+ "title": "Los módulos ayudan a agrupar el trabajo por jerarquía.",
+ "description": "Un módulo de carrito, un módulo de chasis y un módulo de almacén son buenos ejemplos de esta agrupación."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "No hay elementos de trabajo en el módulo",
+ "description": "Crea o agrega elementos de trabajo que quieras lograr como parte de este módulo",
+ "primary_button": {
+ "text": "Crear nuevos elementos de trabajo"
+ },
+ "secondary_button": {
+ "text": "Agregar un elemento de trabajo existente"
+ }
+ },
+ "archived": {
+ "title": "Aún no hay Módulos archivados",
+ "description": "Para mantener ordenado tu proyecto, archiva los módulos completados o cancelados. Encuéntralos aquí una vez archivados."
+ },
+ "sidebar": {
+ "in_active": "Este módulo aún no está activo.",
+ "invalid_date": "Fecha inválida. Por favor ingresa una fecha válida."
+ }
+ },
+ "quick_actions": {
+ "archive_module": "Archivar módulo",
+ "archive_module_description": "Solo los módulos completados o\ncancelados pueden ser archivados.",
+ "delete_module": "Eliminar módulo"
+ },
+ "toast": {
+ "copy": {
+ "success": "Enlace del módulo copiado al portapapeles"
+ },
+ "delete": {
+ "success": "Módulo eliminado exitosamente",
+ "error": "Error al eliminar el módulo"
+ }
+ }
+ },
+
+ "project_views": {
+ "empty_state": {
+ "general": {
+ "title": "Guarda vistas filtradas para tu proyecto. Crea tantas como necesites",
+ "description": "Las vistas son un conjunto de filtros guardados que usas frecuentemente o a los que quieres tener fácil acceso. Todos tus colegas en un proyecto pueden ver las vistas de todos y elegir la que mejor se adapte a sus necesidades.",
+ "primary_button": {
+ "text": "Crea tu primera vista",
+ "comic": {
+ "title": "Las vistas funcionan sobre las propiedades de los Elementos de trabajo.",
+ "description": "Puedes crear una vista desde aquí con tantas propiedades como filtros como consideres apropiado."
+ }
+ }
+ },
+ "filter": {
+ "title": "No hay vistas coincidentes",
+ "description": "Ninguna vista coincide con los criterios de búsqueda. \n Crea una nueva vista en su lugar."
+ }
+ }
+ },
+
+ "project_page": {
+ "empty_state": {
+ "general": {
+ "title": "Escribe una nota, un documento o una base de conocimiento completa. Obtén ayuda de Galileo, el asistente de IA de Plane, para comenzar",
+ "description": "Las páginas son espacios para pensamientos en Plane. Toma notas de reuniones, fórmalas fácilmente, integra elementos de trabajo, organízalas usando una biblioteca de componentes y mantenlas todas en el contexto de tu proyecto. Para hacer cualquier documento rápidamente, invoca a Galileo, la IA de Plane, con un atajo o haciendo clic en un botón.",
+ "primary_button": {
+ "text": "Crea tu primera página"
+ }
+ },
+ "private": {
+ "title": "Aún no hay páginas privadas",
+ "description": "Mantén tus pensamientos privados aquí. Cuando estés listo para compartir, el equipo está a solo un clic de distancia.",
+ "primary_button": {
+ "text": "Crea tu primera página"
+ }
+ },
+ "public": {
+ "title": "Aún no hay páginas públicas",
+ "description": "Ve las páginas compartidas con todos en tu proyecto aquí mismo.",
+ "primary_button": {
+ "text": "Crea tu primera página"
+ }
+ },
+ "archived": {
+ "title": "Aún no hay páginas archivadas",
+ "description": "Archiva las páginas que no estén en tu radar. Accede a ellas aquí cuando las necesites."
+ }
+ }
+ },
+
+ "command_k": {
+ "empty_state": {
+ "search": {
+ "title": "No se encontraron resultados"
+ }
+ }
+ },
+
+ "issue_relation": {
+ "empty_state": {
+ "search": {
+ "title": "No se encontraron elementos de trabajo coincidentes"
+ },
+ "no_issues": {
+ "title": "No se encontraron elementos de trabajo"
+ }
+ }
+ },
+
+ "issue_comment": {
+ "empty_state": {
+ "general": {
+ "title": "Aún no hay comentarios",
+ "description": "Los comentarios pueden usarse como un espacio de discusión y seguimiento para los elementos de trabajo"
+ }
+ }
+ },
+
+ "notification": {
+ "label": "Bandeja de entrada",
+ "page_label": "{workspace} - Bandeja de entrada",
+ "options": {
+ "mark_all_as_read": "Marcar todo como leído",
+ "mark_read": "Marcar como leído",
+ "mark_unread": "Marcar como no leído",
+ "refresh": "Actualizar",
+ "filters": "Filtros de bandeja de entrada",
+ "show_unread": "Mostrar no leídos",
+ "show_snoozed": "Mostrar pospuestos",
+ "show_archived": "Mostrar archivados",
+ "mark_archive": "Archivar",
+ "mark_unarchive": "Desarchivar",
+ "mark_snooze": "Posponer",
+ "mark_unsnooze": "Quitar posposición"
+ },
+ "toasts": {
+ "read": "Notificación marcada como leída",
+ "unread": "Notificación marcada como no leída",
+ "archived": "Notificación marcada como archivada",
+ "unarchived": "Notificación marcada como no archivada",
+ "snoozed": "Notificación pospuesta",
+ "unsnoozed": "Notificación posposición cancelada"
+ },
+ "empty_state": {
+ "detail": {
+ "title": "Selecciona para ver detalles."
+ },
+ "all": {
+ "title": "No hay elementos de trabajo asignados",
+ "description": "Las actualizaciones de elementos de trabajo asignados a ti se pueden \n ver aquí"
+ },
+ "mentions": {
+ "title": "No hay elementos de trabajo asignados",
+ "description": "Las actualizaciones de elementos de trabajo asignados a ti se pueden \n ver aquí"
+ }
+ },
+ "tabs": {
+ "all": "Todo",
+ "mentions": "Menciones"
+ },
+ "filter": {
+ "assigned": "Asignado a mí",
+ "created": "Creado por mí",
+ "subscribed": "Suscrito por mí"
+ },
+ "snooze": {
+ "1_day": "1 día",
+ "3_days": "3 días",
+ "5_days": "5 días",
+ "1_week": "1 semana",
+ "2_weeks": "2 semanas",
+ "custom": "Personalizado"
+ }
+ },
+
+ "active_cycle": {
+ "empty_state": {
+ "progress": {
+ "title": "Agrega elementos de trabajo al ciclo para ver su progreso"
+ },
+ "chart": {
+ "title": "Agrega elementos de trabajo al ciclo para ver el gráfico de avance."
+ },
+ "priority_issue": {
+ "title": "Observa los elementos de trabajo de alta prioridad abordados en el ciclo de un vistazo."
+ },
+ "assignee": {
+ "title": "Agrega asignados a los elementos de trabajo para ver un desglose del trabajo por asignados."
+ },
+ "label": {
+ "title": "Agrega etiquetas a los elementos de trabajo para ver el desglose del trabajo por etiquetas."
+ }
+ }
+ },
+
+ "disabled_project": {
+ "empty_state": {
+ "inbox": {
+ "title": "Intake no está habilitado para el proyecto.",
+ "description": "Intake te ayuda a gestionar las solicitudes entrantes a tu proyecto y agregarlas como elementos de trabajo en tu flujo de trabajo. Habilita Intake desde la configuración del proyecto para gestionar las solicitudes.",
+ "primary_button": {
+ "text": "Gestionar funciones"
+ }
+ },
+ "cycle": {
+ "title": "Los Ciclos no están habilitados para este proyecto.",
+ "description": "Divide el trabajo en fragmentos limitados por tiempo, trabaja hacia atrás desde la fecha límite de tu proyecto para establecer fechas y haz un progreso tangible como equipo. Habilita la función de ciclos para tu proyecto para comenzar a usarlos.",
+ "primary_button": {
+ "text": "Gestionar funciones"
+ }
+ },
+ "module": {
+ "title": "Los Módulos no están habilitados para el proyecto.",
+ "description": "Los Módulos son los componentes básicos de tu proyecto. Habilita los módulos desde la configuración del proyecto para comenzar a usarlos.",
+ "primary_button": {
+ "text": "Gestionar funciones"
+ }
+ },
+ "page": {
+ "title": "Las Páginas no están habilitadas para el proyecto.",
+ "description": "Las Páginas son los componentes básicos de tu proyecto. Habilita las páginas desde la configuración del proyecto para comenzar a usarlas.",
+ "primary_button": {
+ "text": "Gestionar funciones"
+ }
+ },
+ "view": {
+ "title": "Las Vistas no están habilitadas para el proyecto.",
+ "description": "Las Vistas son los componentes básicos de tu proyecto. Habilita las vistas desde la configuración del proyecto para comenzar a usarlas.",
+ "primary_button": {
+ "text": "Gestionar funciones"
+ }
+ }
+ }
+ },
+ "workspace_draft_issues": {
+ "draft_an_issue": "Borrador de elemento de trabajo",
+ "empty_state": {
+ "title": "Los elementos de trabajo a medio escribir y pronto los comentarios aparecerán aquí.",
+ "description": "Para probar esto, comienza a agregar un elemento de trabajo y déjalo a medias o crea tu primer borrador a continuación. 😉",
+ "primary_button": {
+ "text": "Crea tu primer borrador"
+ }
+ },
+ "delete_modal": {
+ "title": "Eliminar borrador",
+ "description": "¿Estás seguro de que quieres eliminar este borrador? Esto no se puede deshacer."
+ },
+ "toasts": {
+ "created": {
+ "success": "Borrador creado",
+ "error": "No se pudo crear el elemento de trabajo. Por favor, inténtalo de nuevo."
+ },
+ "deleted": {
+ "success": "Borrador eliminado"
+ }
+ }
+ },
+
+ "stickies": {
+ "title": "Tus notas adhesivas",
+ "placeholder": "haz clic para escribir aquí",
+ "all": "Todas las notas adhesivas",
+ "no-data": "Anota una idea, captura un momento eureka o registra una inspiración. Agrega una nota adhesiva para comenzar.",
+ "add": "Agregar nota adhesiva",
+ "search_placeholder": "Buscar por título",
+ "delete": "Eliminar nota adhesiva",
+ "delete_confirmation": "¿Estás seguro de que quieres eliminar esta nota adhesiva?",
+ "empty_state": {
+ "simple": "Anota una idea, captura un momento eureka o registra una inspiración. Agrega una nota adhesiva para comenzar.",
+ "general": {
+ "title": "Las notas adhesivas son notas rápidas y tareas pendientes que anotas al vuelo.",
+ "description": "Captura tus pensamientos e ideas sin esfuerzo creando notas adhesivas a las que puedes acceder en cualquier momento y desde cualquier lugar.",
+ "primary_button": {
+ "text": "Agregar nota adhesiva"
+ }
+ },
+ "search": {
+ "title": "Eso no coincide con ninguna de tus notas adhesivas.",
+ "description": "Prueba un término diferente o háznoslo saber\nsi estás seguro de que tu búsqueda es correcta.",
+ "primary_button": {
+ "text": "Agregar nota adhesiva"
+ }
+ }
+ },
+ "toasts": {
+ "errors": {
+ "wrong_name": "El nombre de la nota adhesiva no puede tener más de 100 caracteres.",
+ "already_exists": "Ya existe una nota adhesiva sin descripción"
+ },
+ "created": {
+ "title": "Nota adhesiva creada",
+ "message": "La nota adhesiva se ha creado exitosamente"
+ },
+ "not_created": {
+ "title": "Nota adhesiva no creada",
+ "message": "No se pudo crear la nota adhesiva"
+ },
+ "updated": {
+ "title": "Nota adhesiva actualizada",
+ "message": "La nota adhesiva se ha actualizado exitosamente"
+ },
+ "not_updated": {
+ "title": "Nota adhesiva no actualizada",
+ "message": "No se pudo actualizar la nota adhesiva"
+ },
+ "removed": {
+ "title": "Nota adhesiva eliminada",
+ "message": "La nota adhesiva se ha eliminado exitosamente"
+ },
+ "not_removed": {
+ "title": "Nota adhesiva no eliminada",
+ "message": "No se pudo eliminar la nota adhesiva"
+ }
+ }
+ },
+
+ "role_details": {
+ "guest": {
+ "title": "Invitado",
+ "description": "Los miembros externos de las organizaciones pueden ser invitados como invitados."
+ },
+ "member": {
+ "title": "Miembro",
+ "description": "Capacidad para leer, escribir, editar y eliminar entidades dentro de proyectos, ciclos y módulos"
+ },
+ "admin": {
+ "title": "Administrador",
+ "description": "Todos los permisos establecidos como verdaderos dentro del espacio de trabajo."
+ }
+ },
+
+ "user_roles": {
+ "product_or_project_manager": "Gerente de Producto / Proyecto",
+ "development_or_engineering": "Desarrollo / Ingeniería",
+ "founder_or_executive": "Fundador / Ejecutivo",
+ "freelancer_or_consultant": "Freelancer / Consultor",
+ "marketing_or_growth": "Marketing / Crecimiento",
+ "sales_or_business_development": "Ventas / Desarrollo de Negocios",
+ "support_or_operations": "Soporte / Operaciones",
+ "student_or_professor": "Estudiante / Profesor",
+ "human_resources": "Recursos Humanos",
+ "other": "Otro"
+ },
+
+ "importer": {
+ "github": {
+ "title": "GitHub",
+ "description": "Importa elementos de trabajo desde repositorios de GitHub y sincronízalos."
+ },
+ "jira": {
+ "title": "Jira",
+ "description": "Importa elementos de trabajo y epics desde proyectos y epics de Jira."
+ }
+ },
+
+ "exporter": {
+ "csv": {
+ "title": "CSV",
+ "description": "Exporta elementos de trabajo a un archivo CSV.",
+ "short_description": "Exportar como csv"
+ },
+ "excel": {
+ "title": "Excel",
+ "description": "Exporta elementos de trabajo a un archivo Excel.",
+ "short_description": "Exportar como excel"
+ },
+ "xlsx": {
+ "title": "Excel",
+ "description": "Exporta elementos de trabajo a un archivo Excel.",
+ "short_description": "Exportar como excel"
+ },
+ "json": {
+ "title": "JSON",
+ "description": "Exporta elementos de trabajo a un archivo JSON.",
+ "short_description": "Exportar como json"
+ }
+ },
+ "default_global_view": {
+ "all_issues": "Todos los elementos de trabajo",
+ "assigned": "Asignados",
+ "created": "Creados",
+ "subscribed": "Suscritos"
+ },
+
+ "themes": {
+ "theme_options": {
+ "system_preference": {
+ "label": "Preferencia del sistema"
+ },
+ "light": {
+ "label": "Claro"
+ },
+ "dark": {
+ "label": "Oscuro"
+ },
+ "light_contrast": {
+ "label": "Claro de alto contraste"
+ },
+ "dark_contrast": {
+ "label": "Oscuro de alto contraste"
+ },
+ "custom": {
+ "label": "Tema personalizado"
+ }
+ }
+ },
+ "project_modules": {
+ "status": {
+ "backlog": "Pendientes",
+ "planned": "Planificado",
+ "in_progress": "En progreso",
+ "paused": "Pausado",
+ "completed": "Completado",
+ "cancelled": "Cancelado"
+ },
+ "layout": {
+ "list": "Vista de lista",
+ "board": "Vista de galería",
+ "timeline": "Vista de línea de tiempo"
+ },
+ "order_by": {
+ "name": "Nombre",
+ "progress": "Progreso",
+ "issues": "Número de elementos de trabajo",
+ "due_date": "Fecha de vencimiento",
+ "created_at": "Fecha de creación",
+ "manual": "Manual"
+ }
+ },
+
+ "cycle": {
+ "label": "{count, plural, one {Ciclo} other {Ciclos}}",
+ "no_cycle": "Sin ciclo"
+ },
+
+ "module": {
+ "label": "{count, plural, one {Módulo} other {Módulos}}",
+ "no_module": "Sin módulo"
+ }
+}
diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json
new file mode 100644
index 000000000..f258d5381
--- /dev/null
+++ b/packages/i18n/src/locales/fr/translations.json
@@ -0,0 +1,2362 @@
+{
+ "sidebar": {
+ "projects": "Projets",
+ "pages": "Pages",
+ "new_work_item": "Nouvel élément de travail",
+ "home": "Accueil",
+ "your_work": "Votre travail",
+ "inbox": "Boîte de réception",
+ "workspace": "Espace de travail",
+ "views": "Vues",
+ "analytics": "Analyses",
+ "work_items": "Éléments de travail",
+ "cycles": "Cycles",
+ "modules": "Modules",
+ "intake": "Intake",
+ "drafts": "Brouillons",
+ "favorites": "Favoris",
+ "pro": "Pro",
+ "upgrade": "Mettre à niveau"
+ },
+
+ "auth": {
+ "common": {
+ "email": {
+ "label": "E-mail",
+ "placeholder": "nom@entreprise.com",
+ "errors": {
+ "required": "L'e-mail est requis",
+ "invalid": "L'e-mail est invalide"
+ }
+ },
+ "password": {
+ "label": "Mot de passe",
+ "set_password": "Définir un mot de passe",
+ "placeholder": "Entrer le mot de passe",
+ "confirm_password": {
+ "label": "Confirmer le mot de passe",
+ "placeholder": "Confirmer le mot de passe"
+ },
+ "current_password": {
+ "label": "Mot de passe actuel"
+ },
+ "new_password": {
+ "label": "Nouveau mot de passe",
+ "placeholder": "Entrer le nouveau mot de passe"
+ },
+ "change_password": {
+ "label": {
+ "default": "Changer le mot de passe",
+ "submitting": "Changement du mot de passe"
+ }
+ },
+ "errors": {
+ "match": "Les mots de passe ne correspondent pas",
+ "empty": "Veuillez entrer votre mot de passe",
+ "length": "Le mot de passe doit contenir plus de 8 caractères",
+ "strength": {
+ "weak": "Le mot de passe est faible",
+ "strong": "Le mot de passe est fort"
+ }
+ },
+ "submit": "Définir le mot de passe",
+ "toast": {
+ "change_password": {
+ "success": {
+ "title": "Succès !",
+ "message": "Mot de passe changé avec succès."
+ },
+ "error": {
+ "title": "Erreur !",
+ "message": "Une erreur s'est produite. Veuillez réessayer."
+ }
+ }
+ }
+ },
+ "unique_code": {
+ "label": "Code unique",
+ "placeholder": "obtient-définit-vole",
+ "paste_code": "Collez le code envoyé à votre e-mail",
+ "requesting_new_code": "Demande d'un nouveau code",
+ "sending_code": "Envoi du code"
+ },
+ "already_have_an_account": "Vous avez déjà un compte ?",
+ "login": "Se connecter",
+ "create_account": "Créer un compte",
+ "new_to_plane": "Nouveau sur Plane ?",
+ "back_to_sign_in": "Retour à la connexion",
+ "resend_in": "Renvoyer dans {seconds} secondes",
+ "sign_in_with_unique_code": "Se connecter avec un code unique",
+ "forgot_password": "Mot de passe oublié ?"
+ },
+ "sign_up": {
+ "header": {
+ "label": "Créez un compte pour commencer à gérer le travail avec votre équipe.",
+ "step": {
+ "email": {
+ "header": "S'inscrire",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "S'inscrire",
+ "sub_header": "Inscrivez-vous en utilisant une combinaison e-mail-mot de passe."
+ },
+ "unique_code": {
+ "header": "S'inscrire",
+ "sub_header": "Inscrivez-vous en utilisant un code unique envoyé à l'adresse e-mail ci-dessus."
+ }
+ }
+ },
+ "errors": {
+ "password": {
+ "strength": "Essayez de définir un mot de passe fort pour continuer"
+ }
+ }
+ },
+ "sign_in": {
+ "header": {
+ "label": "Connectez-vous pour commencer à gérer le travail avec votre équipe.",
+ "step": {
+ "email": {
+ "header": "Se connecter ou s'inscrire",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "Se connecter ou s'inscrire",
+ "sub_header": "Utilisez votre combinaison e-mail-mot de passe pour vous connecter."
+ },
+ "unique_code": {
+ "header": "Se connecter ou s'inscrire",
+ "sub_header": "Connectez-vous en utilisant un code unique envoyé à l'adresse e-mail ci-dessus."
+ }
+ }
+ }
+ },
+ "forgot_password": {
+ "title": "Réinitialiser votre mot de passe",
+ "description": "Entrez l'adresse e-mail vérifiée de votre compte utilisateur et nous vous enverrons un lien de réinitialisation du mot de passe.",
+ "email_sent": "Nous avons envoyé le lien de réinitialisation à votre adresse e-mail",
+ "send_reset_link": "Envoyer le lien de réinitialisation",
+ "errors": {
+ "smtp_not_enabled": "Nous constatons que votre administrateur n'a pas activé SMTP, nous ne pourrons pas envoyer de lien de réinitialisation du mot de passe"
+ },
+ "toast": {
+ "success": {
+ "title": "E-mail envoyé",
+ "message": "Vérifiez votre boîte de réception pour un lien de réinitialisation de votre mot de passe. S'il n'apparaît pas dans quelques minutes, vérifiez votre dossier spam."
+ },
+ "error": {
+ "title": "Erreur !",
+ "message": "Une erreur s'est produite. Veuillez réessayer."
+ }
+ }
+ },
+ "reset_password": {
+ "title": "Définir un nouveau mot de passe",
+ "description": "Sécurisez votre compte avec un mot de passe fort"
+ },
+ "set_password": {
+ "title": "Sécurisez votre compte",
+ "description": "La définition d'un mot de passe vous permet de vous connecter en toute sécurité"
+ },
+ "sign_out": {
+ "toast": {
+ "error": {
+ "title": "Erreur !",
+ "message": "Échec de la déconnexion. Veuillez réessayer."
+ }
+ }
+ }
+ },
+
+ "submit": "Soumettre",
+ "cancel": "Annuler",
+ "loading": "Chargement",
+ "error": "Erreur",
+ "success": "Succès",
+ "warning": "Avertissement",
+ "info": "Info",
+ "close": "Fermer",
+ "yes": "Oui",
+ "no": "Non",
+ "ok": "OK",
+ "name": "Nom",
+ "description": "Description",
+ "search": "Rechercher",
+ "add_member": "Ajouter un membre",
+ "adding_members": "Ajout de membres",
+ "remove_member": "Supprimer le membre",
+ "add_members": "Ajouter des membres",
+ "adding_member": "Ajout de membres",
+ "remove_members": "Supprimer des membres",
+ "add": "Ajouter",
+ "adding": "Ajout",
+ "remove": "Supprimer",
+ "add_new": "Ajouter nouveau",
+ "remove_selected": "Supprimer la sélection",
+ "first_name": "Prénom",
+ "last_name": "Nom",
+ "email": "E-mail",
+ "display_name": "Nom d'affichage",
+ "role": "Rôle",
+ "timezone": "Fuseau horaire",
+ "avatar": "Avatar",
+ "cover_image": "Image de couverture",
+ "password": "Mot de passe",
+ "change_cover": "Changer la couverture",
+ "language": "Langue",
+ "saving": "Enregistrement",
+ "save_changes": "Enregistrer les modifications",
+ "deactivate_account": "Désactiver le compte",
+ "deactivate_account_description": "Lors de la désactivation d'un compte, toutes les données et ressources de ce compte seront définitivement supprimées et ne pourront pas être récupérées.",
+ "profile_settings": "Paramètres du profil",
+ "your_account": "Votre compte",
+ "security": "Sécurité",
+ "activity": "Activité",
+ "appearance": "Apparence",
+ "notifications": "Notifications",
+ "workspaces": "Espaces de travail",
+ "create_workspace": "Créer un espace de travail",
+ "invitations": "Invitations",
+ "summary": "Résumé",
+ "assigned": "Assigné",
+ "created": "Créé",
+ "subscribed": "Abonné",
+ "you_do_not_have_the_permission_to_access_this_page": "Vous n'avez pas la permission d'accéder à cette page.",
+ "something_went_wrong_please_try_again": "Une erreur s'est produite. Veuillez réessayer.",
+ "load_more": "Charger plus",
+ "select_or_customize_your_interface_color_scheme": "Sélectionnez ou personnalisez votre schéma de couleurs d'interface.",
+ "theme": "Thème",
+ "system_preference": "Préférence système",
+ "light": "Clair",
+ "dark": "Sombre",
+ "light_contrast": "Contraste élevé clair",
+ "dark_contrast": "Contraste élevé sombre",
+ "custom": "Thème personnalisé",
+ "select_your_theme": "Sélectionnez votre thème",
+ "customize_your_theme": "Personnalisez votre thème",
+ "background_color": "Couleur de fond",
+ "text_color": "Couleur du texte",
+ "primary_color": "Couleur principale (Thème)",
+ "sidebar_background_color": "Couleur de fond de la barre latérale",
+ "sidebar_text_color": "Couleur du texte de la barre latérale",
+ "set_theme": "Définir le thème",
+ "enter_a_valid_hex_code_of_6_characters": "Entrez un code hexadécimal valide de 6 caractères",
+ "background_color_is_required": "La couleur de fond est requise",
+ "text_color_is_required": "La couleur du texte est requise",
+ "primary_color_is_required": "La couleur principale est requise",
+ "sidebar_background_color_is_required": "La couleur de fond de la barre latérale est requise",
+ "sidebar_text_color_is_required": "La couleur du texte de la barre latérale est requise",
+ "updating_theme": "Mise à jour du thème",
+ "theme_updated_successfully": "Thème mis à jour avec succès",
+ "failed_to_update_the_theme": "Échec de la mise à jour du thème",
+ "email_notifications": "Notifications par e-mail",
+ "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "Restez informé des éléments de travail auxquels vous êtes abonné. Activez ceci pour être notifié.",
+ "email_notification_setting_updated_successfully": "Paramètre de notification par e-mail mis à jour avec succès",
+ "failed_to_update_email_notification_setting": "Échec de la mise à jour du paramètre de notification par e-mail",
+ "notify_me_when": "Me notifier quand",
+ "property_changes": "Modifications des propriétés",
+ "property_changes_description": "Me notifier lorsque les propriétés des éléments de travail comme les assignés, la priorité, les estimations ou autre changent.",
+ "state_change": "Changement d'état",
+ "state_change_description": "Me notifier lorsque les éléments de travail passent à un état différent",
+ "issue_completed": "Élément de travail terminé",
+ "issue_completed_description": "Me notifier uniquement lorsqu'un élément de travail est terminé",
+ "comments": "Commentaires",
+ "comments_description": "Me notifier lorsque quelqu'un laisse un commentaire sur l'élément de travail",
+ "mentions": "Mentions",
+ "mentions_description": "Me notifier uniquement lorsque quelqu'un me mentionne dans les commentaires ou la description",
+ "old_password": "Ancien mot de passe",
+ "general_settings": "Paramètres généraux",
+ "sign_out": "Se déconnecter",
+ "signing_out": "Déconnexion",
+ "active_cycles": "Cycles actifs",
+ "active_cycles_description": "Surveillez les cycles à travers les projets, suivez les éléments de travail prioritaires et zoomez sur les cycles qui nécessitent de l'attention.",
+ "on_demand_snapshots_of_all_your_cycles": "Instantanés à la demande de tous vos cycles",
+ "upgrade": "Mettre à niveau",
+ "10000_feet_view": "Vue à 10 000 pieds de tous les cycles actifs.",
+ "10000_feet_view_description": "Dézoomez pour voir les cycles en cours dans tous vos projets en même temps au lieu de passer d'un cycle à l'autre dans chaque projet.",
+ "get_snapshot_of_each_active_cycle": "Obtenez un aperçu de chaque cycle actif.",
+ "get_snapshot_of_each_active_cycle_description": "Suivez les métriques de haut niveau pour tous les cycles actifs, voyez leur état d'avancement et obtenez une idée de la portée par rapport aux échéances.",
+ "compare_burndowns": "Comparez les burndowns.",
+ "compare_burndowns_description": "Surveillez les performances de chacune de vos équipes en jetant un coup d'œil au rapport burndown de chaque cycle.",
+ "quickly_see_make_or_break_issues": "Voyez rapidement les éléments de travail critiques.",
+ "quickly_see_make_or_break_issues_description": "Prévisualisez les éléments de travail hautement prioritaires pour chaque cycle par rapport aux dates d'échéance. Voyez-les tous par cycle en un clic.",
+ "zoom_into_cycles_that_need_attention": "Zoomez sur les cycles qui nécessitent de l'attention.",
+ "zoom_into_cycles_that_need_attention_description": "Examinez l'état de tout cycle qui ne correspond pas aux attentes en un clic.",
+ "stay_ahead_of_blockers": "Anticipez les blocages.",
+ "stay_ahead_of_blockers_description": "Repérez les défis d'un projet à l'autre et voyez les dépendances inter-cycles qui ne sont pas évidentes depuis une autre vue.",
+ "analytics": "Analyses",
+ "workspace_invites": "Invitations à l'espace de travail",
+ "enter_god_mode": "Entrer en mode dieu",
+ "workspace_logo": "Logo de l'espace de travail",
+ "new_issue": "Nouvel élément de travail",
+ "your_work": "Votre travail",
+ "drafts": "Brouillons",
+ "projects": "Projets",
+ "views": "Vues",
+ "workspace": "Espace de travail",
+ "archives": "Archives",
+ "settings": "Paramètres",
+ "failed_to_move_favorite": "Échec du déplacement du favori",
+ "favorites": "Favoris",
+ "no_favorites_yet": "Pas encore de favoris",
+ "create_folder": "Créer un dossier",
+ "new_folder": "Nouveau dossier",
+ "favorite_updated_successfully": "Favori mis à jour avec succès",
+ "favorite_created_successfully": "Favori créé avec succès",
+ "folder_already_exists": "Le dossier existe déjà",
+ "folder_name_cannot_be_empty": "Le nom du dossier ne peut pas être vide",
+ "something_went_wrong": "Une erreur s'est produite",
+ "failed_to_reorder_favorite": "Échec de la réorganisation du favori",
+ "favorite_removed_successfully": "Favori supprimé avec succès",
+ "failed_to_create_favorite": "Échec de la création du favori",
+ "failed_to_rename_favorite": "Échec du renommage du favori",
+ "project_link_copied_to_clipboard": "Lien du projet copié dans le presse-papiers",
+ "link_copied": "Lien copié",
+ "add_project": "Ajouter un projet",
+ "create_project": "Créer un projet",
+ "failed_to_remove_project_from_favorites": "Impossible de supprimer le projet des favoris. Veuillez réessayer.",
+ "project_created_successfully": "Projet créé avec succès",
+ "project_created_successfully_description": "Projet créé avec succès. Vous pouvez maintenant commencer à ajouter des éléments de travail.",
+ "project_cover_image_alt": "Image de couverture du projet",
+ "name_is_required": "Le nom est requis",
+ "title_should_be_less_than_255_characters": "Le titre doit faire moins de 255 caractères",
+ "project_name": "Nom du projet",
+ "project_id_must_be_at_least_1_character": "L'ID du projet doit comporter au moins 1 caractère",
+ "project_id_must_be_at_most_5_characters": "L'ID du projet doit comporter au plus 5 caractères",
+ "project_id": "ID du projet",
+ "project_id_tooltip_content": "Vous aide à identifier uniquement les éléments de travail dans le projet. Maximum 5 caractères.",
+ "description_placeholder": "Description",
+ "only_alphanumeric_non_latin_characters_allowed": "Seuls les caractères alphanumériques et non latins sont autorisés.",
+ "project_id_is_required": "L'ID du projet est requis",
+ "project_id_allowed_char": "Seuls les caractères alphanumériques et non latins sont autorisés.",
+ "project_id_min_char": "L'ID du projet doit comporter au moins 1 caractère",
+ "project_id_max_char": "L'ID du projet doit comporter au plus 5 caractères",
+ "project_description_placeholder": "Entrez la description du projet",
+ "select_network": "Sélectionner le réseau",
+ "lead": "Responsable",
+ "date_range": "Plage de dates",
+ "private": "Privé",
+ "public": "Public",
+ "accessible_only_by_invite": "Accessible uniquement sur invitation",
+ "anyone_in_the_workspace_except_guests_can_join": "Tout le monde dans l'espace de travail sauf les invités peut rejoindre",
+ "creating": "Création",
+ "creating_project": "Création du projet",
+ "adding_project_to_favorites": "Ajout du projet aux favoris",
+ "project_added_to_favorites": "Projet ajouté aux favoris",
+ "couldnt_add_the_project_to_favorites": "Impossible d'ajouter le projet aux favoris. Veuillez réessayer.",
+ "removing_project_from_favorites": "Suppression du projet des favoris",
+ "project_removed_from_favorites": "Projet supprimé des favoris",
+ "couldnt_remove_the_project_from_favorites": "Impossible de supprimer le projet des favoris. Veuillez réessayer.",
+ "add_to_favorites": "Ajouter aux favoris",
+ "remove_from_favorites": "Supprimer des favoris",
+ "publish_settings": "Paramètres de publication",
+ "publish": "Publier",
+ "copy_link": "Copier le lien",
+ "leave_project": "Quitter le projet",
+ "join_the_project_to_rearrange": "Rejoignez le projet pour réorganiser",
+ "drag_to_rearrange": "Glisser pour réorganiser",
+ "congrats": "Félicitations !",
+ "open_project": "Ouvrir le projet",
+ "issues": "Éléments de travail",
+ "cycles": "Cycles",
+ "modules": "Modules",
+ "pages": "Pages",
+ "intake": "Intake",
+ "time_tracking": "Suivi du temps",
+ "work_management": "Gestion du travail",
+ "projects_and_issues": "Projets et éléments de travail",
+ "projects_and_issues_description": "Activez ou désactivez ces éléments pour ce projet.",
+ "cycles_description": "Planifiez le travail comme vous le souhaitez par projet et changez la fréquence d'une période à l'autre.",
+ "modules_description": "Regroupez le travail en configurations de type sous-projet avec leurs propres responsables et assignés.",
+ "views_description": "Enregistrez les tris, filtres et options d'affichage pour plus tard ou partagez-les.",
+ "pages_description": "Écrivez n'importe quoi comme vous écrivez n'importe quoi.",
+ "intake_description": "Restez informé des éléments de travail auxquels vous êtes abonné. Activez ceci pour être notifié.",
+ "time_tracking_description": "Suivez le temps passé sur les éléments de travail et les projets.",
+ "work_management_description": "Gérez votre travail et vos projets facilement.",
+ "documentation": "Documentation",
+ "message_support": "Contacter le support",
+ "contact_sales": "Contacter les ventes",
+ "hyper_mode": "Mode Hyper",
+ "keyboard_shortcuts": "Raccourcis clavier",
+ "whats_new": "Quoi de neuf ?",
+ "version": "Version",
+ "we_are_having_trouble_fetching_the_updates": "Nous avons des difficultés à récupérer les mises à jour.",
+ "our_changelogs": "nos journaux des modifications",
+ "for_the_latest_updates": "pour les dernières mises à jour.",
+ "please_visit": "Veuillez visiter",
+ "docs": "Documentation",
+ "full_changelog": "Journal des modifications complet",
+ "support": "Support",
+ "discord": "Discord",
+ "powered_by_plane_pages": "Propulsé par Plane Pages",
+ "please_select_at_least_one_invitation": "Veuillez sélectionner au moins une invitation.",
+ "please_select_at_least_one_invitation_description": "Veuillez sélectionner au moins une invitation pour rejoindre l'espace de travail.",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace": "Nous voyons que quelqu'un vous a invité à rejoindre un espace de travail",
+ "join_a_workspace": "Rejoindre un espace de travail",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "Nous voyons que quelqu'un vous a invité à rejoindre un espace de travail",
+ "join_a_workspace_description": "Rejoindre un espace de travail",
+ "accept_and_join": "Accepter et rejoindre",
+ "go_home": "Aller à l'accueil",
+ "no_pending_invites": "Aucune invitation en attente",
+ "you_can_see_here_if_someone_invites_you_to_a_workspace": "Vous pouvez voir ici si quelqu'un vous invite à un espace de travail",
+ "back_to_home": "Retour à l'accueil",
+ "workspace_name": "nom-espace-de-travail",
+ "deactivate_your_account": "Désactiver votre compte",
+ "deactivate_your_account_description": "Une fois désactivé, vous ne pourrez plus être assigné à des éléments de travail ni être facturé pour votre espace de travail. Pour réactiver votre compte, vous aurez besoin d'une invitation à un espace de travail avec cette adresse e-mail.",
+ "deactivating": "Désactivation",
+ "confirm": "Confirmer",
+ "confirming": "Confirmation",
+ "draft_created": "Brouillon créé",
+ "issue_created_successfully": "Élément de travail créé avec succès",
+ "draft_creation_failed": "Échec de la création du brouillon",
+ "issue_creation_failed": "Échec de la création de l'élément de travail",
+ "draft_issue": "Élément de travail en brouillon",
+ "issue_updated_successfully": "Élément de travail mis à jour avec succès",
+ "issue_could_not_be_updated": "L'élément de travail n'a pas pu être mis à jour",
+ "create_a_draft": "Créer un brouillon",
+ "save_to_drafts": "Enregistrer dans les brouillons",
+ "save": "Enregistrer",
+ "update": "Mettre à jour",
+ "updating": "Mise à jour",
+ "create_new_issue": "Créer un nouvel élément de travail",
+ "editor_is_not_ready_to_discard_changes": "L'éditeur n'est pas prêt à annuler les modifications",
+ "failed_to_move_issue_to_project": "Échec du déplacement de l'élément de travail vers le projet",
+ "create_more": "Créer plus",
+ "add_to_project": "Ajouter au projet",
+ "discard": "Annuler",
+ "duplicate_issue_found": "Élément de travail en double trouvé",
+ "duplicate_issues_found": "Éléments de travail en double trouvés",
+ "no_matching_results": "Aucun résultat correspondant",
+ "title_is_required": "Le titre est requis",
+ "title": "Titre",
+ "state": "État",
+ "priority": "Priorité",
+ "none": "Aucun",
+ "urgent": "Urgent",
+ "high": "Élevé",
+ "medium": "Moyen",
+ "low": "Faible",
+ "members": "Membres",
+ "assignee": "Assigné",
+ "assignees": "Assignés",
+ "you": "Vous",
+ "labels": "Étiquettes",
+ "create_new_label": "Créer une nouvelle étiquette",
+ "start_date": "Date de début",
+ "end_date": "Date de fin",
+ "due_date": "Date d'échéance",
+ "estimate": "Estimation",
+ "change_parent_issue": "Changer l'élément de travail parent",
+ "remove_parent_issue": "Supprimer l'élément de travail parent",
+ "add_parent": "Ajouter un parent",
+ "loading_members": "Chargement des membres",
+ "view_link_copied_to_clipboard": "Lien de la vue copié dans le presse-papiers.",
+ "required": "Requis",
+ "optional": "Optionnel",
+ "Cancel": "Annuler",
+ "edit": "Modifier",
+ "archive": "Archiver",
+ "restore": "Restaurer",
+ "open_in_new_tab": "Ouvrir dans un nouvel onglet",
+ "delete": "Supprimer",
+ "deleting": "Suppression",
+ "make_a_copy": "Faire une copie",
+ "move_to_project": "Déplacer vers le projet",
+ "good": "Bonjour",
+ "morning": "matin",
+ "afternoon": "après-midi",
+ "evening": "soir",
+ "show_all": "Tout afficher",
+ "show_less": "Afficher moins",
+ "no_data_yet": "Pas encore de données",
+ "syncing": "Synchronisation",
+ "add_work_item": "Ajouter un élément de travail",
+ "advanced_description_placeholder": "Appuyez sur '/' pour les commandes",
+ "create_work_item": "Créer un élément de travail",
+ "attachments": "Pièces jointes",
+ "declining": "Refus",
+ "declined": "Refusé",
+ "decline": "Refuser",
+ "unassigned": "Non assigné",
+ "work_items": "Éléments de travail",
+ "add_link": "Ajouter un lien",
+ "points": "Points",
+ "no_assignee": "Pas d'assigné",
+ "no_assignees_yet": "Pas encore d'assignés",
+ "no_labels_yet": "Pas encore d'étiquettes",
+ "ideal": "Idéal",
+ "current": "Actuel",
+ "no_matching_members": "Aucun membre correspondant",
+ "leaving": "Départ",
+ "removing": "Suppression",
+ "leave": "Quitter",
+ "refresh": "Actualiser",
+ "refreshing": "Actualisation",
+ "refresh_status": "Actualiser l'état",
+ "prev": "Précédent",
+ "next": "Suivant",
+ "re_generating": "Régénération",
+ "re_generate": "Régénérer",
+ "re_generate_key": "Régénérer la clé",
+ "export": "Exporter",
+ "member": "{count, plural, one{# membre} other{# membres}}",
+
+ "project_view": {
+ "sort_by": {
+ "created_at": "Créé le",
+ "updated_at": "Mis à jour le",
+ "name": "Nom"
+ }
+ },
+
+ "toast": {
+ "success": "Succès !",
+ "error": "Erreur !"
+ },
+
+ "links": {
+ "toasts": {
+ "created": {
+ "title": "Lien créé",
+ "message": "Le lien a été créé avec succès"
+ },
+ "not_created": {
+ "title": "Lien non créé",
+ "message": "Le lien n'a pas pu être créé"
+ },
+ "updated": {
+ "title": "Lien mis à jour",
+ "message": "Le lien a été mis à jour avec succès"
+ },
+ "not_updated": {
+ "title": "Lien non mis à jour",
+ "message": "Le lien n'a pas pu être mis à jour"
+ },
+ "removed": {
+ "title": "Lien supprimé",
+ "message": "Le lien a été supprimé avec succès"
+ },
+ "not_removed": {
+ "title": "Lien non supprimé",
+ "message": "Le lien n'a pas pu être supprimé"
+ }
+ }
+ },
+
+ "home": {
+ "empty": {
+ "quickstart_guide": "Guide de démarrage rapide",
+ "not_right_now": "Pas maintenant",
+ "create_project": {
+ "title": "Créer un projet",
+ "description": "La plupart des choses commencent par un projet dans Plane.",
+ "cta": "Commencer"
+ },
+ "invite_team": {
+ "title": "Inviter votre équipe",
+ "description": "Construisez, déployez et gérez avec vos collègues.",
+ "cta": "Les faire entrer"
+ },
+ "configure_workspace": {
+ "title": "Configurez votre espace de travail.",
+ "description": "Activez ou désactivez des fonctionnalités ou allez plus loin.",
+ "cta": "Configurer cet espace de travail"
+ },
+ "personalize_account": {
+ "title": "Faites de Plane le vôtre.",
+ "description": "Choisissez votre photo, vos couleurs et plus encore.",
+ "cta": "Personnaliser maintenant"
+ },
+ "widgets": {
+ "title": "C'est calme sans widgets, activez-les",
+ "description": "Il semble que tous vos widgets soient désactivés. Activez-les\nmaintenant pour améliorer votre expérience !",
+ "primary_button": {
+ "text": "Gérer les widgets"
+ }
+ }
+ },
+ "quick_links": {
+ "empty": "Enregistrez des liens vers des éléments de travail que vous souhaitez avoir à portée de main.",
+ "add": "Ajouter un lien rapide",
+ "title": "Lien rapide",
+ "title_plural": "Liens rapides"
+ },
+ "recents": {
+ "title": "Récents",
+ "empty": {
+ "project": "Vos projets récents apparaîtront ici une fois que vous en aurez visité un.",
+ "page": "Vos pages récentes apparaîtront ici une fois que vous en aurez visité une.",
+ "issue": "Vos éléments de travail récents apparaîtront ici une fois que vous en aurez visité un.",
+ "default": "Vous n'avez pas encore d'éléments récents."
+ },
+ "filters": {
+ "all": "Tous les éléments",
+ "projects": "Projets",
+ "pages": "Pages",
+ "issues": "Éléments de travail"
+ }
+ },
+ "new_at_plane": {
+ "title": "Nouveau sur Plane"
+ },
+ "quick_tutorial": {
+ "title": "Tutoriel rapide"
+ },
+ "widget": {
+ "reordered_successfully": "Widget réorganisé avec succès.",
+ "reordering_failed": "Une erreur s'est produite lors de la réorganisation du widget."
+ },
+ "manage_widgets": "Gérer les widgets",
+ "title": "Accueil",
+ "star_us_on_github": "Donnez-nous une étoile sur GitHub"
+ },
+
+ "link": {
+ "modal": {
+ "url": {
+ "text": "URL",
+ "required": "L'URL n'est pas valide",
+ "placeholder": "Tapez ou collez une URL"
+ },
+ "title": {
+ "text": "Titre d'affichage",
+ "placeholder": "Comment souhaitez-vous voir ce lien"
+ }
+ }
+ },
+
+ "common": {
+ "all": "Tout",
+ "states": "États",
+ "state": "État",
+ "state_groups": "Groupes d'états",
+ "priority": "Priorité",
+ "team_project": "Projet d'équipe",
+ "project": "Projet",
+ "cycle": "Cycle",
+ "cycles": "Cycles",
+ "module": "Module",
+ "modules": "Modules",
+ "labels": "Étiquettes",
+ "assignees": "Assignés",
+ "assignee": "Assigné",
+ "created_by": "Créé par",
+ "none": "Aucun",
+ "link": "Lien",
+ "estimate": "Estimation",
+ "layout": "Disposition",
+ "filters": "Filtres",
+ "display": "Affichage",
+ "load_more": "Charger plus",
+ "activity": "Activité",
+ "analytics": "Analyses",
+ "dates": "Dates",
+ "success": "Succès !",
+ "something_went_wrong": "Quelque chose s'est mal passé",
+ "error": {
+ "label": "Erreur !",
+ "message": "Une erreur s'est produite. Veuillez réessayer."
+ },
+ "group_by": "Grouper par",
+ "epic": "Epic",
+ "epics": "Epics",
+ "work_item": "Élément de travail",
+ "work_items": "Éléments de travail",
+ "sub_work_item": "Sous-élément de travail",
+ "add": "Ajouter",
+ "warning": "Avertissement",
+ "updating": "Mise à jour",
+ "adding": "Ajout",
+ "update": "Mettre à jour",
+ "creating": "Création",
+ "create": "Créer",
+ "cancel": "Annuler",
+ "description": "Description",
+ "title": "Titre",
+ "attachment": "Pièce jointe",
+ "general": "Général",
+ "features": "Fonctionnalités",
+ "automation": "Automatisation",
+ "project_name": "Nom du projet",
+ "project_id": "ID du projet",
+ "project_timezone": "Fuseau horaire du projet",
+ "created_on": "Créé le",
+ "update_project": "Mettre à jour le projet",
+ "identifier_already_exists": "L'identifiant existe déjà",
+ "add_more": "Ajouter plus",
+ "defaults": "Par défaut",
+ "add_label": "Ajouter une étiquette",
+ "estimates": "Estimations",
+ "customize_time_range": "Personnaliser la plage de temps",
+ "loading": "Chargement",
+ "attachments": "Pièces jointes",
+ "property": "Propriété",
+ "properties": "Propriétés",
+ "parent": "Parent",
+ "remove": "Supprimer",
+ "archiving": "Archivage",
+ "archive": "Archiver",
+ "access": {
+ "public": "Public",
+ "private": "Privé"
+ },
+ "done": "Terminé",
+ "sub_work_items": "Sous-éléments de travail",
+ "comment": "Commentaire",
+ "workspace_level": "Niveau espace de travail",
+ "order_by": {
+ "label": "Trier par",
+ "manual": "Manuel",
+ "last_created": "Dernier créé",
+ "last_updated": "Dernière mise à jour",
+ "start_date": "Date de début",
+ "due_date": "Date d'échéance",
+ "asc": "Croissant",
+ "desc": "Décroissant",
+ "updated_on": "Mis à jour le"
+ },
+ "sort": {
+ "asc": "Croissant",
+ "desc": "Décroissant",
+ "created_on": "Créé le",
+ "updated_on": "Mis à jour le"
+ },
+ "comments": "Commentaires",
+ "updates": "Mises à jour",
+ "clear_all": "Tout effacer",
+ "copied": "Copié !",
+ "link_copied": "Lien copié !",
+ "link_copied_to_clipboard": "Lien copié dans le presse-papiers",
+ "copied_to_clipboard": "Lien de l'élément de travail copié dans le presse-papiers",
+ "is_copied_to_clipboard": "L'élément de travail est copié dans le presse-papiers",
+ "no_links_added_yet": "Aucun lien ajouté pour l'instant",
+ "add_link": "Ajouter un lien",
+ "links": "Liens",
+ "go_to_workspace": "Aller à l'espace de travail",
+ "progress": "Progression",
+ "optional": "Optionnel",
+ "join": "Rejoindre",
+ "go_back": "Retour",
+ "continue": "Continuer",
+ "resend": "Renvoyer",
+ "relations": "Relations",
+ "errors": {
+ "default": {
+ "title": "Erreur !",
+ "message": "Quelque chose s'est mal passé. Veuillez réessayer."
+ },
+ "required": "Ce champ est obligatoire",
+ "entity_required": "{entity} est requis"
+ },
+ "update_link": "Mettre à jour le lien",
+ "attach": "Joindre",
+ "create_new": "Créer nouveau",
+ "add_existing": "Ajouter existant",
+ "type_or_paste_a_url": "Tapez ou collez une URL",
+ "url_is_invalid": "L'URL n'est pas valide",
+ "display_title": "Titre d'affichage",
+ "link_title_placeholder": "Comment souhaitez-vous voir ce lien",
+ "url": "URL",
+ "side_peek": "Aperçu latéral",
+ "modal": "Modal",
+ "full_screen": "Plein écran",
+ "close_peek_view": "Fermer l'aperçu",
+ "toggle_peek_view_layout": "Basculer la disposition de l'aperçu",
+ "options": "Options",
+ "duration": "Durée",
+ "today": "Aujourd'hui",
+ "week": "Semaine",
+ "month": "Mois",
+ "quarter": "Trimestre",
+ "press_for_commands": "Appuyez sur '/' pour les commandes",
+ "click_to_add_description": "Cliquez pour ajouter une description",
+ "search": {
+ "label": "Rechercher",
+ "placeholder": "Tapez pour rechercher",
+ "no_matches_found": "Aucune correspondance trouvée",
+ "no_matching_results": "Aucun résultat correspondant"
+ },
+ "actions": {
+ "edit": "Modifier",
+ "make_a_copy": "Faire une copie",
+ "open_in_new_tab": "Ouvrir dans un nouvel onglet",
+ "copy_link": "Copier le lien",
+ "archive": "Archiver",
+ "delete": "Supprimer",
+ "remove_relation": "Supprimer la relation",
+ "subscribe": "S'abonner",
+ "unsubscribe": "Se désabonner",
+ "clear_sorting": "Effacer le tri",
+ "show_weekends": "Afficher les week-ends",
+ "enable": "Activer",
+ "disable": "Désactiver"
+ },
+ "name": "Nom",
+ "discard": "Abandonner",
+ "confirm": "Confirmer",
+ "confirming": "Confirmation",
+ "read_the_docs": "Lire la documentation",
+ "default": "Par défaut",
+ "active": "Actif",
+ "enabled": "Activé",
+ "disabled": "Désactivé",
+ "mandate": "Mandat",
+ "mandatory": "Obligatoire",
+ "yes": "Oui",
+ "no": "Non",
+ "please_wait": "Veuillez patienter",
+ "enabling": "Activation",
+ "disabling": "Désactivation",
+ "beta": "Bêta",
+ "or": "ou",
+ "next": "Suivant",
+ "back": "Retour",
+ "cancelling": "Annulation",
+ "configuring": "Configuration",
+ "clear": "Effacer",
+ "import": "Importer",
+ "connect": "Connecter",
+ "authorizing": "Autorisation",
+ "processing": "Traitement",
+ "no_data_available": "Aucune donnée disponible",
+ "from": "de {name}",
+ "authenticated": "Authentifié",
+ "select": "Sélectionner",
+ "upgrade": "Mettre à niveau",
+ "add_seats": "Ajouter des sièges",
+ "label": "Étiquette",
+ "priorities": "Priorités",
+ "projects": "Projets",
+ "workspace": "Espace de travail",
+ "workspaces": "Espaces de travail",
+ "team": "Équipe",
+ "teams": "Équipes",
+ "entity": "Entité",
+ "entities": "Entités",
+ "task": "Tâche",
+ "tasks": "Tâches",
+ "section": "Section",
+ "sections": "Sections",
+ "edit": "Modifier",
+ "connecting": "Connexion",
+ "connected": "Connecté",
+ "disconnect": "Déconnecter",
+ "disconnecting": "Déconnexion",
+ "installing": "Installation",
+ "install": "Installer",
+ "reset": "Réinitialiser",
+ "live": "En direct",
+ "change_history": "Historique des modifications",
+ "coming_soon": "À venir",
+ "members": "Membres",
+ "you": "Vous",
+ "upgrade_cta": {
+ "higher_subscription": "Passer à une abonnement plus élevé",
+ "talk_to_sales": "Parler à la vente"
+ },
+ "category": "Catégorie",
+ "categories": "Catégories",
+ "saving": "Enregistrement",
+ "save_changes": "Enregistrer les modifications",
+ "delete": "Supprimer",
+ "deleting": "Suppression",
+ "pending": "En attente",
+ "invite": "Inviter"
+ },
+
+ "chart": {
+ "x_axis": "Axe X",
+ "y_axis": "Axe Y",
+ "metric": "Métrique"
+ },
+
+ "form": {
+ "title": {
+ "required": "Le titre est requis",
+ "max_length": "Le titre doit contenir moins de {length} caractères"
+ }
+ },
+
+ "entity": {
+ "grouping_title": "Regroupement {entity}",
+ "priority": "Priorité {entity}",
+ "all": "Tous les {entity}",
+ "drop_here_to_move": "Déposez ici pour déplacer le {entity}",
+ "delete": {
+ "label": "Supprimer {entity}",
+ "success": "{entity} supprimé avec succès",
+ "failed": "Échec de la suppression de {entity}"
+ },
+ "update": {
+ "failed": "Échec de la mise à jour de {entity}",
+ "success": "{entity} mis à jour avec succès"
+ },
+ "link_copied_to_clipboard": "Lien {entity} copié dans le presse-papiers",
+ "fetch": {
+ "failed": "Erreur lors de la récupération de {entity}"
+ },
+ "add": {
+ "success": "{entity} ajouté avec succès",
+ "failed": "Erreur lors de l'ajout de {entity}"
+ }
+ },
+
+ "epic": {
+ "all": "Tous les Epics",
+ "label": "{count, plural, one {Epic} other {Epics}}",
+ "new": "Nouvel Epic",
+ "adding": "Ajout d'un epic",
+ "create": {
+ "success": "Epic créé avec succès"
+ },
+ "add": {
+ "press_enter": "Appuyez sur 'Entrée' pour ajouter un autre epic",
+ "label": "Ajouter un Epic"
+ },
+ "title": {
+ "label": "Titre de l'Epic",
+ "required": "Le titre de l'Epic est requis."
+ }
+ },
+
+ "issue": {
+ "label": "{count, plural, one {Élément de travail} other {Éléments de travail}}",
+ "all": "Tous les éléments de travail",
+ "edit": "Modifier l'élément de travail",
+ "title": {
+ "label": "Titre de l'élément de travail",
+ "required": "Le titre de l'élément de travail est requis."
+ },
+ "add": {
+ "press_enter": "Appuyez sur 'Entrée' pour ajouter un autre élément de travail",
+ "label": "Ajouter un élément de travail",
+ "cycle": {
+ "failed": "L'élément de travail n'a pas pu être ajouté au cycle. Veuillez réessayer.",
+ "success": "{count, plural, one {Élément de travail} other {Éléments de travail}} ajouté(s) au cycle avec succès.",
+ "loading": "Ajout de {count, plural, one {l'élément de travail} other {éléments de travail}} au cycle"
+ },
+ "assignee": "Ajouter des assignés",
+ "start_date": "Ajouter une date de début",
+ "due_date": "Ajouter une date d'échéance",
+ "parent": "Ajouter un élément de travail parent",
+ "sub_issue": "Ajouter un sous-élément de travail",
+ "relation": "Ajouter une relation",
+ "link": "Ajouter un lien",
+ "existing": "Ajouter un élément de travail existant"
+ },
+ "remove": {
+ "label": "Supprimer l'élément de travail",
+ "cycle": {
+ "loading": "Suppression de l'élément de travail du cycle",
+ "success": "Élément de travail supprimé du cycle avec succès.",
+ "failed": "L'élément de travail n'a pas pu être supprimé du cycle. Veuillez réessayer."
+ },
+ "module": {
+ "loading": "Suppression de l'élément de travail du module",
+ "success": "Élément de travail supprimé du module avec succès.",
+ "failed": "L'élément de travail n'a pas pu être supprimé du module. Veuillez réessayer."
+ },
+ "parent": {
+ "label": "Supprimer l'élément de travail parent"
+ }
+ },
+ "new": "Nouvel élément de travail",
+ "adding": "Ajout d'un élément de travail",
+ "create": {
+ "success": "Élément de travail créé avec succès"
+ },
+ "priority": {
+ "urgent": "Urgent",
+ "high": "Haute",
+ "medium": "Moyenne",
+ "low": "Basse"
+ },
+ "display": {
+ "properties": {
+ "label": "Propriétés d'affichage",
+ "id": "ID",
+ "issue_type": "Type d'élément de travail",
+ "sub_issue_count": "Nombre de sous-éléments",
+ "attachment_count": "Nombre de pièces jointes",
+ "created_on": "Créé le",
+ "sub_issue": "Sous-élément de travail"
+ },
+ "extra": {
+ "show_sub_issues": "Afficher les sous-éléments",
+ "show_empty_groups": "Afficher les groupes vides"
+ }
+ },
+ "layouts": {
+ "ordered_by_label": "Cette disposition est triée par",
+ "list": "Liste",
+ "kanban": "Tableau",
+ "calendar": "Calendrier",
+ "spreadsheet": "Tableau",
+ "gantt": "Chronologie",
+ "title": {
+ "list": "Disposition en liste",
+ "kanban": "Disposition en tableau",
+ "calendar": "Disposition en calendrier",
+ "spreadsheet": "Disposition en tableau",
+ "gantt": "Disposition en chronologie"
+ }
+ },
+ "states": {
+ "active": "Actif",
+ "backlog": "Backlog"
+ },
+ "comments": {
+ "placeholder": "Ajouter un commentaire",
+ "switch": {
+ "private": "Passer en commentaire privé",
+ "public": "Passer en commentaire public"
+ },
+ "create": {
+ "success": "Commentaire créé avec succès",
+ "error": "Échec de la création du commentaire. Veuillez réessayer plus tard."
+ },
+ "update": {
+ "success": "Commentaire mis à jour avec succès",
+ "error": "Échec de la mise à jour du commentaire. Veuillez réessayer plus tard."
+ },
+ "remove": {
+ "success": "Commentaire supprimé avec succès",
+ "error": "Échec de la suppression du commentaire. Veuillez réessayer plus tard."
+ },
+ "upload": {
+ "error": "Échec du téléchargement du fichier. Veuillez réessayer plus tard."
+ }
+ },
+ "empty_state": {
+ "issue_detail": {
+ "title": "L'élément de travail n'existe pas",
+ "description": "L'élément de travail que vous recherchez n'existe pas, a été archivé ou a été supprimé.",
+ "primary_button": {
+ "text": "Voir les autres éléments de travail"
+ }
+ }
+ },
+ "sibling": {
+ "label": "Éléments de travail frères"
+ },
+ "archive": {
+ "description": "Seuls les éléments de travail\nterminés ou annulés peuvent être archivés",
+ "label": "Archiver l'élément de travail",
+ "confirm_message": "Êtes-vous sûr de vouloir archiver l'élément de travail ? Tous vos éléments archivés peuvent être restaurés ultérieurement.",
+ "success": {
+ "label": "Archivage réussi",
+ "message": "Vos archives se trouvent dans les archives du projet."
+ },
+ "failed": {
+ "message": "L'élément de travail n'a pas pu être archivé. Veuillez réessayer."
+ }
+ },
+ "restore": {
+ "success": {
+ "title": "Restauration réussie",
+ "message": "Votre élément de travail se trouve dans les éléments de travail du projet."
+ },
+ "failed": {
+ "message": "L'élément de travail n'a pas pu être restauré. Veuillez réessayer."
+ }
+ },
+ "relation": {
+ "relates_to": "En relation avec",
+ "duplicate": "Doublon de",
+ "blocked_by": "Bloqué par",
+ "blocking": "Bloque"
+ },
+ "copy_link": "Copier le lien de l'élément de travail",
+ "delete": {
+ "label": "Supprimer l'élément de travail",
+ "error": "Erreur lors de la suppression de l'élément de travail"
+ },
+ "subscription": {
+ "actions": {
+ "subscribed": "Abonnement à l'élément de travail réussi",
+ "unsubscribed": "Désabonnement de l'élément de travail réussi"
+ }
+ },
+ "select": {
+ "error": "Veuillez sélectionner au moins un élément de travail",
+ "empty": "Aucun élément de travail sélectionné",
+ "add_selected": "Ajouter les éléments de travail sélectionnés"
+ },
+ "open_in_full_screen": "Ouvrir l'élément de travail en plein écran"
+ },
+
+ "attachment": {
+ "error": "Le fichier n'a pas pu être joint. Essayez de le télécharger à nouveau.",
+ "only_one_file_allowed": "Un seul fichier peut être téléchargé à la fois.",
+ "file_size_limit": "Le fichier doit faire {size}MB ou moins.",
+ "drag_and_drop": "Glissez-déposez n'importe où pour télécharger",
+ "delete": "Supprimer la pièce jointe"
+ },
+
+ "label": {
+ "select": "Sélectionner une étiquette",
+ "create": {
+ "success": "Étiquette créée avec succès",
+ "failed": "Échec de la création de l'étiquette",
+ "already_exists": "L'étiquette existe déjà",
+ "type": "Tapez pour ajouter une nouvelle étiquette"
+ }
+ },
+
+ "sub_work_item": {
+ "update": {
+ "success": "Sous-élément de travail mis à jour avec succès",
+ "error": "Erreur lors de la mise à jour du sous-élément de travail"
+ },
+ "remove": {
+ "success": "Sous-élément de travail supprimé avec succès",
+ "error": "Erreur lors de la suppression du sous-élément de travail"
+ }
+ },
+
+ "view": {
+ "label": "{count, plural, one {Vue} other {Vues}}",
+ "create": {
+ "label": "Créer une vue"
+ },
+ "update": {
+ "label": "Mettre à jour la vue"
+ }
+ },
+
+ "inbox_issue": {
+ "status": {
+ "pending": {
+ "title": "En attente",
+ "description": "En attente"
+ },
+ "declined": {
+ "title": "Refusé",
+ "description": "Refusé"
+ },
+ "snoozed": {
+ "title": "Reporté",
+ "description": "{days, plural, one{# jour} other{# jours}} restant(s)"
+ },
+ "accepted": {
+ "title": "Accepté",
+ "description": "Accepté"
+ },
+ "duplicate": {
+ "title": "Doublon",
+ "description": "Doublon"
+ }
+ },
+ "modals": {
+ "decline": {
+ "title": "Refuser l'élément de travail",
+ "content": "Êtes-vous sûr de vouloir refuser l'élément de travail {value} ?"
+ },
+ "delete": {
+ "title": "Supprimer l'élément de travail",
+ "content": "Êtes-vous sûr de vouloir supprimer l'élément de travail {value} ?",
+ "success": "Élément de travail supprimé avec succès"
+ }
+ },
+ "errors": {
+ "snooze_permission": "Seuls les administrateurs du projet peuvent reporter/annuler le report des éléments de travail",
+ "accept_permission": "Seuls les administrateurs du projet peuvent accepter les éléments de travail",
+ "decline_permission": "Seuls les administrateurs du projet peuvent refuser les éléments de travail"
+ },
+ "actions": {
+ "accept": "Accepter",
+ "decline": "Refuser",
+ "snooze": "Reporter",
+ "unsnooze": "Annuler le report",
+ "copy": "Copier le lien de l'élément de travail",
+ "delete": "Supprimer",
+ "open": "Ouvrir l'élément de travail",
+ "mark_as_duplicate": "Marquer comme doublon",
+ "move": "Déplacer {value} vers les éléments de travail du projet"
+ },
+ "source": {
+ "in-app": "in-app"
+ },
+ "order_by": {
+ "created_at": "Créé le",
+ "updated_at": "Mis à jour le",
+ "id": "ID"
+ },
+ "label": "Intake",
+ "page_label": "{workspace} - Intake",
+ "modal": {
+ "title": "Créer un élément de travail Intake"
+ },
+ "tabs": {
+ "open": "Ouvert",
+ "closed": "Fermé"
+ },
+ "empty_state": {
+ "sidebar_open_tab": {
+ "title": "Aucun élément de travail ouvert",
+ "description": "Trouvez les éléments de travail ouverts ici. Créez un nouvel élément de travail."
+ },
+ "sidebar_closed_tab": {
+ "title": "Aucun élément de travail fermé",
+ "description": "Tous les éléments de travail, qu'ils soient acceptés ou refusés, peuvent être trouvés ici."
+ },
+ "sidebar_filter": {
+ "title": "Aucun élément de travail correspondant",
+ "description": "Aucun élément de travail ne correspond au filtre appliqué dans Intake. Créez un nouvel élément de travail."
+ },
+ "detail": {
+ "title": "Sélectionnez un élément de travail pour voir ses détails."
+ }
+ }
+ },
+
+ "workspace_creation": {
+ "heading": "Créez votre espace de travail",
+ "subheading": "Pour commencer à utiliser Plane, vous devez créer ou rejoindre un espace de travail.",
+ "form": {
+ "name": {
+ "label": "Nommez votre espace de travail",
+ "placeholder": "Quelque chose de familier et reconnaissable est toujours préférable."
+ },
+ "url": {
+ "label": "Définissez l'URL de votre espace de travail",
+ "placeholder": "Tapez ou collez une URL",
+ "edit_slug": "Vous ne pouvez modifier que le slug de l'URL"
+ },
+ "organization_size": {
+ "label": "Combien de personnes utiliseront cet espace de travail ?",
+ "placeholder": "Sélectionnez une plage"
+ }
+ },
+ "errors": {
+ "creation_disabled": {
+ "title": "Seul l'administrateur de votre instance peut créer des espaces de travail",
+ "description": "Si vous connaissez l'adresse e-mail de votre administrateur d'instance, cliquez sur le bouton ci-dessous pour le contacter.",
+ "request_button": "Contacter l'administrateur d'instance"
+ },
+ "validation": {
+ "name_alphanumeric": "Les noms d'espaces de travail ne peuvent contenir que (' '), ('-'), ('_') et des caractères alphanumériques.",
+ "name_length": "Limitez votre nom à 80 caractères.",
+ "url_alphanumeric": "Les URL ne peuvent contenir que ('-') et des caractères alphanumériques.",
+ "url_length": "Limitez votre URL à 48 caractères.",
+ "url_already_taken": "L'URL de l'espace de travail est déjà prise !"
+ }
+ },
+ "request_email": {
+ "subject": "Demande d'un nouvel espace de travail",
+ "body": "Bonjour administrateur(s) d'instance,\n\nVeuillez créer un nouvel espace de travail avec l'URL [/workspace-name] pour [objectif de création de l'espace de travail].\n\nMerci,\n{firstName} {lastName}\n{email}"
+ },
+ "button": {
+ "default": "Créer l'espace de travail",
+ "loading": "Création de l'espace de travail"
+ },
+ "toast": {
+ "success": {
+ "title": "Succès",
+ "message": "Espace de travail créé avec succès"
+ },
+ "error": {
+ "title": "Erreur",
+ "message": "L'espace de travail n'a pas pu être créé. Veuillez réessayer."
+ }
+ }
+ },
+
+ "workspace_dashboard": {
+ "empty_state": {
+ "general": {
+ "title": "Aperçu de vos projets, activités et métriques",
+ "description": "Bienvenue sur Plane, nous sommes ravis de vous avoir parmi nous. Créez votre premier projet et suivez vos éléments de travail, et cette page se transformera en un espace qui vous aide à progresser. Les administrateurs verront également les éléments qui aident leur équipe à progresser.",
+ "primary_button": {
+ "text": "Construisez votre premier projet",
+ "comic": {
+ "title": "Tout commence par un projet dans Plane",
+ "description": "Un projet peut être la feuille de route d'un produit, une campagne marketing ou le lancement d'une nouvelle voiture."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_analytics": {
+ "label": "Analytique",
+ "page_label": "{workspace} - Analytique",
+ "open_tasks": "Total des tâches ouvertes",
+ "error": "Une erreur s'est produite lors de la récupération des données.",
+ "work_items_closed_in": "Éléments de travail fermés dans",
+ "selected_projects": "Projets sélectionnés",
+ "total_members": "Total des membres",
+ "total_cycles": "Total des Cycles",
+ "total_modules": "Total des Modules",
+ "pending_work_items": {
+ "title": "Éléments de travail en attente",
+ "empty_state": "L'analyse des éléments de travail en attente par collègues apparaît ici."
+ },
+ "work_items_closed_in_a_year": {
+ "title": "Éléments de travail fermés dans l'année",
+ "empty_state": "Fermez des éléments de travail pour voir leur analyse sous forme de graphique."
+ },
+ "most_work_items_created": {
+ "title": "Plus d'éléments de travail créés",
+ "empty_state": "Les collègues et le nombre d'éléments de travail créés par eux apparaissent ici."
+ },
+ "most_work_items_closed": {
+ "title": "Plus d'éléments de travail fermés",
+ "empty_state": "Les collègues et le nombre d'éléments de travail fermés par eux apparaissent ici."
+ },
+ "tabs": {
+ "scope_and_demand": "Portée et Demande",
+ "custom": "Analytique Personnalisée"
+ },
+ "empty_state": {
+ "general": {
+ "title": "Suivez les progrès, les charges de travail et les allocations. Repérez les tendances, supprimez les blocages et accélérez le travail",
+ "description": "Visualisez la portée par rapport à la demande, les estimations et l'augmentation de la portée. Obtenez les performances par membres de l'équipe et équipes, et assurez-vous que votre projet se déroule dans les délais.",
+ "primary_button": {
+ "text": "Commencez votre premier projet",
+ "comic": {
+ "title": "L'analytique fonctionne mieux avec les Cycles + Modules",
+ "description": "D'abord, planifiez vos éléments de travail dans des Cycles et, si possible, regroupez les éléments de travail qui s'étendent sur plus d'un cycle dans des Modules. Consultez les deux dans la navigation de gauche."
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_projects": {
+ "label": "{count, plural, one {Projet} other {Projets}}",
+ "create": {
+ "label": "Ajouter un Projet"
+ },
+ "network": {
+ "private": {
+ "title": "Privé",
+ "description": "Accessible uniquement sur invitation"
+ },
+ "public": {
+ "title": "Public",
+ "description": "Accessible à tous dans l'espace de travail sauf les invités"
+ }
+ },
+ "error": {
+ "permission": "Vous n'avez pas la permission d'effectuer cette action.",
+ "cycle_delete": "Échec de la suppression du cycle",
+ "module_delete": "Échec de la suppression du module",
+ "issue_delete": "Échec de la suppression de l'élément de travail"
+ },
+ "state": {
+ "backlog": "Backlog",
+ "unstarted": "Non commencé",
+ "started": "Commencé",
+ "completed": "Terminé",
+ "cancelled": "Annulé"
+ },
+ "sort": {
+ "manual": "Manuel",
+ "name": "Nom",
+ "created_at": "Date de création",
+ "members_length": "Nombre de membres"
+ },
+ "scope": {
+ "my_projects": "Mes projets",
+ "archived_projects": "Archivés"
+ },
+ "common": {
+ "months_count": "{months, plural, one{# mois} other{# mois}}"
+ },
+ "empty_state": {
+ "general": {
+ "title": "Aucun projet actif",
+ "description": "Considérez chaque projet comme le parent d'un travail orienté objectif. Les projets sont l'endroit où vivent les Tâches, les Cycles et les Modules et, avec vos collègues, vous aident à atteindre cet objectif. Créez un nouveau projet ou filtrez les projets archivés.",
+ "primary_button": {
+ "text": "Commencez votre premier projet",
+ "comic": {
+ "title": "Tout commence par un projet dans Plane",
+ "description": "Un projet peut être la feuille de route d'un produit, une campagne marketing ou le lancement d'une nouvelle voiture."
+ }
+ }
+ },
+ "no_projects": {
+ "title": "Aucun projet",
+ "description": "Pour créer des éléments de travail ou gérer votre travail, vous devez créer un projet ou faire partie d'un projet.",
+ "primary_button": {
+ "text": "Commencez votre premier projet",
+ "comic": {
+ "title": "Tout commence par un projet dans Plane",
+ "description": "Un projet peut être la feuille de route d'un produit, une campagne marketing ou le lancement d'une nouvelle voiture."
+ }
+ }
+ },
+ "filter": {
+ "title": "Aucun projet correspondant",
+ "description": "Aucun projet détecté avec les critères correspondants. \n Créez plutôt un nouveau projet."
+ },
+ "search": {
+ "description": "Aucun projet détecté avec les critères correspondants.\nCréez plutôt un nouveau projet"
+ }
+ }
+ },
+
+ "workspace_views": {
+ "add_view": "Ajouter une vue",
+ "empty_state": {
+ "all-issues": {
+ "title": "Aucun élément de travail dans le projet",
+ "description": "Premier projet terminé ! Maintenant, découpez votre travail en morceaux traçables avec des éléments de travail. Allons-y !",
+ "primary_button": {
+ "text": "Créer un nouvel élément de travail"
+ }
+ },
+ "assigned": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Les éléments de travail qui vous sont assignés peuvent être suivis ici.",
+ "primary_button": {
+ "text": "Créer un nouvel élément de travail"
+ }
+ },
+ "created": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Tous les éléments de travail que vous créez arrivent ici, suivez-les directement ici.",
+ "primary_button": {
+ "text": "Créer un nouvel élément de travail"
+ }
+ },
+ "subscribed": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Abonnez-vous aux éléments de travail qui vous intéressent, suivez-les tous ici."
+ },
+ "custom-view": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Les éléments de travail qui correspondent aux filtres, suivez-les tous ici."
+ }
+ }
+ },
+
+ "workspace_settings": {
+ "label": "Paramètres de l'espace de travail",
+ "page_label": "{workspace} - Paramètres généraux",
+ "key_created": "Clé créée",
+ "copy_key": "Copiez et sauvegardez cette clé secrète dans Plane Pages. Vous ne pourrez plus voir cette clé après avoir cliqué sur Fermer. Un fichier CSV contenant la clé a été téléchargé.",
+ "token_copied": "Jeton copié dans le presse-papiers.",
+ "settings": {
+ "general": {
+ "title": "Général",
+ "upload_logo": "Télécharger le logo",
+ "edit_logo": "Modifier le logo",
+ "name": "Nom de l'espace de travail",
+ "company_size": "Taille de l'entreprise",
+ "url": "URL de l'espace de travail",
+ "update_workspace": "Mettre à jour l'espace de travail",
+ "delete_workspace": "Supprimer cet espace de travail",
+ "delete_workspace_description": "Lors de la suppression d'un espace de travail, toutes les données et ressources au sein de cet espace seront définitivement supprimées et ne pourront pas être récupérées.",
+ "delete_btn": "Supprimer cet espace de travail",
+ "delete_modal": {
+ "title": "Êtes-vous sûr de vouloir supprimer cet espace de travail ?",
+ "description": "Vous avez un essai actif sur l'un de nos forfaits payants. Veuillez d'abord l'annuler pour continuer.",
+ "dismiss": "Fermer",
+ "cancel": "Annuler l'essai",
+ "success_title": "Espace de travail supprimé.",
+ "success_message": "Vous serez bientôt redirigé vers votre page de profil.",
+ "error_title": "Cela n'a pas fonctionné.",
+ "error_message": "Veuillez réessayer."
+ },
+ "errors": {
+ "name": {
+ "required": "Le nom est requis",
+ "max_length": "Le nom de l'espace de travail ne doit pas dépasser 80 caractères"
+ },
+ "company_size": {
+ "required": "La taille de l'entreprise est requise"
+ }
+ }
+ },
+ "members": {
+ "title": "Membres",
+ "add_member": "Ajouter un membre",
+ "pending_invites": "Invitations en attente",
+ "invitations_sent_successfully": "Invitations envoyées avec succès",
+ "leave_confirmation": "Êtes-vous sûr de vouloir quitter l'espace de travail ? Vous n'aurez plus accès à cet espace de travail. Cette action ne peut pas être annulée.",
+ "details": {
+ "full_name": "Nom complet",
+ "display_name": "Nom d'affichage",
+ "email_address": "Adresse e-mail",
+ "account_type": "Type de compte",
+ "authentication": "Authentification",
+ "joining_date": "Date d'adhésion"
+ },
+ "modal": {
+ "title": "Inviter des personnes à collaborer",
+ "description": "Invitez des personnes à collaborer sur votre espace de travail.",
+ "button": "Envoyer les invitations",
+ "button_loading": "Envoi des invitations",
+ "placeholder": "nom@entreprise.com",
+ "errors": {
+ "required": "Nous avons besoin d'une adresse e-mail pour les inviter.",
+ "invalid": "L'e-mail est invalide"
+ }
+ }
+ },
+ "billing_and_plans": {
+ "title": "Facturation & Plans",
+ "current_plan": "Plan actuel",
+ "free_plan": "Vous utilisez actuellement le plan gratuit",
+ "view_plans": "Voir les plans"
+ },
+ "exports": {
+ "title": "Exportations",
+ "exporting": "Exportation",
+ "previous_exports": "Exportations précédentes",
+ "export_separate_files": "Exporter les données dans des fichiers séparés",
+ "modal": {
+ "title": "Exporter vers",
+ "toasts": {
+ "success": {
+ "title": "Exportation réussie",
+ "message": "Vous pourrez télécharger les {entity} exportés depuis l'exportation précédente."
+ },
+ "error": {
+ "title": "Échec de l'exportation",
+ "message": "L'exportation a échoué. Veuillez réessayer."
+ }
+ }
+ }
+ },
+ "webhooks": {
+ "title": "Webhooks",
+ "add_webhook": "Ajouter un webhook",
+ "modal": {
+ "title": "Créer un webhook",
+ "details": "Détails du webhook",
+ "payload": "URL de la charge utile",
+ "question": "Quels événements souhaitez-vous déclencher avec ce webhook ?",
+ "error": "L'URL est requise"
+ },
+ "secret_key": {
+ "title": "Clé secrète",
+ "message": "Générer un jeton pour signer la charge utile du webhook"
+ },
+ "options": {
+ "all": "Envoyez-moi tout",
+ "individual": "Sélectionner des événements individuels"
+ },
+ "toasts": {
+ "created": {
+ "title": "Webhook créé",
+ "message": "Le webhook a été créé avec succès"
+ },
+ "not_created": {
+ "title": "Webhook non créé",
+ "message": "Le webhook n'a pas pu être créé"
+ },
+ "updated": {
+ "title": "Webhook mis à jour",
+ "message": "Le webhook a été mis à jour avec succès"
+ },
+ "not_updated": {
+ "title": "Webhook non mis à jour",
+ "message": "Le webhook n'a pas pu être mis à jour"
+ },
+ "removed": {
+ "title": "Webhook supprimé",
+ "message": "Le webhook a été supprimé avec succès"
+ },
+ "not_removed": {
+ "title": "Webhook non supprimé",
+ "message": "Le webhook n'a pas pu être supprimé"
+ },
+ "secret_key_copied": {
+ "message": "Clé secrète copiée dans le presse-papiers."
+ },
+ "secret_key_not_copied": {
+ "message": "Une erreur s'est produite lors de la copie de la clé secrète."
+ }
+ }
+ },
+ "api_tokens": {
+ "title": "Jetons API",
+ "add_token": "Ajouter un jeton API",
+ "create_token": "Créer un jeton",
+ "never_expires": "N'expire jamais",
+ "generate_token": "Générer un jeton",
+ "generating": "Génération",
+ "delete": {
+ "title": "Supprimer le jeton API",
+ "description": "Toute application utilisant ce jeton n'aura plus accès aux données de Plane. Cette action ne peut pas être annulée.",
+ "success": {
+ "title": "Succès !",
+ "message": "Le jeton API a été supprimé avec succès"
+ },
+ "error": {
+ "title": "Erreur !",
+ "message": "Le jeton API n'a pas pu être supprimé"
+ }
+ }
+ }
+ },
+ "empty_state": {
+ "api_tokens": {
+ "title": "Aucun jeton API créé",
+ "description": "Les API Plane peuvent être utilisées pour intégrer vos données dans Plane avec n'importe quel système externe. Créez un jeton pour commencer."
+ },
+ "webhooks": {
+ "title": "Aucun webhook ajouté",
+ "description": "Créez des webhooks pour recevoir des mises à jour en temps réel et automatiser des actions."
+ },
+ "exports": {
+ "title": "Aucune exportation pour le moment",
+ "description": "Chaque fois que vous exportez, vous aurez également une copie ici pour référence."
+ },
+ "imports": {
+ "title": "Aucune importation pour le moment",
+ "description": "Trouvez toutes vos importations précédentes ici et téléchargez-les."
+ }
+ }
+ },
+
+ "profile": {
+ "label": "Profil",
+ "page_label": "Votre travail",
+ "work": "Travail",
+ "details": {
+ "joined_on": "Inscrit le",
+ "time_zone": "Fuseau horaire"
+ },
+ "stats": {
+ "workload": "Charge de travail",
+ "overview": "Vue d'ensemble",
+ "created": "Éléments de travail créés",
+ "assigned": "Éléments de travail assignés",
+ "subscribed": "Éléments de travail suivis",
+ "state_distribution": {
+ "title": "Éléments de travail par état",
+ "empty": "Créez des éléments de travail pour les visualiser par état dans le graphique pour une meilleure analyse."
+ },
+ "priority_distribution": {
+ "title": "Éléments de travail par priorité",
+ "empty": "Créez des éléments de travail pour les visualiser par priorité dans le graphique pour une meilleure analyse."
+ },
+ "recent_activity": {
+ "title": "Activité récente",
+ "empty": "Nous n'avons pas trouvé de données. Veuillez consulter vos contributions",
+ "button": "Télécharger l'activité du jour",
+ "button_loading": "Téléchargement"
+ }
+ },
+ "actions": {
+ "profile": "Profil",
+ "security": "Sécurité",
+ "activity": "Activité",
+ "appearance": "Apparence",
+ "notifications": "Notifications"
+ },
+ "tabs": {
+ "summary": "Résumé",
+ "assigned": "Assigné",
+ "created": "Créé",
+ "subscribed": "Suivi",
+ "activity": "Activité"
+ },
+ "empty_state": {
+ "activity": {
+ "title": "Aucune activité pour le moment",
+ "description": "Commencez par créer un nouvel élément de travail ! Ajoutez-y des détails et des propriétés. Explorez davantage Plane pour voir votre activité."
+ },
+ "assigned": {
+ "title": "Aucun élément de travail ne vous est assigné",
+ "description": "Les éléments de travail qui vous sont assignés peuvent être suivis ici."
+ },
+ "created": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Tous les éléments de travail que vous créez apparaissent ici, suivez-les directement ici."
+ },
+ "subscribed": {
+ "title": "Aucun élément de travail pour le moment",
+ "description": "Abonnez-vous aux éléments de travail qui vous intéressent, suivez-les tous ici."
+ }
+ }
+ },
+
+ "project_settings": {
+ "general": {
+ "enter_project_id": "Saisissez l'ID du projet",
+ "please_select_a_timezone": "Veuillez sélectionner un fuseau horaire",
+ "archive_project": {
+ "title": "Archiver le projet",
+ "description": "L'archivage d'un projet le retirera de votre navigation latérale, bien que vous pourrez toujours y accéder depuis votre page de projets. Vous pouvez restaurer le projet ou le supprimer quand vous le souhaitez.",
+ "button": "Archiver le projet"
+ },
+ "delete_project": {
+ "title": "Supprimer le projet",
+ "description": "Lors de la suppression d'un projet, toutes les données et ressources de ce projet seront définitivement supprimées et ne pourront pas être récupérées.",
+ "button": "Supprimer mon projet"
+ },
+ "toast": {
+ "success": "Projet mis à jour avec succès",
+ "error": "Le projet n'a pas pu être mis à jour. Veuillez réessayer."
+ }
+ },
+ "members": {
+ "label": "Membres",
+ "project_lead": "Chef de projet",
+ "default_assignee": "Assigné par défaut",
+ "guest_super_permissions": {
+ "title": "Accorder l'accès en lecture à tous les éléments de travail pour les utilisateurs invités :",
+ "sub_heading": "Cela permettra aux invités d'avoir un accès en lecture à tous les éléments de travail du projet."
+ },
+ "invite_members": {
+ "title": "Inviter des membres",
+ "sub_heading": "Invitez des membres à travailler sur votre projet.",
+ "select_co_worker": "Sélectionner un collaborateur"
+ }
+ },
+ "states": {
+ "describe_this_state_for_your_members": "Décrivez cet état pour vos membres.",
+ "empty_state": {
+ "title": "Aucun état disponible pour le groupe {groupKey}",
+ "description": "Veuillez créer un nouvel état"
+ }
+ },
+ "labels": {
+ "label_title": "Titre de l'étiquette",
+ "label_title_is_required": "Le titre de l'étiquette est requis",
+ "label_max_char": "Le nom de l'étiquette ne doit pas dépasser 255 caractères",
+ "toast": {
+ "error": "Erreur lors de la mise à jour de l'étiquette"
+ }
+ },
+ "estimates": {
+ "title": "Activer les estimations pour mon projet",
+ "description": "Elles vous aident à communiquer la complexité et la charge de travail de l'équipe."
+ },
+ "automations": {
+ "label": "Automatisations",
+ "auto-archive": {
+ "title": "Archiver automatiquement les éléments de travail fermés",
+ "description": "Plane archivera automatiquement les éléments de travail qui ont été terminés ou annulés.",
+ "duration": "Archiver automatiquement les éléments de travail qui sont fermés depuis"
+ },
+ "auto-close": {
+ "title": "Fermer automatiquement les éléments de travail",
+ "description": "Plane fermera automatiquement les éléments de travail qui n'ont pas été terminés ou annulés.",
+ "duration": "Fermer automatiquement les éléments de travail inactifs depuis",
+ "auto_close_status": "Statut de fermeture automatique"
+ }
+ },
+
+ "empty_state": {
+ "labels": {
+ "title": "Aucune étiquette pour le moment",
+ "description": "Créez des étiquettes pour aider à organiser et filtrer les éléments de travail dans votre projet."
+ },
+ "estimates": {
+ "title": "Aucun système d'estimation pour le moment",
+ "description": "Créez un ensemble d'estimations pour communiquer la quantité de travail par élément de travail.",
+ "primary_button": "Ajouter un système d'estimation"
+ }
+ }
+ },
+
+ "project_cycles": {
+ "add_cycle": "Ajouter un cycle",
+ "more_details": "Plus de détails",
+ "cycle": "Cycle",
+ "update_cycle": "Mettre à jour le cycle",
+ "create_cycle": "Créer un cycle",
+ "no_matching_cycles": "Aucun cycle correspondant",
+ "remove_filters_to_see_all_cycles": "Supprimez les filtres pour voir tous les cycles",
+ "remove_search_criteria_to_see_all_cycles": "Supprimez les critères de recherche pour voir tous les cycles",
+ "only_completed_cycles_can_be_archived": "Seuls les cycles terminés peuvent être archivés",
+ "active_cycle": {
+ "label": "Cycle actif",
+ "progress": "Progression",
+ "chart": "Graphique d'avancement",
+ "priority_issue": "Éléments de travail prioritaires",
+ "assignees": "Assignés",
+ "issue_burndown": "Graphique d'avancement des éléments",
+ "ideal": "Idéal",
+ "current": "Actuel",
+ "labels": "Étiquettes"
+ },
+ "upcoming_cycle": {
+ "label": "Cycle à venir"
+ },
+ "completed_cycle": {
+ "label": "Cycle terminé"
+ },
+ "status": {
+ "days_left": "Jours restants",
+ "completed": "Terminé",
+ "yet_to_start": "Pas encore commencé",
+ "in_progress": "En cours",
+ "draft": "Brouillon"
+ },
+ "action": {
+ "restore": {
+ "title": "Restaurer le cycle",
+ "success": {
+ "title": "Cycle restauré",
+ "description": "Le cycle a été restauré."
+ },
+ "failed": {
+ "title": "Échec de la restauration du cycle",
+ "description": "Le cycle n'a pas pu être restauré. Veuillez réessayer."
+ }
+ },
+ "favorite": {
+ "loading": "Ajout du cycle aux favoris",
+ "success": {
+ "description": "Cycle ajouté aux favoris.",
+ "title": "Succès !"
+ },
+ "failed": {
+ "description": "Impossible d'ajouter le cycle aux favoris. Veuillez réessayer.",
+ "title": "Erreur !"
+ }
+ },
+ "unfavorite": {
+ "loading": "Suppression du cycle des favoris",
+ "success": {
+ "description": "Cycle retiré des favoris.",
+ "title": "Succès !"
+ },
+ "failed": {
+ "description": "Impossible de retirer le cycle des favoris. Veuillez réessayer.",
+ "title": "Erreur !"
+ }
+ },
+ "update": {
+ "loading": "Mise à jour du cycle",
+ "success": {
+ "description": "Cycle mis à jour avec succès.",
+ "title": "Succès !"
+ },
+ "failed": {
+ "description": "Erreur lors de la mise à jour du cycle. Veuillez réessayer.",
+ "title": "Erreur !"
+ },
+ "error": {
+ "already_exists": "Vous avez déjà un cycle aux dates indiquées. Si vous souhaitez créer un cycle en brouillon, vous pouvez le faire en supprimant les deux dates."
+ }
+ }
+ },
+ "empty_state": {
+ "general": {
+ "title": "Regroupez et planifiez votre travail en Cycles.",
+ "description": "Découpez le travail en périodes définies, planifiez à rebours depuis la date limite de votre projet pour fixer les dates, et progressez concrètement en équipe.",
+ "primary_button": {
+ "text": "Définissez votre premier cycle",
+ "comic": {
+ "title": "Les cycles sont des périodes répétitives.",
+ "description": "Un sprint, une itération, ou tout autre terme que vous utilisez pour le suivi hebdomadaire ou bimensuel du travail est un cycle."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "Aucun élément de travail ajouté au cycle",
+ "description": "Ajoutez ou créez des éléments de travail que vous souhaitez planifier et livrer dans ce cycle",
+ "primary_button": {
+ "text": "Créer un nouvel élément de travail"
+ },
+ "secondary_button": {
+ "text": "Ajouter un élément existant"
+ }
+ },
+ "completed_no_issues": {
+ "title": "Aucun élément de travail dans le cycle",
+ "description": "Aucun élément de travail dans le cycle. Les éléments sont soit transférés soit masqués. Pour voir les éléments masqués s'il y en a, mettez à jour vos propriétés d'affichage en conséquence."
+ },
+ "active": {
+ "title": "Aucun cycle actif",
+ "description": "Un cycle actif inclut toute période qui englobe la date d'aujourd'hui dans sa plage. Trouvez ici la progression et les détails du cycle actif."
+ },
+ "archived": {
+ "title": "Aucun cycle archivé pour le moment",
+ "description": "Pour organiser votre projet, archivez les cycles terminés. Retrouvez-les ici une fois archivés."
+ }
+ }
+ },
+
+ "project_issues": {
+ "empty_state": {
+ "no_issues": {
+ "title": "Créez un élément de travail et assignez-le à quelqu'un, même à vous-même",
+ "description": "Pensez aux éléments de travail comme des tâches, du travail, ou des JTBD (Jobs To Be Done). Un élément de travail et ses sous-éléments sont généralement des actions temporelles assignées aux membres de votre équipe. Votre équipe crée, assigne et complète des éléments de travail pour faire progresser votre projet vers son objectif.",
+ "primary_button": {
+ "text": "Créez votre premier élément de travail",
+ "comic": {
+ "title": "Les éléments de travail sont les blocs de construction dans Plane.",
+ "description": "Refondre l'interface de Plane, Renouveler l'image de marque de l'entreprise, ou Lancer le nouveau système d'injection de carburant sont des exemples d'éléments de travail qui ont probablement des sous-éléments."
+ }
+ }
+ },
+ "no_archived_issues": {
+ "title": "Aucun élément de travail archivé pour le moment",
+ "description": "Manuellement ou par automatisation, vous pouvez archiver les éléments de travail terminés ou annulés. Retrouvez-les ici une fois archivés.",
+ "primary_button": {
+ "text": "Configurer l'automatisation"
+ }
+ },
+ "issues_empty_filter": {
+ "title": "Aucun élément de travail trouvé correspondant aux filtres appliqués",
+ "secondary_button": {
+ "text": "Effacer tous les filtres"
+ }
+ }
+ }
+ },
+
+ "project_module": {
+ "add_module": "Ajouter un module",
+ "update_module": "Mettre à jour le module",
+ "create_module": "Créer un module",
+ "archive_module": "Archiver le module",
+ "restore_module": "Restaurer le module",
+ "delete_module": "Supprimer le module",
+ "empty_state": {
+ "general": {
+ "title": "Associez vos jalons de projet aux Modules et suivez facilement le travail agrégé.",
+ "description": "Un groupe d'éléments de travail qui appartiennent à un parent logique et hiérarchique forme un module. Considérez-les comme un moyen de suivre le travail par jalons de projet. Ils ont leurs propres périodes et délais ainsi que des analyses pour vous aider à voir à quel point vous êtes proche ou loin d'un jalon.",
+ "primary_button": {
+ "text": "Construisez votre premier module",
+ "comic": {
+ "title": "Les modules aident à regrouper le travail par hiérarchie.",
+ "description": "Un module panier, un module châssis et un module entrepôt sont tous de bons exemples de ce regroupement."
+ }
+ }
+ },
+ "no_issues": {
+ "title": "Aucun élément de travail dans le module",
+ "description": "Créez ou ajoutez des éléments de travail que vous souhaitez accomplir dans le cadre de ce module",
+ "primary_button": {
+ "text": "Créer de nouveaux éléments de travail"
+ },
+ "secondary_button": {
+ "text": "Ajouter un élément existant"
+ }
+ },
+ "archived": {
+ "title": "Aucun module archivé pour le moment",
+ "description": "Pour organiser votre projet, archivez les modules terminés ou annulés. Retrouvez-les ici une fois archivés."
+ },
+ "sidebar": {
+ "in_active": "Ce module n'est pas encore actif.",
+ "invalid_date": "Date invalide. Veuillez entrer une date valide."
+ }
+ },
+ "quick_actions": {
+ "archive_module": "Archiver le module",
+ "archive_module_description": "Seuls les modules terminés ou\nannulés peuvent être archivés.",
+ "delete_module": "Supprimer le module"
+ },
+ "toast": {
+ "copy": {
+ "success": "Lien du module copié dans le presse-papiers"
+ },
+ "delete": {
+ "success": "Module supprimé avec succès",
+ "error": "Échec de la suppression du module"
+ }
+ }
+ },
+
+ "project_views": {
+ "empty_state": {
+ "general": {
+ "title": "Enregistrez des vues filtrées pour votre projet. Créez-en autant que nécessaire",
+ "description": "Les vues sont un ensemble de filtres enregistrés que vous utilisez fréquemment ou auxquels vous souhaitez avoir un accès facile. Tous vos collègues dans un projet peuvent voir les vues de chacun et choisir celle qui convient le mieux à leurs besoins.",
+ "primary_button": {
+ "text": "Créez votre première vue",
+ "comic": {
+ "title": "Les vues fonctionnent sur les propriétés des éléments de travail.",
+ "description": "Vous pouvez créer une vue ici avec autant de propriétés comme filtres que vous le jugez approprié."
+ }
+ }
+ },
+ "filter": {
+ "title": "Aucune vue correspondante",
+ "description": "Aucune vue ne correspond aux critères de recherche. \n Créez plutôt une nouvelle vue."
+ }
+ }
+ },
+
+ "project_page": {
+ "empty_state": {
+ "general": {
+ "title": "Rédigez une note, un document ou une base de connaissances complète. Obtenez l'aide de Galileo, l'assistant IA de Plane, pour commencer",
+ "description": "Les Pages sont un espace de réflexion dans Plane. Prenez des notes de réunion, formatez-les facilement, intégrez des éléments de travail, disposez-les à l'aide d'une bibliothèque de composants, et gardez-les tous dans le contexte de votre projet. Pour faciliter la rédaction de tout document, faites appel à Galileo, l'IA de Plane, avec un raccourci ou un clic sur un bouton.",
+ "primary_button": {
+ "text": "Créez votre première page"
+ }
+ },
+ "private": {
+ "title": "Pas encore de pages privées",
+ "description": "Gardez vos pensées privées ici. Quand vous serez prêt à partager, l'équipe n'est qu'à un clic.",
+ "primary_button": {
+ "text": "Créez votre première page"
+ }
+ },
+ "public": {
+ "title": "Pas encore de pages publiques",
+ "description": "Consultez ici les pages partagées avec tout le monde dans votre projet.",
+ "primary_button": {
+ "text": "Créez votre première page"
+ }
+ },
+ "archived": {
+ "title": "Pas encore de pages archivées",
+ "description": "Archivez les pages qui ne sont pas dans votre radar. Accédez-y ici quand nécessaire."
+ }
+ }
+ },
+
+ "command_k": {
+ "empty_state": {
+ "search": {
+ "title": "Aucun résultat trouvé"
+ }
+ }
+ },
+
+ "issue_relation": {
+ "empty_state": {
+ "search": {
+ "title": "Aucun élément de travail correspondant trouvé"
+ },
+ "no_issues": {
+ "title": "Aucun élément de travail trouvé"
+ }
+ }
+ },
+
+ "issue_comment": {
+ "empty_state": {
+ "general": {
+ "title": "Pas encore de commentaires",
+ "description": "Les commentaires peuvent être utilisés comme espace de discussion et de suivi pour les éléments de travail"
+ }
+ }
+ },
+
+ "notification": {
+ "label": "Boîte de réception",
+ "page_label": "{workspace} - Boîte de réception",
+ "options": {
+ "mark_all_as_read": "Tout marquer comme lu",
+ "mark_read": "Marquer comme lu",
+ "mark_unread": "Marquer comme non lu",
+ "refresh": "Actualiser",
+ "filters": "Filtres de la boîte de réception",
+ "show_unread": "Afficher les non lus",
+ "show_snoozed": "Afficher les reportés",
+ "show_archived": "Afficher les archivés",
+ "mark_archive": "Archiver",
+ "mark_unarchive": "Désarchiver",
+ "mark_snooze": "Reporter",
+ "mark_unsnooze": "Annuler le report"
+ },
+ "toasts": {
+ "read": "Notification marquée comme lue",
+ "unread": "Notification marquée comme non lue",
+ "archived": "Notification marquée comme archivée",
+ "unarchived": "Notification marquée comme non archivée",
+ "snoozed": "Notification reportée",
+ "unsnoozed": "Report de la notification annulé"
+ },
+ "empty_state": {
+ "detail": {
+ "title": "Sélectionnez pour voir les détails."
+ },
+ "all": {
+ "title": "Aucun élément de travail assigné",
+ "description": "Les mises à jour des éléments de travail qui vous sont assignés peuvent être \n vues ici"
+ },
+ "mentions": {
+ "title": "Aucun élément de travail assigné",
+ "description": "Les mises à jour des éléments de travail qui vous sont assignés peuvent être \n vues ici"
+ }
+ },
+ "tabs": {
+ "all": "Tout",
+ "mentions": "Mentions"
+ },
+ "filter": {
+ "assigned": "Assigné à moi",
+ "created": "Créé par moi",
+ "subscribed": "Suivi par moi"
+ },
+ "snooze": {
+ "1_day": "1 jour",
+ "3_days": "3 jours",
+ "5_days": "5 jours",
+ "1_week": "1 semaine",
+ "2_weeks": "2 semaines",
+ "custom": "Personnalisé"
+ }
+ },
+
+ "active_cycle": {
+ "empty_state": {
+ "progress": {
+ "title": "Ajoutez des éléments de travail au cycle pour voir sa progression"
+ },
+ "chart": {
+ "title": "Ajoutez des éléments de travail au cycle pour voir le graphique d'avancement."
+ },
+ "priority_issue": {
+ "title": "Observez en un coup d'œil les éléments de travail prioritaires traités dans le cycle."
+ },
+ "assignee": {
+ "title": "Ajoutez des assignés aux éléments de travail pour voir une répartition du travail par assigné."
+ },
+ "label": {
+ "title": "Ajoutez des étiquettes aux éléments de travail pour voir la répartition du travail par étiquette."
+ }
+ }
+ },
+
+ "disabled_project": {
+ "empty_state": {
+ "inbox": {
+ "title": "L'Intake n'est pas activé pour le projet.",
+ "description": "L'Intake vous aide à gérer les demandes entrantes dans votre projet et à les ajouter comme éléments de travail dans votre flux. Activez l'Intake depuis les paramètres du projet pour gérer les demandes.",
+ "primary_button": {
+ "text": "Gérer les fonctionnalités"
+ }
+ },
+ "cycle": {
+ "title": "Les Cycles ne sont pas activés pour ce projet.",
+ "description": "Découpez le travail en segments temporels, planifiez à rebours depuis la date limite de votre projet pour définir les dates, et progressez concrètement en équipe. Activez la fonctionnalité Cycles pour votre projet pour commencer à les utiliser.",
+ "primary_button": {
+ "text": "Gérer les fonctionnalités"
+ }
+ },
+ "module": {
+ "title": "Les Modules ne sont pas activés pour le projet.",
+ "description": "Les Modules sont les éléments constitutifs de votre projet. Activez les modules depuis les paramètres du projet pour commencer à les utiliser.",
+ "primary_button": {
+ "text": "Gérer les fonctionnalités"
+ }
+ },
+ "page": {
+ "title": "Les Pages ne sont pas activées pour le projet.",
+ "description": "Les Pages sont les éléments constitutifs de votre projet. Activez les pages depuis les paramètres du projet pour commencer à les utiliser.",
+ "primary_button": {
+ "text": "Gérer les fonctionnalités"
+ }
+ },
+ "view": {
+ "title": "Les Vues ne sont pas activées pour le projet.",
+ "description": "Les Vues sont les éléments constitutifs de votre projet. Activez les vues depuis les paramètres du projet pour commencer à les utiliser.",
+ "primary_button": {
+ "text": "Gérer les fonctionnalités"
+ }
+ }
+ }
+ },
+ "workspace_draft_issues": {
+ "draft_an_issue": "Créer un brouillon d'élément de travail",
+ "empty_state": {
+ "title": "Les éléments de travail partiellement rédigés, et bientôt les commentaires, apparaîtront ici.",
+ "description": "Pour essayer cela, commencez à ajouter un élément de travail et laissez-le à mi-chemin ou créez votre premier brouillon ci-dessous. 😉",
+ "primary_button": {
+ "text": "Créez votre premier brouillon"
+ }
+ },
+ "delete_modal": {
+ "title": "Supprimer le brouillon",
+ "description": "Êtes-vous sûr de vouloir supprimer ce brouillon ? Cette action ne peut pas être annulée."
+ },
+ "toasts": {
+ "created": {
+ "success": "Brouillon créé",
+ "error": "L'élément de travail n'a pas pu être créé. Veuillez réessayer."
+ },
+ "deleted": {
+ "success": "Brouillon supprimé"
+ }
+ }
+ },
+
+ "stickies": {
+ "title": "Vos notes adhésives",
+ "placeholder": "cliquez pour écrire ici",
+ "all": "Toutes les notes adhésives",
+ "no-data": "Notez une idée, capturez une révélation ou enregistrez une inspiration. Ajoutez une note adhésive pour commencer.",
+ "add": "Ajouter une note adhésive",
+ "search_placeholder": "Rechercher par titre",
+ "delete": "Supprimer la note adhésive",
+ "delete_confirmation": "Êtes-vous sûr de vouloir supprimer cette note adhésive ?",
+ "empty_state": {
+ "simple": "Notez une idée, capturez une révélation ou enregistrez une inspiration. Ajoutez une note adhésive pour commencer.",
+ "general": {
+ "title": "Les notes adhésives sont des notes rapides et des tâches que vous prenez à la volée.",
+ "description": "Capturez vos pensées et idées facilement en créant des notes adhésives que vous pouvez consulter à tout moment et de n'importe où.",
+ "primary_button": {
+ "text": "Ajouter une note adhésive"
+ }
+ },
+ "search": {
+ "title": "Cela ne correspond à aucune de vos notes adhésives.",
+ "description": "Essayez un terme différent ou faites-nous savoir\nsi vous êtes sûr que votre recherche est correcte.",
+ "primary_button": {
+ "text": "Ajouter une note adhésive"
+ }
+ }
+ },
+ "toasts": {
+ "errors": {
+ "wrong_name": "Le nom de la note adhésive ne peut pas dépasser 100 caractères.",
+ "already_exists": "Il existe déjà une note adhésive sans description"
+ },
+ "created": {
+ "title": "Note adhésive créée",
+ "message": "La note adhésive a été créée avec succès"
+ },
+ "not_created": {
+ "title": "Note adhésive non créée",
+ "message": "La note adhésive n'a pas pu être créée"
+ },
+ "updated": {
+ "title": "Note adhésive mise à jour",
+ "message": "La note adhésive a été mise à jour avec succès"
+ },
+ "not_updated": {
+ "title": "Note adhésive non mise à jour",
+ "message": "La note adhésive n'a pas pu être mise à jour"
+ },
+ "removed": {
+ "title": "Note adhésive supprimée",
+ "message": "La note adhésive a été supprimée avec succès"
+ },
+ "not_removed": {
+ "title": "Note adhésive non supprimée",
+ "message": "La note adhésive n'a pas pu être supprimée"
+ }
+ }
+ },
+
+ "role_details": {
+ "guest": {
+ "title": "Invité",
+ "description": "Les membres externes des organisations peuvent être invités en tant qu'invités."
+ },
+ "member": {
+ "title": "Membre",
+ "description": "Capacité à lire, écrire, modifier et supprimer des entités dans les projets, cycles et modules"
+ },
+ "admin": {
+ "title": "Administrateur",
+ "description": "Toutes les permissions sont activées dans l'espace de travail."
+ }
+ },
+
+ "user_roles": {
+ "product_or_project_manager": "Chef de produit / Chef de projet",
+ "development_or_engineering": "Développement / Ingénierie",
+ "founder_or_executive": "Fondateur / Dirigeant",
+ "freelancer_or_consultant": "Freelance / Consultant",
+ "marketing_or_growth": "Marketing / Croissance",
+ "sales_or_business_development": "Ventes / Développement commercial",
+ "support_or_operations": "Support / Opérations",
+ "student_or_professor": "Étudiant / Professeur",
+ "human_resources": "Ressources Humaines",
+ "other": "Autre"
+ },
+
+ "importer": {
+ "github": {
+ "title": "GitHub",
+ "description": "Importez des éléments de travail depuis les dépôts GitHub et synchronisez-les."
+ },
+ "jira": {
+ "title": "Jira",
+ "description": "Importez des éléments de travail et des epics depuis les projets et epics Jira."
+ }
+ },
+
+ "exporter": {
+ "csv": {
+ "title": "CSV",
+ "description": "Exportez les éléments de travail vers un fichier CSV.",
+ "short_description": "Exporter en csv"
+ },
+ "excel": {
+ "title": "Excel",
+ "description": "Exportez les éléments de travail vers un fichier Excel.",
+ "short_description": "Exporter en excel"
+ },
+ "xlsx": {
+ "title": "Excel",
+ "description": "Exportez les éléments de travail vers un fichier Excel.",
+ "short_description": "Exporter en excel"
+ },
+ "json": {
+ "title": "JSON",
+ "description": "Exportez les éléments de travail vers un fichier JSON.",
+ "short_description": "Exporter en json"
+ }
+ },
+ "default_global_view": {
+ "all_issues": "Tous les éléments de travail",
+ "assigned": "Assignés",
+ "created": "Créés",
+ "subscribed": "Suivis"
+ },
+
+ "themes": {
+ "theme_options": {
+ "system_preference": {
+ "label": "Préférence système"
+ },
+ "light": {
+ "label": "Clair"
+ },
+ "dark": {
+ "label": "Sombre"
+ },
+ "light_contrast": {
+ "label": "Contraste clair élevé"
+ },
+ "dark_contrast": {
+ "label": "Contraste sombre élevé"
+ },
+ "custom": {
+ "label": "Thème personnalisé"
+ }
+ }
+ },
+ "project_modules": {
+ "status": {
+ "backlog": "Backlog",
+ "planned": "Planifié",
+ "in_progress": "En cours",
+ "paused": "En pause",
+ "completed": "Terminé",
+ "cancelled": "Annulé"
+ },
+ "layout": {
+ "list": "Vue liste",
+ "board": "Vue galerie",
+ "timeline": "Vue chronologique"
+ },
+ "order_by": {
+ "name": "Nom",
+ "progress": "Progression",
+ "issues": "Nombre d'éléments de travail",
+ "due_date": "Date d'échéance",
+ "created_at": "Date de création",
+ "manual": "Manuel"
+ }
+ },
+
+ "cycle": {
+ "label": "{count, plural, one {Cycle} other {Cycles}}",
+ "no_cycle": "Pas de cycle"
+ },
+
+ "module": {
+ "label": "{count, plural, one {Module} other {Modules}}",
+ "no_module": "Pas de module"
+ }
+}
diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json
new file mode 100644
index 000000000..eec78decc
--- /dev/null
+++ b/packages/i18n/src/locales/ja/translations.json
@@ -0,0 +1,2362 @@
+{
+ "sidebar": {
+ "projects": "プロジェクト",
+ "pages": "ページ",
+ "new_work_item": "新規作業項目",
+ "home": "ホーム",
+ "your_work": "あなたの作業",
+ "inbox": "受信トレイ",
+ "workspace": "ワークスペース",
+ "views": "ビュー",
+ "analytics": "アナリティクス",
+ "work_items": "作業項目",
+ "cycles": "サイクル",
+ "modules": "モジュール",
+ "intake": "インテーク",
+ "drafts": "下書き",
+ "favorites": "お気に入り",
+ "pro": "プロ",
+ "upgrade": "アップグレード"
+ },
+
+ "auth": {
+ "common": {
+ "email": {
+ "label": "メールアドレス",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "メールアドレスは必須です",
+ "invalid": "メールアドレスが無効です"
+ }
+ },
+ "password": {
+ "label": "パスワード",
+ "set_password": "パスワードを設定",
+ "placeholder": "パスワードを入力",
+ "confirm_password": {
+ "label": "パスワードの確認",
+ "placeholder": "パスワードを確認"
+ },
+ "current_password": {
+ "label": "現在のパスワード"
+ },
+ "new_password": {
+ "label": "新しいパスワード",
+ "placeholder": "新しいパスワードを入力"
+ },
+ "change_password": {
+ "label": {
+ "default": "パスワードを変更",
+ "submitting": "パスワードを変更中"
+ }
+ },
+ "errors": {
+ "match": "パスワードが一致しません",
+ "empty": "パスワードを入力してください",
+ "length": "パスワードは8文字以上である必要があります",
+ "strength": {
+ "weak": "パスワードが弱すぎます",
+ "strong": "パスワードは十分な強度です"
+ }
+ },
+ "submit": "パスワードを設定",
+ "toast": {
+ "change_password": {
+ "success": {
+ "title": "成功!",
+ "message": "パスワードが正常に変更されました。"
+ },
+ "error": {
+ "title": "エラー!",
+ "message": "問題が発生しました。もう一度お試しください。"
+ }
+ }
+ }
+ },
+ "unique_code": {
+ "label": "ユニークコード",
+ "placeholder": "gets-sets-flys",
+ "paste_code": "メールで送信されたコードを貼り付けてください",
+ "requesting_new_code": "新しいコードをリクエスト中",
+ "sending_code": "コードを送信中"
+ },
+ "already_have_an_account": "すでにアカウントをお持ちですか?",
+ "login": "ログイン",
+ "create_account": "アカウントを作成",
+ "new_to_plane": "Planeは初めてですか?",
+ "back_to_sign_in": "サインインに戻る",
+ "resend_in": "{seconds}秒後に再送信",
+ "sign_in_with_unique_code": "ユニークコードでサインイン",
+ "forgot_password": "パスワードをお忘れですか?"
+ },
+ "sign_up": {
+ "header": {
+ "label": "チームと作業を管理するためのアカウントを作成してください。",
+ "step": {
+ "email": {
+ "header": "サインアップ",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "サインアップ",
+ "sub_header": "メールアドレスとパスワードの組み合わせでサインアップ。"
+ },
+ "unique_code": {
+ "header": "サインアップ",
+ "sub_header": "上記のメールアドレスに送信されたユニークコードでサインアップ。"
+ }
+ }
+ },
+ "errors": {
+ "password": {
+ "strength": "強力なパスワードを設定して続行してください"
+ }
+ }
+ },
+ "sign_in": {
+ "header": {
+ "label": "チームと作業を管理するためにログインしてください。",
+ "step": {
+ "email": {
+ "header": "ログインまたはサインアップ",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "ログインまたはサインアップ",
+ "sub_header": "メールアドレスとパスワードの組み合わせでログイン。"
+ },
+ "unique_code": {
+ "header": "ログインまたはサインアップ",
+ "sub_header": "上記のメールアドレスに送信されたユニークコードでログイン。"
+ }
+ }
+ }
+ },
+ "forgot_password": {
+ "title": "パスワードをリセット",
+ "description": "確認済みのユーザーアカウントのメールアドレスを入力してください。パスワードリセットリンクを送信します。",
+ "email_sent": "リセットリンクをメールアドレスに送信しました",
+ "send_reset_link": "リセットリンクを送信",
+ "errors": {
+ "smtp_not_enabled": "管理者がSMTPを有効にしていないため、パスワードリセットリンクを送信できません"
+ },
+ "toast": {
+ "success": {
+ "title": "メール送信完了",
+ "message": "パスワードをリセットするためのリンクを受信トレイで確認してください。数分以内に表示されない場合は、迷惑メールフォルダを確認してください。"
+ },
+ "error": {
+ "title": "エラー!",
+ "message": "問題が発生しました。もう一度お試しください。"
+ }
+ }
+ },
+ "reset_password": {
+ "title": "新しいパスワードを設定",
+ "description": "強力なパスワードでアカウントを保護"
+ },
+ "set_password": {
+ "title": "アカウントを保護",
+ "description": "パスワードを設定して安全にログイン"
+ },
+ "sign_out": {
+ "toast": {
+ "error": {
+ "title": "エラー!",
+ "message": "サインアウトに失敗しました。もう一度お試しください。"
+ }
+ }
+ }
+ },
+
+ "submit": "送信",
+ "cancel": "キャンセル",
+ "loading": "読み込み中",
+ "error": "エラー",
+ "success": "成功",
+ "warning": "警告",
+ "info": "情報",
+ "close": "閉じる",
+ "yes": "はい",
+ "no": "いいえ",
+ "ok": "OK",
+ "name": "名前",
+ "description": "説明",
+ "search": "検索",
+ "add_member": "メンバーを追加",
+ "adding_members": "メンバーを追加中",
+ "remove_member": "メンバーを削除",
+ "add_members": "メンバーを追加",
+ "adding_member": "メンバーを追加中",
+ "remove_members": "メンバーを削除",
+ "add": "追加",
+ "adding": "追加中",
+ "remove": "削除",
+ "add_new": "新規追加",
+ "remove_selected": "選択項目を削除",
+ "first_name": "名",
+ "last_name": "姓",
+ "email": "メールアドレス",
+ "display_name": "表示名",
+ "role": "役割",
+ "timezone": "タイムゾーン",
+ "avatar": "アバター",
+ "cover_image": "カバー画像",
+ "password": "パスワード",
+ "change_cover": "カバーを変更",
+ "language": "言語",
+ "saving": "保存中",
+ "save_changes": "変更を保存",
+ "deactivate_account": "アカウントを無効化",
+ "deactivate_account_description": "アカウントを無効化すると、そのアカウント内のすべてのデータとリソースが完全に削除され、復元できなくなります。",
+ "profile_settings": "プロフィール設定",
+ "your_account": "あなたのアカウント",
+ "security": "セキュリティ",
+ "activity": "アクティビティ",
+ "appearance": "外観",
+ "notifications": "通知",
+ "workspaces": "ワークスペース",
+ "create_workspace": "ワークスペースを作成",
+ "invitations": "招待",
+ "summary": "概要",
+ "assigned": "割り当て済み",
+ "created": "作成済み",
+ "subscribed": "購読中",
+ "you_do_not_have_the_permission_to_access_this_page": "このページにアクセスする権限がありません。",
+ "something_went_wrong_please_try_again": "問題が発生しました。もう一度お試しください。",
+ "load_more": "もっと読み込む",
+ "select_or_customize_your_interface_color_scheme": "インターフェースの配色を選択またはカスタマイズしてください。",
+ "theme": "テーマ",
+ "system_preference": "システム設定に従う",
+ "light": "ライト",
+ "dark": "ダーク",
+ "light_contrast": "ライトハイコントラスト",
+ "dark_contrast": "ダークハイコントラスト",
+ "custom": "カスタムテーマ",
+ "select_your_theme": "テーマを選択",
+ "customize_your_theme": "テーマをカスタマイズ",
+ "background_color": "背景色",
+ "text_color": "文字色",
+ "primary_color": "プライマリ(テーマ)カラー",
+ "sidebar_background_color": "サイドバーの背景色",
+ "sidebar_text_color": "サイドバーの文字色",
+ "set_theme": "テーマを設定",
+ "enter_a_valid_hex_code_of_6_characters": "6文字の有効な16進コードを入力してください",
+ "background_color_is_required": "背景色は必須です",
+ "text_color_is_required": "文字色は必須です",
+ "primary_color_is_required": "プライマリカラーは必須です",
+ "sidebar_background_color_is_required": "サイドバーの背景色は必須です",
+ "sidebar_text_color_is_required": "サイドバーの文字色は必須です",
+ "updating_theme": "テーマを更新中",
+ "theme_updated_successfully": "テーマが正常に更新されました",
+ "failed_to_update_the_theme": "テーマの更新に失敗しました",
+ "email_notifications": "メール通知",
+ "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "購読している作業項目の最新情報を受け取ります。通知を受け取るには有効にしてください。",
+ "email_notification_setting_updated_successfully": "メール通知設定が正常に更新されました",
+ "failed_to_update_email_notification_setting": "メール通知設定の更新に失敗しました",
+ "notify_me_when": "通知を受け取るタイミング",
+ "property_changes": "プロパティの変更",
+ "property_changes_description": "作業項目の担当者、優先度、見積もりなどのプロパティが変更されたときに通知します。",
+ "state_change": "状態の変更",
+ "state_change_description": "作業項目が異なる状態に移動したときに通知します",
+ "issue_completed": "作業項目の完了",
+ "issue_completed_description": "作業項目が完了したときのみ通知します",
+ "comments": "コメント",
+ "comments_description": "誰かが作業項目にコメントを残したときに通知します",
+ "mentions": "メンション",
+ "mentions_description": "誰かがコメントや説明で自分をメンションしたときのみ通知します",
+ "old_password": "現在のパスワード",
+ "general_settings": "一般設定",
+ "sign_out": "サインアウト",
+ "signing_out": "サインアウト中",
+ "active_cycles": "アクティブサイクル",
+ "active_cycles_description": "プロジェクト全体のサイクルを監視し、優先度の高い作業項目を追跡し、注意が必要なサイクルにズームインします。",
+ "on_demand_snapshots_of_all_your_cycles": "すべてのサイクルのオンデマンドスナップショット",
+ "upgrade": "アップグレード",
+ "10000_feet_view": "すべてのアクティブサイクルの俯瞰図",
+ "10000_feet_view_description": "各プロジェクトのサイクル間を移動する代わりに、すべてのプロジェクトの実行中のサイクルを一度に確認できます。",
+ "get_snapshot_of_each_active_cycle": "各アクティブサイクルのスナップショットを取得",
+ "get_snapshot_of_each_active_cycle_description": "すべてのアクティブサイクルの主要な指標を追跡し、進捗状況を確認し、期限に対する範囲を把握します。",
+ "compare_burndowns": "バーンダウンを比較",
+ "compare_burndowns_description": "各サイクルのバーンダウンレポートを確認して、各チームのパフォーマンスを監視します。",
+ "quickly_see_make_or_break_issues": "重要な作業項目をすぐに確認",
+ "quickly_see_make_or_break_issues_description": "期限に対する各サイクルの優先度の高い作業項目をプレビューします。ワンクリックでサイクルごとにすべての項目を確認できます。",
+ "zoom_into_cycles_that_need_attention": "注意が必要なサイクルにズームイン",
+ "zoom_into_cycles_that_need_attention_description": "期待に沿わないサイクルの状態をワンクリックで調査します。",
+ "stay_ahead_of_blockers": "ブロッカーに先手を打つ",
+ "stay_ahead_of_blockers_description": "プロジェクト間の課題を特定し、他のビューでは明らかでないサイクル間の依存関係を確認します。",
+ "analytics": "アナリティクス",
+ "workspace_invites": "ワークスペースの招待",
+ "enter_god_mode": "ゴッドモードに入る",
+ "workspace_logo": "ワークスペースのロゴ",
+ "new_issue": "新規作業項目",
+ "your_work": "あなたの作業",
+ "drafts": "下書き",
+ "projects": "プロジェクト",
+ "views": "ビュー",
+ "workspace": "ワークスペース",
+ "archives": "アーカイブ",
+ "settings": "設定",
+ "failed_to_move_favorite": "お気に入りの移動に失敗しました",
+ "favorites": "お気に入り",
+ "no_favorites_yet": "まだお気に入りがありません",
+ "create_folder": "フォルダを作成",
+ "new_folder": "新規フォルダ",
+ "favorite_updated_successfully": "お気に入りが正常に更新されました",
+ "favorite_created_successfully": "お気に入りが正常に作成されました",
+ "folder_already_exists": "フォルダは既に存在します",
+ "folder_name_cannot_be_empty": "フォルダ名を空にすることはできません",
+ "something_went_wrong": "問題が発生しました",
+ "failed_to_reorder_favorite": "お気に入りの並び替えに失敗しました",
+ "favorite_removed_successfully": "お気に入りが正常に削除されました",
+ "failed_to_create_favorite": "お気に入りの作成に失敗しました",
+ "failed_to_rename_favorite": "お気に入りの名前変更に失敗しました",
+ "project_link_copied_to_clipboard": "プロジェクトリンクがクリップボードにコピーされました",
+ "link_copied": "リンクがコピーされました",
+ "add_project": "プロジェクトを追加",
+ "create_project": "プロジェクトを作成",
+ "failed_to_remove_project_from_favorites": "プロジェクトをお気に入りから削除できませんでした。もう一度お試しください。",
+ "project_created_successfully": "プロジェクトが正常に作成されました",
+ "project_created_successfully_description": "プロジェクトが正常に作成されました。作業項目を追加できるようになりました。",
+ "project_cover_image_alt": "プロジェクトのカバー画像",
+ "name_is_required": "名前は必須です",
+ "title_should_be_less_than_255_characters": "タイトルは255文字未満である必要があります",
+ "project_name": "プロジェクト名",
+ "project_id_must_be_at_least_1_character": "プロジェクトIDは最低1文字必要です",
+ "project_id_must_be_at_most_5_characters": "プロジェクトIDは最大5文字までです",
+ "project_id": "プロジェクトID",
+ "project_id_tooltip_content": "プロジェクト内の作業項目を一意に識別するのに役立ちます。最大5文字。",
+ "description_placeholder": "説明",
+ "only_alphanumeric_non_latin_characters_allowed": "英数字と非ラテン文字のみ使用できます。",
+ "project_id_is_required": "プロジェクトIDは必須です",
+ "project_id_allowed_char": "英数字と非ラテン文字のみ使用できます。",
+ "project_id_min_char": "プロジェクトIDは最低1文字必要です",
+ "project_id_max_char": "プロジェクトIDは最大5文字までです",
+ "project_description_placeholder": "プロジェクトの説明を入力",
+ "select_network": "ネットワークを選択",
+ "lead": "リード",
+ "date_range": "日付範囲",
+ "private": "プライベート",
+ "public": "パブリック",
+ "accessible_only_by_invite": "招待のみアクセス可能",
+ "anyone_in_the_workspace_except_guests_can_join": "ゲスト以外のワークスペースのメンバーが参加可能",
+ "creating": "作成中",
+ "creating_project": "プロジェクトを作成中",
+ "adding_project_to_favorites": "プロジェクトをお気に入りに追加中",
+ "project_added_to_favorites": "プロジェクトがお気に入りに追加されました",
+ "couldnt_add_the_project_to_favorites": "プロジェクトをお気に入りに追加できませんでした。もう一度お試しください。",
+ "removing_project_from_favorites": "プロジェクトをお気に入りから削除中",
+ "project_removed_from_favorites": "プロジェクトがお気に入りから削除されました",
+ "couldnt_remove_the_project_from_favorites": "プロジェクトをお気に入りから削除できませんでした。もう一度お試しください。",
+ "add_to_favorites": "お気に入りに追加",
+ "remove_from_favorites": "お気に入りから削除",
+ "publish_settings": "公開設定",
+ "publish": "公開",
+ "copy_link": "リンクをコピー",
+ "leave_project": "プロジェクトを退出",
+ "join_the_project_to_rearrange": "並び替えるにはプロジェクトに参加してください",
+ "drag_to_rearrange": "ドラッグして並び替え",
+ "congrats": "おめでとうございます!",
+ "open_project": "プロジェクトを開く",
+ "issues": "作業項目",
+ "cycles": "Cycles",
+ "modules": "Modules",
+ "pages": "Pages",
+ "intake": "Intake",
+ "time_tracking": "時間トラッキング",
+ "work_management": "作業管理",
+ "projects_and_issues": "プロジェクトと作業項目",
+ "projects_and_issues_description": "このプロジェクトでオン/オフを切り替えます。",
+ "cycles_description": "プロジェクトごとに作業を時間枠で区切り、期間を次の期間に変更します。",
+ "modules_description": "サブプロジェクトのような設定で、独自のリーダーと担当者を持つグループ作業を行います。",
+ "views_description": "並び替え、フィルター、表示オプションを後で使用するために保存するか、共有します。",
+ "pages_description": "何でも書けるように何でも書きます。",
+ "intake_description": "購読している作業項目の最新情報を受け取ります。通知を受け取るには有効にしてください。",
+ "time_tracking_description": "作業項目とプロジェクトの作業時間を追跡します。",
+ "work_management_description": "作業とプロジェクトを簡単に管理します。",
+ "documentation": "ドキュメント",
+ "message_support": "サポートにメッセージ",
+ "contact_sales": "営業に問い合わせ",
+ "hyper_mode": "Hyper Mode",
+ "keyboard_shortcuts": "キーボードショートカット",
+ "whats_new": "新機能",
+ "version": "バージョン",
+ "we_are_having_trouble_fetching_the_updates": "更新情報の取得に問題が発生しています。",
+ "our_changelogs": "変更履歴",
+ "for_the_latest_updates": "最新の更新情報については",
+ "please_visit": "をご覧ください",
+ "docs": "ドキュメント",
+ "full_changelog": "完全な変更履歴",
+ "support": "サポート",
+ "discord": "Discord",
+ "powered_by_plane_pages": "Powered by Plane Pages",
+ "please_select_at_least_one_invitation": "少なくとも1つの招待を選択してください。",
+ "please_select_at_least_one_invitation_description": "ワークスペースに参加するには少なくとも1つの招待を選択してください。",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace": "誰かがあなたをワークスペースに招待しています",
+ "join_a_workspace": "ワークスペースに参加",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "誰かがあなたをワークスペースに招待しています",
+ "join_a_workspace_description": "ワークスペースに参加",
+ "accept_and_join": "承諾して参加",
+ "go_home": "ホームへ",
+ "no_pending_invites": "保留中の招待はありません",
+ "you_can_see_here_if_someone_invites_you_to_a_workspace": "誰かがワークスペースに招待した場合、ここで確認できます",
+ "back_to_home": "ホームに戻る",
+ "workspace_name": "ワークスペース名",
+ "deactivate_your_account": "アカウントを無効化",
+ "deactivate_your_account_description": "無効化すると、作業項目を割り当てられなくなり、ワークスペースの請求対象外となります。アカウントを再有効化するには、このメールアドレスでワークスペースへの招待が必要です。",
+ "deactivating": "無効化中",
+ "confirm": "確認",
+ "confirming": "確認中",
+ "draft_created": "下書きが作成されました",
+ "issue_created_successfully": "作業項目が正常に作成されました",
+ "draft_creation_failed": "下書きの作成に失敗しました",
+ "issue_creation_failed": "作業項目の作成に失敗しました",
+ "draft_issue": "作業項目の下書き",
+ "issue_updated_successfully": "作業項目が正常に更新されました",
+ "issue_could_not_be_updated": "作業項目を更新できませんでした",
+ "create_a_draft": "下書きを作成",
+ "save_to_drafts": "下書きに保存",
+ "save": "保存",
+ "update": "更新",
+ "updating": "更新中",
+ "create_new_issue": "新規作業項目を作成",
+ "editor_is_not_ready_to_discard_changes": "エディターは変更を破棄する準備ができていません",
+ "failed_to_move_issue_to_project": "作業項目をプロジェクトに移動できませんでした",
+ "create_more": "さらに作成",
+ "add_to_project": "プロジェクトに追加",
+ "discard": "破棄",
+ "duplicate_issue_found": "重複する作業項目が見つかりました",
+ "duplicate_issues_found": "重複する作業項目が見つかりました",
+ "no_matching_results": "一致する結果がありません",
+ "title_is_required": "タイトルは必須です",
+ "title": "タイトル",
+ "state": "状態",
+ "priority": "優先度",
+ "none": "なし",
+ "urgent": "緊急",
+ "high": "高",
+ "medium": "中",
+ "low": "低",
+ "members": "メンバー",
+ "assignee": "担当者",
+ "assignees": "担当者",
+ "you": "あなた",
+ "labels": "ラベル",
+ "create_new_label": "新規ラベルを作成",
+ "start_date": "開始日",
+ "end_date": "終了日",
+ "due_date": "期限",
+ "estimate": "見積もり",
+ "change_parent_issue": "親作業項目を変更",
+ "remove_parent_issue": "親作業項目を削除",
+ "add_parent": "親を追加",
+ "loading_members": "メンバーを読み込み中",
+ "view_link_copied_to_clipboard": "ビューリンクがクリップボードにコピーされました。",
+ "required": "必須",
+ "optional": "任意",
+ "Cancel": "キャンセル",
+ "edit": "編集",
+ "archive": "アーカイブ",
+ "restore": "復元",
+ "open_in_new_tab": "新しいタブで開く",
+ "delete": "削除",
+ "deleting": "削除中",
+ "make_a_copy": "コピーを作成",
+ "move_to_project": "プロジェクトに移動",
+ "good": "おはよう",
+ "morning": "ございます",
+ "afternoon": "こんにちは",
+ "evening": "こんばんは",
+ "show_all": "すべて表示",
+ "show_less": "表示を減らす",
+ "no_data_yet": "まだデータがありません",
+ "syncing": "同期中",
+ "add_work_item": "作業項目を追加",
+ "advanced_description_placeholder": "コマンドには '/' を押してください",
+ "create_work_item": "作業項目を作成",
+ "attachments": "添付ファイル",
+ "declining": "辞退中",
+ "declined": "辞退済み",
+ "decline": "辞退",
+ "unassigned": "未割り当て",
+ "work_items": "作業項目",
+ "add_link": "リンクを追加",
+ "points": "ポイント",
+ "no_assignee": "担当者なし",
+ "no_assignees_yet": "まだ担当者がいません",
+ "no_labels_yet": "まだラベルがありません",
+ "ideal": "理想",
+ "current": "現在",
+ "no_matching_members": "一致するメンバーがいません",
+ "leaving": "退出中",
+ "removing": "削除中",
+ "leave": "退出",
+ "refresh": "更新",
+ "refreshing": "更新中",
+ "refresh_status": "状態を更新",
+ "prev": "前へ",
+ "next": "次へ",
+ "re_generating": "再生成中",
+ "re_generate": "再生成",
+ "re_generate_key": "キーを再生成",
+ "export": "エクスポート",
+ "member": "{count, plural, other{# メンバー}}",
+
+ "project_view": {
+ "sort_by": {
+ "created_at": "作成日時",
+ "updated_at": "更新日時",
+ "name": "名前"
+ }
+ },
+
+ "toast": {
+ "success": "成功!",
+ "error": "エラー!"
+ },
+
+ "links": {
+ "toasts": {
+ "created": {
+ "title": "リンクが作成されました",
+ "message": "リンクが正常に作成されました"
+ },
+ "not_created": {
+ "title": "リンクが作成されませんでした",
+ "message": "リンクを作成できませんでした"
+ },
+ "updated": {
+ "title": "リンクが更新されました",
+ "message": "リンクが正常に更新されました"
+ },
+ "not_updated": {
+ "title": "リンクが更新されませんでした",
+ "message": "リンクを更新できませんでした"
+ },
+ "removed": {
+ "title": "リンクが削除されました",
+ "message": "リンクが正常に削除されました"
+ },
+ "not_removed": {
+ "title": "リンクが削除されませんでした",
+ "message": "リンクを削除できませんでした"
+ }
+ }
+ },
+
+ "home": {
+ "empty": {
+ "quickstart_guide": "クイックスタートガイド",
+ "not_right_now": "今はしない",
+ "create_project": {
+ "title": "プロジェクトを作成",
+ "description": "Planeのほとんどはプロジェクトから始まります。",
+ "cta": "始める"
+ },
+ "invite_team": {
+ "title": "チームを招待",
+ "description": "同僚と一緒に構築、デプロイ、管理しましょう。",
+ "cta": "招待する"
+ },
+ "configure_workspace": {
+ "title": "ワークスペースを設定する。",
+ "description": "機能のオン/オフを切り替えたり、さらに詳細な設定を行ったりできます。",
+ "cta": "このワークスペースを設定"
+ },
+ "personalize_account": {
+ "title": "Planeをあなた好みにカスタマイズ。",
+ "description": "プロフィール画像、カラー、その他の設定を選択してください。",
+ "cta": "今すぐパーソナライズ"
+ },
+ "widgets": {
+ "title": "ウィジェットがないと静かですね、オンにしましょう",
+ "description": "すべてのウィジェットがオフになっているようです。体験を向上させるために\n今すぐ有効にしましょう!",
+ "primary_button": {
+ "text": "ウィジェットを管理"
+ }
+ }
+ },
+ "quick_links": {
+ "empty": "手元に置いておきたい作業関連のリンクを保存してください。",
+ "add": "クイックリンクを追加",
+ "title": "クイックリンク",
+ "title_plural": "クイックリンク"
+ },
+ "recents": {
+ "title": "最近",
+ "empty": {
+ "project": "プロジェクトを訪問すると、最近のプロジェクトがここに表示されます。",
+ "page": "ページを訪問すると、最近のページがここに表示されます。",
+ "issue": "作業項目を訪問すると、最近の作業項目がここに表示されます。",
+ "default": "まだ最近の項目がありません。"
+ },
+ "filters": {
+ "all": "すべての項目",
+ "projects": "プロジェクト",
+ "pages": "ページ",
+ "issues": "作業項目"
+ }
+ },
+ "new_at_plane": {
+ "title": "Planeの新機能"
+ },
+ "quick_tutorial": {
+ "title": "クイックチュートリアル"
+ },
+ "widget": {
+ "reordered_successfully": "ウィジェットの並び替えが完了しました。",
+ "reordering_failed": "ウィジェットの並び替え中にエラーが発生しました。"
+ },
+ "manage_widgets": "ウィジェットを管理",
+ "title": "ホーム",
+ "star_us_on_github": "GitHubでスターをつける"
+ },
+
+ "link": {
+ "modal": {
+ "url": {
+ "text": "URL",
+ "required": "URLが無効です",
+ "placeholder": "URLを入力または貼り付け"
+ },
+ "title": {
+ "text": "表示タイトル",
+ "placeholder": "このリンクをどのように表示したいか"
+ }
+ }
+ },
+
+ "common": {
+ "all": "すべて",
+ "states": "ステータス",
+ "state": "ステータス",
+ "state_groups": "ステータスグループ",
+ "priority": "優先度",
+ "team_project": "チームプロジェクト",
+ "project": "プロジェクト",
+ "cycle": "サイクル",
+ "cycles": "サイクル",
+ "module": "モジュール",
+ "modules": "モジュール",
+ "labels": "ラベル",
+ "assignees": "担当者",
+ "assignee": "担当者",
+ "created_by": "作成者",
+ "none": "なし",
+ "link": "リンク",
+ "estimate": "見積もり",
+ "layout": "レイアウト",
+ "filters": "フィルター",
+ "display": "表示",
+ "load_more": "もっと読み込む",
+ "activity": "アクティビティ",
+ "analytics": "アナリティクス",
+ "dates": "日付",
+ "success": "成功!",
+ "something_went_wrong": "問題が発生しました",
+ "error": {
+ "label": "エラー!",
+ "message": "エラーが発生しました。もう一度お試しください。"
+ },
+ "group_by": "グループ化",
+ "epic": "エピック",
+ "epics": "エピック",
+ "work_item": "作業項目",
+ "work_items": "作業項目",
+ "sub_work_item": "サブ作業項目",
+ "add": "追加",
+ "warning": "警告",
+ "updating": "更新中",
+ "adding": "追加中",
+ "update": "更新",
+ "creating": "作成中",
+ "create": "作成",
+ "cancel": "キャンセル",
+ "description": "説明",
+ "title": "タイトル",
+ "attachment": "添付ファイル",
+ "general": "一般",
+ "features": "機能",
+ "automation": "自動化",
+ "project_name": "プロジェクト名",
+ "project_id": "プロジェクトID",
+ "project_timezone": "プロジェクトのタイムゾーン",
+ "created_on": "作成日",
+ "update_project": "プロジェクトを更新",
+ "identifier_already_exists": "識別子は既に存在します",
+ "add_more": "さらに追加",
+ "defaults": "デフォルト",
+ "add_label": "ラベルを追加",
+ "estimates": "見積もり",
+ "customize_time_range": "期間をカスタマイズ",
+ "loading": "読み込み中",
+ "attachments": "添付ファイル",
+ "property": "プロパティ",
+ "properties": "プロパティ",
+ "parent": "親",
+ "remove": "削除",
+ "archiving": "アーカイブ中",
+ "archive": "アーカイブ",
+ "access": {
+ "public": "公開",
+ "private": "非公開"
+ },
+ "done": "完了",
+ "sub_work_items": "サブ作業項目",
+ "comment": "コメント",
+ "workspace_level": "ワークスペースレベル",
+ "order_by": {
+ "label": "並び順",
+ "manual": "手動",
+ "last_created": "最終作成日",
+ "last_updated": "最終更新日",
+ "start_date": "開始日",
+ "due_date": "期限日",
+ "asc": "昇順",
+ "desc": "降順",
+ "updated_on": "更新日"
+ },
+ "sort": {
+ "asc": "昇順",
+ "desc": "降順",
+ "created_on": "作成日",
+ "updated_on": "更新日"
+ },
+ "comments": "コメント",
+ "updates": "更新",
+ "clear_all": "すべてクリア",
+ "copied": "コピーしました!",
+ "link_copied": "リンクをコピーしました!",
+ "link_copied_to_clipboard": "リンクをクリップボードにコピーしました",
+ "copied_to_clipboard": "作業項目のリンクをクリップボードにコピーしました",
+ "is_copied_to_clipboard": "作業項目をクリップボードにコピーしました",
+ "no_links_added_yet": "リンクはまだ追加されていません",
+ "add_link": "リンクを追加",
+ "links": "リンク",
+ "go_to_workspace": "ワークスペースへ移動",
+ "progress": "進捗",
+ "optional": "任意",
+ "join": "参加",
+ "go_back": "戻る",
+ "continue": "続ける",
+ "resend": "再送信",
+ "relations": "関連",
+ "errors": {
+ "default": {
+ "title": "エラー!",
+ "message": "問題が発生しました。もう一度お試しください。"
+ },
+ "required": "この項目は必須です",
+ "entity_required": "{entity}は必須です"
+ },
+ "update_link": "リンクを更新",
+ "attach": "添付",
+ "create_new": "新規作成",
+ "add_existing": "既存を追加",
+ "type_or_paste_a_url": "URLを入力または貼り付け",
+ "url_is_invalid": "URLが無効です",
+ "display_title": "表示タイトル",
+ "link_title_placeholder": "このリンクをどのように表示したいか",
+ "url": "URL",
+ "side_peek": "サイドピーク",
+ "modal": "モーダル",
+ "full_screen": "全画面",
+ "close_peek_view": "ピークビューを閉じる",
+ "toggle_peek_view_layout": "ピークビューのレイアウトを切り替え",
+ "options": "オプション",
+ "duration": "期間",
+ "today": "今日",
+ "week": "週",
+ "month": "月",
+ "quarter": "四半期",
+ "press_for_commands": "コマンドは「/」を押してください",
+ "click_to_add_description": "クリックして説明を追加",
+ "search": {
+ "label": "検索",
+ "placeholder": "検索するキーワードを入力",
+ "no_matches_found": "一致する結果が見つかりません",
+ "no_matching_results": "一致する結果がありません"
+ },
+ "actions": {
+ "edit": "編集",
+ "make_a_copy": "コピーを作成",
+ "open_in_new_tab": "新しいタブで開く",
+ "copy_link": "リンクをコピー",
+ "archive": "アーカイブ",
+ "delete": "削除",
+ "remove_relation": "関連を削除",
+ "subscribe": "購読",
+ "unsubscribe": "購読解除",
+ "clear_sorting": "並び替えをクリア",
+ "show_weekends": "週末を表示",
+ "enable": "有効化",
+ "disable": "無効化"
+ },
+ "name": "名前",
+ "discard": "破棄",
+ "confirm": "確認",
+ "confirming": "確認中",
+ "read_the_docs": "ドキュメントを読む",
+ "default": "デフォルト",
+ "active": "アクティブ",
+ "enabled": "有効",
+ "disabled": "無効",
+ "mandate": "必須",
+ "mandatory": "必須",
+ "yes": "はい",
+ "no": "いいえ",
+ "please_wait": "お待ちください",
+ "enabling": "有効化中",
+ "disabling": "無効化中",
+ "beta": "ベータ",
+ "or": "または",
+ "next": "次へ",
+ "back": "戻る",
+ "cancelling": "キャンセル中",
+ "configuring": "設定中",
+ "clear": "クリア",
+ "import": "インポート",
+ "connect": "接続",
+ "authorizing": "認証中",
+ "processing": "処理中",
+ "no_data_available": "データがありません",
+ "from": "{name}から",
+ "authenticated": "認証済み",
+ "select": "選択",
+ "upgrade": "アップグレード",
+ "add_seats": "シートを追加",
+ "label": "ラベル",
+ "priorities": "優先度",
+ "projects": "プロジェクト",
+ "workspace": "ワークスペース",
+ "workspaces": "ワークスペース",
+ "team": "チーム",
+ "teams": "チーム",
+ "entity": "エンティティ",
+ "entities": "エンティティ",
+ "task": "タスク",
+ "tasks": "タスク",
+ "section": "セクション",
+ "sections": "セクション",
+ "edit": "編集",
+ "connecting": "接続中",
+ "connected": "接続済み",
+ "disconnect": "切断",
+ "disconnecting": "切断中",
+ "installing": "インストール中",
+ "install": "インストール",
+ "reset": "リセット",
+ "live": "ライブ",
+ "change_history": "変更履歴",
+ "coming_soon": "近日公開",
+ "members": "メンバー",
+ "you": "あなた",
+ "upgrade_cta": {
+ "higher_subscription": "高いサブスクリプションにアップグレード",
+ "talk_to_sales": "セールスに連絡"
+ },
+ "category": "カテゴリー",
+ "categories": "カテゴリーズ",
+ "saving": "セービング",
+ "save_changes": "セーブ チェンジズ",
+ "delete": "デリート",
+ "deleting": "デリーティング",
+ "pending": "保留中",
+ "invite": "招待"
+ },
+
+ "chart": {
+ "x_axis": "エックス アクシス",
+ "y_axis": "ワイ アクシス",
+ "metric": "メトリック"
+ },
+
+ "form": {
+ "title": {
+ "required": "タイトルは必須です",
+ "max_length": "タイトルは{length}文字未満である必要があります"
+ }
+ },
+
+ "entity": {
+ "grouping_title": "{entity}のグループ化",
+ "priority": "{entity}の優先度",
+ "all": "すべての{entity}",
+ "drop_here_to_move": "ここにドロップして{entity}を移動",
+ "delete": {
+ "label": "{entity}を削除",
+ "success": "{entity}を削除しました",
+ "failed": "{entity}の削除に失敗しました"
+ },
+ "update": {
+ "failed": "{entity}の更新に失敗しました",
+ "success": "{entity}を更新しました"
+ },
+ "link_copied_to_clipboard": "{entity}のリンクをクリップボードにコピーしました",
+ "fetch": {
+ "failed": "{entity}の取得中にエラーが発生しました"
+ },
+ "add": {
+ "success": "{entity}を追加しました",
+ "failed": "{entity}の追加中にエラーが発生しました"
+ }
+ },
+
+ "epic": {
+ "all": "すべてのエピック",
+ "label": "{count, plural, one {エピック} other {エピック}}",
+ "new": "新規エピック",
+ "adding": "エピックを追加中",
+ "create": {
+ "success": "エピックを作成しました"
+ },
+ "add": {
+ "press_enter": "Enterを押して別のエピックを追加",
+ "label": "エピックを追加"
+ },
+ "title": {
+ "label": "エピックのタイトル",
+ "required": "エピックのタイトルは必須です。"
+ }
+ },
+
+ "issue": {
+ "label": "{count, plural, one {作業項目} other {作業項目}}",
+ "all": "すべての作業項目",
+ "edit": "作業項目を編集",
+ "title": {
+ "label": "作業項目のタイトル",
+ "required": "作業項目のタイトルは必須です。"
+ },
+ "add": {
+ "press_enter": "Enterを押して別の作業項目を追加",
+ "label": "作業項目を追加",
+ "cycle": {
+ "failed": "作業項目をサイクルに追加できませんでした。もう一度お試しください。",
+ "success": "{count, plural, one {作業項目} other {作業項目}}をサイクルに追加しました。",
+ "loading": "{count, plural, one {作業項目} other {作業項目}}をサイクルに追加中"
+ },
+ "assignee": "担当者を追加",
+ "start_date": "開始日を追加",
+ "due_date": "期限日を追加",
+ "parent": "親作業項目を追加",
+ "sub_issue": "サブ作業項目を追加",
+ "relation": "関連を追加",
+ "link": "リンクを追加",
+ "existing": "既存の作業項目を追加"
+ },
+ "remove": {
+ "label": "作業項目を削除",
+ "cycle": {
+ "loading": "サイクルから作業項目を削除中",
+ "success": "作業項目をサイクルから削除しました。",
+ "failed": "作業項目をサイクルから削除できませんでした。もう一度お試しください。"
+ },
+ "module": {
+ "loading": "モジュールから作業項目を削除中",
+ "success": "作業項目をモジュールから削除しました。",
+ "failed": "作業項目をモジュールから削除できませんでした。もう一度お試しください。"
+ },
+ "parent": {
+ "label": "親作業項目を削除"
+ }
+ },
+ "new": "新規作業項目",
+ "adding": "作業項目を追加中",
+ "create": {
+ "success": "作業項目を作成しました"
+ },
+ "priority": {
+ "urgent": "緊急",
+ "high": "高",
+ "medium": "中",
+ "low": "低"
+ },
+ "display": {
+ "properties": {
+ "label": "表示プロパティ",
+ "id": "ID",
+ "issue_type": "作業項目タイプ",
+ "sub_issue_count": "サブ作業項目数",
+ "attachment_count": "添付ファイル数",
+ "created_on": "作成日",
+ "sub_issue": "サブ作業項目"
+ },
+ "extra": {
+ "show_sub_issues": "サブ作業項目を表示",
+ "show_empty_groups": "空のグループを表示"
+ }
+ },
+ "layouts": {
+ "ordered_by_label": "このレイアウトは次の順序で並べ替えられています:",
+ "list": "リスト",
+ "kanban": "ボード",
+ "calendar": "カレンダー",
+ "spreadsheet": "テーブル",
+ "gantt": "タイムライン",
+ "title": {
+ "list": "リストレイアウト",
+ "kanban": "ボードレイアウト",
+ "calendar": "カレンダーレイアウト",
+ "spreadsheet": "テーブルレイアウト",
+ "gantt": "タイムラインレイアウト"
+ }
+ },
+ "states": {
+ "active": "アクティブ",
+ "backlog": "バックログ"
+ },
+ "comments": {
+ "placeholder": "コメントを追加",
+ "switch": {
+ "private": "プライベートコメントに切り替え",
+ "public": "公開コメントに切り替え"
+ },
+ "create": {
+ "success": "コメントを作成しました",
+ "error": "コメントの作成に失敗しました。後でもう一度お試しください。"
+ },
+ "update": {
+ "success": "コメントを更新しました",
+ "error": "コメントの更新に失敗しました。後でもう一度お試しください。"
+ },
+ "remove": {
+ "success": "コメントを削除しました",
+ "error": "コメントの削除に失敗しました。後でもう一度お試しください。"
+ },
+ "upload": {
+ "error": "アセットのアップロードに失敗しました。後でもう一度お試しください。"
+ }
+ },
+ "empty_state": {
+ "issue_detail": {
+ "title": "作業項目が存在しません",
+ "description": "お探しの作業項目は存在しないか、アーカイブされているか、削除されています。",
+ "primary_button": {
+ "text": "他の作業項目を表示"
+ }
+ }
+ },
+ "sibling": {
+ "label": "兄弟作業項目"
+ },
+ "archive": {
+ "description": "完了またはキャンセルされた\n作業項目のみアーカイブできます",
+ "label": "作業項目をアーカイブ",
+ "confirm_message": "作業項目をアーカイブしてもよろしいですか?アーカイブされた作業項目は後で復元できます。",
+ "success": {
+ "label": "アーカイブ成功",
+ "message": "アーカイブはプロジェクトのアーカイブで確認できます。"
+ },
+ "failed": {
+ "message": "作業項目をアーカイブできませんでした。もう一度お試しください。"
+ }
+ },
+ "restore": {
+ "success": {
+ "title": "復元成功",
+ "message": "作業項目はプロジェクトの作業項目で確認できます。"
+ },
+ "failed": {
+ "message": "作業項目を復元できませんでした。もう一度お試しください。"
+ }
+ },
+ "relation": {
+ "relates_to": "関連する",
+ "duplicate": "重複する",
+ "blocked_by": "ブロックされている",
+ "blocking": "ブロックしている"
+ },
+ "copy_link": "作業項目のリンクをコピー",
+ "delete": {
+ "label": "作業項目を削除",
+ "error": "作業項目の削除中にエラーが発生しました"
+ },
+ "subscription": {
+ "actions": {
+ "subscribed": "作業項目を購読しました",
+ "unsubscribed": "作業項目の購読を解除しました"
+ }
+ },
+ "select": {
+ "error": "少なくとも1つの作業項目を選択してください",
+ "empty": "作業項目が選択されていません",
+ "add_selected": "選択した作業項目を追加"
+ },
+ "open_in_full_screen": "作業項目をフルスクリーンで開く"
+ },
+
+ "attachment": {
+ "error": "ファイルを添付できませんでした。もう一度アップロードしてください。",
+ "only_one_file_allowed": "一度にアップロードできるファイルは1つだけです。",
+ "file_size_limit": "ファイルサイズは{size}MB以下である必要があります。",
+ "drag_and_drop": "どこにでもドラッグ&ドロップでアップロード",
+ "delete": "添付ファイルを削除"
+ },
+
+ "label": {
+ "select": "ラベルを選択",
+ "create": {
+ "success": "ラベルを作成しました",
+ "failed": "ラベルの作成に失敗しました",
+ "already_exists": "ラベルは既に存在します",
+ "type": "新しいラベルを追加するには入力してください"
+ }
+ },
+
+ "sub_work_item": {
+ "update": {
+ "success": "サブ作業項目を更新しました",
+ "error": "サブ作業項目の更新中にエラーが発生しました"
+ },
+ "remove": {
+ "success": "サブ作業項目を削除しました",
+ "error": "サブ作業項目の削除中にエラーが発生しました"
+ }
+ },
+
+ "view": {
+ "label": "{count, plural, one {ビュー} other {ビュー}}",
+ "create": {
+ "label": "ビューを作成"
+ },
+ "update": {
+ "label": "ビューを更新"
+ }
+ },
+
+ "inbox_issue": {
+ "status": {
+ "pending": {
+ "title": "保留中",
+ "description": "保留中"
+ },
+ "declined": {
+ "title": "却下",
+ "description": "却下"
+ },
+ "snoozed": {
+ "title": "スヌーズ",
+ "description": "残り{days, plural, one{# 日} other{# 日}}"
+ },
+ "accepted": {
+ "title": "承認済み",
+ "description": "承認済み"
+ },
+ "duplicate": {
+ "title": "重複",
+ "description": "重複"
+ }
+ },
+ "modals": {
+ "decline": {
+ "title": "作業項目を却下",
+ "content": "作業項目{value}を却下してもよろしいですか?"
+ },
+ "delete": {
+ "title": "作業項目を削除",
+ "content": "作業項目{value}を削除してもよろしいですか?",
+ "success": "作業項目を削除しました"
+ }
+ },
+ "errors": {
+ "snooze_permission": "プロジェクト管理者のみが作業項目をスヌーズ/スヌーズ解除できます",
+ "accept_permission": "プロジェクト管理者のみが作業項目を承認できます",
+ "decline_permission": "プロジェクト管理者のみが作業項目を却下できます"
+ },
+ "actions": {
+ "accept": "承認",
+ "decline": "却下",
+ "snooze": "スヌーズ",
+ "unsnooze": "スヌーズ解除",
+ "copy": "作業項目のリンクをコピー",
+ "delete": "削除",
+ "open": "作業項目を開く",
+ "mark_as_duplicate": "重複としてマーク",
+ "move": "{value}をプロジェクトの作業項目に移動"
+ },
+ "source": {
+ "in-app": "アプリ内"
+ },
+ "order_by": {
+ "created_at": "作成日",
+ "updated_at": "更新日",
+ "id": "ID"
+ },
+ "label": "インテーク",
+ "page_label": "{workspace} - インテーク",
+ "modal": {
+ "title": "インテーク作業項目を作成"
+ },
+ "tabs": {
+ "open": "オープン",
+ "closed": "クローズ"
+ },
+ "empty_state": {
+ "sidebar_open_tab": {
+ "title": "オープンな作業項目がありません",
+ "description": "オープンな作業項目はここで見つかります。新しい作業項目を作成してください。"
+ },
+ "sidebar_closed_tab": {
+ "title": "クローズされた作業項目がありません",
+ "description": "承認または却下されたすべての作業項目はここで見つかります。"
+ },
+ "sidebar_filter": {
+ "title": "一致する作業項目がありません",
+ "description": "インテークに適用されたフィルターに一致する作業項目がありません。新しい作業項目を作成してください。"
+ },
+ "detail": {
+ "title": "詳細を表示する作業項目を選択してください。"
+ }
+ }
+ },
+
+ "workspace_creation": {
+ "heading": "ワークスペースを作成",
+ "subheading": "Planeを使用するには、ワークスペースを作成するか参加する必要があります。",
+ "form": {
+ "name": {
+ "label": "ワークスペース名を設定",
+ "placeholder": "馴染みがあり認識しやすい名前が最適です。"
+ },
+ "url": {
+ "label": "ワークスペースのURLを設定",
+ "placeholder": "URLを入力または貼り付け",
+ "edit_slug": "URLのスラッグのみ編集可能です"
+ },
+ "organization_size": {
+ "label": "このワークスペースを何人で使用しますか?",
+ "placeholder": "範囲を選択"
+ }
+ },
+ "errors": {
+ "creation_disabled": {
+ "title": "インスタンス管理者のみがワークスペースを作成できます",
+ "description": "インスタンス管理者のメールアドレスをご存知の場合は、下のボタンをクリックして連絡を取ってください。",
+ "request_button": "インスタンス管理者にリクエスト"
+ },
+ "validation": {
+ "name_alphanumeric": "ワークスペース名には (' '), ('-'), ('_') と英数字のみ使用できます。",
+ "name_length": "名前は80文字以内にしてください。",
+ "url_alphanumeric": "URLには ('-') と英数字のみ使用できます。",
+ "url_length": "URLは48文字以内にしてください。",
+ "url_already_taken": "ワークスペースのURLは既に使用されています!"
+ }
+ },
+ "request_email": {
+ "subject": "新規ワークスペースのリクエスト",
+ "body": "インスタンス管理者様\n\n[ワークスペース作成の目的]のために、URL [/workspace-name] の新規ワークスペースを作成していただけますでしょうか。\n\nよろしくお願いいたします。\n{firstName} {lastName}\n{email}"
+ },
+ "button": {
+ "default": "ワークスペースを作成",
+ "loading": "ワークスペースを作成中"
+ },
+ "toast": {
+ "success": {
+ "title": "成功",
+ "message": "ワークスペースが正常に作成されました"
+ },
+ "error": {
+ "title": "エラー",
+ "message": "ワークスペースを作成できませんでした。もう一度お試しください。"
+ }
+ }
+ },
+
+ "workspace_dashboard": {
+ "empty_state": {
+ "general": {
+ "title": "プロジェクト、アクティビティ、メトリクスの概要",
+ "description": "Planeへようこそ。ご利用いただき嬉しく思います。最初のプロジェクトを作成して作業項目を追跡すると、このページは進捗を把握するのに役立つスペースに変わります。管理者はチームの進捗に役立つ項目も表示されます。",
+ "primary_button": {
+ "text": "最初のプロジェクトを作成",
+ "comic": {
+ "title": "Planeではすべてがプロジェクトから始まります",
+ "description": "プロジェクトは製品のロードマップ、マーケティングキャンペーン、新車の発売などになります。"
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_analytics": {
+ "label": "アナリティクス",
+ "page_label": "{workspace} - アナリティクス",
+ "open_tasks": "オープンタスクの合計",
+ "error": "データの取得中にエラーが発生しました。",
+ "work_items_closed_in": "クローズされた作業項目",
+ "selected_projects": "選択されたプロジェクト",
+ "total_members": "メンバー総数",
+ "total_cycles": "サイクル総数",
+ "total_modules": "モジュール総数",
+ "pending_work_items": {
+ "title": "保留中の作業項目",
+ "empty_state": "同僚による保留中の作業項目の分析がここに表示されます。"
+ },
+ "work_items_closed_in_a_year": {
+ "title": "1年間でクローズされた作業項目",
+ "empty_state": "作業項目をクローズすると、グラフ形式で分析が表示されます。"
+ },
+ "most_work_items_created": {
+ "title": "作成された作業項目が最も多い",
+ "empty_state": "同僚と作成した作業項目の数がここに表示されます。"
+ },
+ "most_work_items_closed": {
+ "title": "クローズされた作業項目が最も多い",
+ "empty_state": "同僚とクローズした作業項目の数がここに表示されます。"
+ },
+ "tabs": {
+ "scope_and_demand": "スコープと需要",
+ "custom": "カスタムアナリティクス"
+ },
+ "empty_state": {
+ "general": {
+ "title": "進捗、ワークロード、割り当てを追跡。傾向を把握し、ブロッカーを解消して、作業をより速く進めましょう",
+ "description": "スコープと需要、見積もり、スコープクリープを確認できます。チームメンバーとチームのパフォーマンスを把握し、プロジェクトが予定通りに進むようにします。",
+ "primary_button": {
+ "text": "最初のプロジェクトを開始",
+ "comic": {
+ "title": "アナリティクスはサイクル + モジュールで最も効果を発揮",
+ "description": "まず、作業項目をサイクルでタイムボックス化し、可能であれば、複数のサイクルにまたがる作業項目をモジュールにグループ化します。左のナビゲーションで両方を確認してください。"
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_projects": {
+ "label": "{count, plural, one {プロジェクト} other {プロジェクト}}",
+ "create": {
+ "label": "プロジェクトを追加"
+ },
+ "network": {
+ "private": {
+ "title": "非公開",
+ "description": "招待された人のみアクセス可能"
+ },
+ "public": {
+ "title": "公開",
+ "description": "ゲスト以外のワークスペースの全員が参加可能"
+ }
+ },
+ "error": {
+ "permission": "この操作を実行する権限がありません。",
+ "cycle_delete": "サイクルの削除に失敗しました",
+ "module_delete": "モジュールの削除に失敗しました",
+ "issue_delete": "作業項目の削除に失敗しました"
+ },
+ "state": {
+ "backlog": "バックログ",
+ "unstarted": "未開始",
+ "started": "開始済み",
+ "completed": "完了",
+ "cancelled": "キャンセル"
+ },
+ "sort": {
+ "manual": "手動",
+ "name": "名前",
+ "created_at": "作成日",
+ "members_length": "メンバー数"
+ },
+ "scope": {
+ "my_projects": "自分のプロジェクト",
+ "archived_projects": "アーカイブ済み"
+ },
+ "common": {
+ "months_count": "{months, plural, one{# ヶ月} other{# ヶ月}}"
+ },
+ "empty_state": {
+ "general": {
+ "title": "アクティブなプロジェクトがありません",
+ "description": "各プロジェクトは目標指向の作業の親として考えてください。プロジェクトには作業、サイクル、モジュールが含まれ、同僚と共にその目標の達成を支援します。新しいプロジェクトを作成するか、アーカイブされたプロジェクトをフィルタリングしてください。",
+ "primary_button": {
+ "text": "最初のプロジェクトを開始",
+ "comic": {
+ "title": "Planeではすべてがプロジェクトから始まります",
+ "description": "プロジェクトは製品のロードマップ、マーケティングキャンペーン、新車の発売などになります。"
+ }
+ }
+ },
+ "no_projects": {
+ "title": "プロジェクトがありません",
+ "description": "作業項目を作成したり作業を管理したりするには、プロジェクトを作成するか、プロジェクトのメンバーになる必要があります。",
+ "primary_button": {
+ "text": "最初のプロジェクトを開始",
+ "comic": {
+ "title": "Planeではすべてがプロジェクトから始まります",
+ "description": "プロジェクトは製品のロードマップ、マーケティングキャンペーン、新車の発売などになります。"
+ }
+ }
+ },
+ "filter": {
+ "title": "一致するプロジェクトがありません",
+ "description": "条件に一致するプロジェクトが見つかりません。\n代わりに新しいプロジェクトを作成してください。"
+ },
+ "search": {
+ "description": "条件に一致するプロジェクトが見つかりません。\n代わりに新しいプロジェクトを作成してください。"
+ }
+ }
+ },
+
+ "workspace_views": {
+ "add_view": "ビューを追加",
+ "empty_state": {
+ "all-issues": {
+ "title": "プロジェクトに作業項目がありません",
+ "description": "最初のプロジェクトが完了しました!次は、作業を追跡可能な作業項目に分割しましょう。始めましょう!",
+ "primary_button": {
+ "text": "新しい作業項目を作成"
+ }
+ },
+ "assigned": {
+ "title": "作業項目がまだありません",
+ "description": "あなたに割り当てられた作業項目をここで追跡できます。",
+ "primary_button": {
+ "text": "新しい作業項目を作成"
+ }
+ },
+ "created": {
+ "title": "作業項目がまだありません",
+ "description": "あなたが作成したすべての作業項目がここに表示され、直接追跡できます。",
+ "primary_button": {
+ "text": "新しい作業項目を作成"
+ }
+ },
+ "subscribed": {
+ "title": "作業項目がまだありません",
+ "description": "興味のある作業項目を購読して、ここですべてを追跡できます。"
+ },
+ "custom-view": {
+ "title": "作業項目がまだありません",
+ "description": "フィルターに該当する作業項目をここで追跡できます。"
+ }
+ }
+ },
+
+ "workspace_settings": {
+ "label": "ワークスペース設定",
+ "page_label": "{workspace} - 一般設定",
+ "key_created": "キーが作成されました",
+ "copy_key": "このシークレットキーをコピーしてPlaneページに保存してください。閉じた後はこのキーを見ることができません。キーを含むCSVファイルがダウンロードされました。",
+ "token_copied": "トークンがクリップボードにコピーされました。",
+ "settings": {
+ "general": {
+ "title": "一般",
+ "upload_logo": "ロゴをアップロード",
+ "edit_logo": "ロゴを編集",
+ "name": "ワークスペース名",
+ "company_size": "会社の規模",
+ "url": "ワークスペースURL",
+ "update_workspace": "ワークスペースを更新",
+ "delete_workspace": "このワークスペースを削除",
+ "delete_workspace_description": "ワークスペースを削除すると、そのワークスペース内のすべてのデータとリソースが完全に削除され、復元することはできません。",
+ "delete_btn": "このワークスペースを削除",
+ "delete_modal": {
+ "title": "このワークスペースを削除してもよろしいですか?",
+ "description": "有料プランの無料トライアルが有効です。続行するには、まずトライアルをキャンセルしてください。",
+ "dismiss": "閉じる",
+ "cancel": "トライアルをキャンセル",
+ "success_title": "ワークスペースが削除されました。",
+ "success_message": "まもなくプロフィールページに移動します。",
+ "error_title": "操作に失敗しました。",
+ "error_message": "もう一度お試しください。"
+ },
+ "errors": {
+ "name": {
+ "required": "名前は必須です",
+ "max_length": "ワークスペース名は80文字を超えることはできません"
+ },
+ "company_size": {
+ "required": "会社の規模は必須です"
+ }
+ }
+ },
+ "members": {
+ "title": "メンバー",
+ "add_member": "メンバーを追加",
+ "pending_invites": "保留中の招待",
+ "invitations_sent_successfully": "招待が正常に送信されました",
+ "leave_confirmation": "ワークスペースから退出してもよろしいですか?このワークスペースにアクセスできなくなります。この操作は取り消せません。",
+ "details": {
+ "full_name": "フルネーム",
+ "display_name": "表示名",
+ "email_address": "メールアドレス",
+ "account_type": "アカウントタイプ",
+ "authentication": "認証",
+ "joining_date": "参加日"
+ },
+ "modal": {
+ "title": "共同作業者を招待",
+ "description": "ワークスペースに共同作業者を招待します。",
+ "button": "招待を送信",
+ "button_loading": "招待を送信中",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "招待するにはメールアドレスが必要です。",
+ "invalid": "メールアドレスが無効です"
+ }
+ }
+ },
+ "billing_and_plans": {
+ "title": "請求とプラン",
+ "current_plan": "現在のプラン",
+ "free_plan": "現在フリープランを使用中です",
+ "view_plans": "プランを表示"
+ },
+ "exports": {
+ "title": "エクスポート",
+ "exporting": "エクスポート中",
+ "previous_exports": "過去のエクスポート",
+ "export_separate_files": "データを個別のファイルにエクスポート",
+ "modal": {
+ "title": "エクスポート先",
+ "toasts": {
+ "success": {
+ "title": "エクスポート成功",
+ "message": "エクスポートした{entity}は過去のエクスポートからダウンロードできます。"
+ },
+ "error": {
+ "title": "エクスポート失敗",
+ "message": "エクスポートに失敗しました。もう一度お試しください。"
+ }
+ }
+ }
+ },
+ "webhooks": {
+ "title": "Webhook",
+ "add_webhook": "Webhookを追加",
+ "modal": {
+ "title": "Webhookを作成",
+ "details": "Webhook詳細",
+ "payload": "ペイロードURL",
+ "question": "このWebhookをトリガーするイベントを選択してください",
+ "error": "URLは必須です"
+ },
+ "secret_key": {
+ "title": "シークレットキー",
+ "message": "Webhookペイロードにサインインするためのトークンを生成"
+ },
+ "options": {
+ "all": "すべてを送信",
+ "individual": "個別のイベントを選択"
+ },
+ "toasts": {
+ "created": {
+ "title": "Webhook作成完了",
+ "message": "Webhookが正常に作成されました"
+ },
+ "not_created": {
+ "title": "Webhook作成失敗",
+ "message": "Webhookを作成できませんでした"
+ },
+ "updated": {
+ "title": "Webhook更新完了",
+ "message": "Webhookが正常に更新されました"
+ },
+ "not_updated": {
+ "title": "Webhook更新失敗",
+ "message": "Webhookを更新できませんでした"
+ },
+ "removed": {
+ "title": "Webhook削除完了",
+ "message": "Webhookが正常に削除されました"
+ },
+ "not_removed": {
+ "title": "Webhook削除失敗",
+ "message": "Webhookを削除できませんでした"
+ },
+ "secret_key_copied": {
+ "message": "シークレットキーがクリップボードにコピーされました。"
+ },
+ "secret_key_not_copied": {
+ "message": "シークレットキーのコピー中にエラーが発生しました。"
+ }
+ }
+ },
+ "api_tokens": {
+ "title": "APIトークン",
+ "add_token": "APIトークンを追加",
+ "create_token": "トークンを作成",
+ "never_expires": "無期限",
+ "generate_token": "トークンを生成",
+ "generating": "生成中",
+ "delete": {
+ "title": "APIトークンを削除",
+ "description": "このトークンを使用しているアプリケーションはPlaneのデータにアクセスできなくなります。この操作は取り消せません。",
+ "success": {
+ "title": "成功!",
+ "message": "APIトークンが正常に削除されました"
+ },
+ "error": {
+ "title": "エラー!",
+ "message": "APIトークンを削除できませんでした"
+ }
+ }
+ }
+ },
+ "empty_state": {
+ "api_tokens": {
+ "title": "APIトークンがまだ作成されていません",
+ "description": "PlaneのAPIを使用して、Planeのデータを外部システムと統合できます。トークンを作成して始めましょう。"
+ },
+ "webhooks": {
+ "title": "Webhookが追加されていません",
+ "description": "Webhookを作成してリアルタイムの更新を受け取り、アクションを自動化します。"
+ },
+ "exports": {
+ "title": "エクスポートがまだありません",
+ "description": "エクスポートすると、参照用のコピーがここに保存されます。"
+ },
+ "imports": {
+ "title": "インポートがまだありません",
+ "description": "過去のインポートをここで確認し、ダウンロードできます。"
+ }
+ }
+ },
+
+ "profile": {
+ "label": "プロフィール",
+ "page_label": "あなたの作業",
+ "work": "作業",
+ "details": {
+ "joined_on": "参加日",
+ "time_zone": "タイムゾーン"
+ },
+ "stats": {
+ "workload": "作業負荷",
+ "overview": "概要",
+ "created": "作成した作業項目",
+ "assigned": "割り当てられた作業項目",
+ "subscribed": "購読中の作業項目",
+ "state_distribution": {
+ "title": "状態別作業項目",
+ "empty": "より良い分析のために、作業項目を作成してグラフで状態別に表示します。"
+ },
+ "priority_distribution": {
+ "title": "優先度別作業項目",
+ "empty": "より良い分析のために、作業項目を作成してグラフで優先度別に表示します。"
+ },
+ "recent_activity": {
+ "title": "最近のアクティビティ",
+ "empty": "データが見つかりませんでした。入力内容を確認してください",
+ "button": "今日のアクティビティをダウンロード",
+ "button_loading": "ダウンロード中"
+ }
+ },
+ "actions": {
+ "profile": "プロフィール",
+ "security": "セキュリティ",
+ "activity": "アクティビティ",
+ "appearance": "外観",
+ "notifications": "通知"
+ },
+ "tabs": {
+ "summary": "サマリー",
+ "assigned": "割り当て済み",
+ "created": "作成済み",
+ "subscribed": "購読中",
+ "activity": "アクティビティ"
+ },
+ "empty_state": {
+ "activity": {
+ "title": "アクティビティがまだありません",
+ "description": "新しい作業項目を作成して始めましょう!詳細とプロパティを追加してください。Planeをさらに探索してアクティビティを確認しましょう。"
+ },
+ "assigned": {
+ "title": "割り当てられた作業項目がありません",
+ "description": "あなたに割り当てられた作業項目をここで追跡できます。"
+ },
+ "created": {
+ "title": "作業項目がまだありません",
+ "description": "あなたが作成したすべての作業項目がここに表示され、直接追跡できます。"
+ },
+ "subscribed": {
+ "title": "作業項目がまだありません",
+ "description": "興味のある作業項目を購読して、ここですべてを追跡できます。"
+ }
+ }
+ },
+
+ "project_settings": {
+ "general": {
+ "enter_project_id": "プロジェクトIDを入力",
+ "please_select_a_timezone": "タイムゾーンを選択してください",
+ "archive_project": {
+ "title": "プロジェクトをアーカイブ",
+ "description": "プロジェクトをアーカイブすると、サイドナビゲーションから非表示になりますが、プロジェクトページからアクセスすることはできます。プロジェクトを復元または削除することもできます。",
+ "button": "プロジェクトをアーカイブ"
+ },
+ "delete_project": {
+ "title": "プロジェクトを削除",
+ "description": "プロジェクトを削除すると、そのプロジェクト内のすべてのデータとリソースが永久に削除され、復元できなくなります。",
+ "button": "プロジェクトを削除"
+ },
+ "toast": {
+ "success": "プロジェクトが正常に更新されました",
+ "error": "プロジェクトを更新できませんでした。もう一度お試しください。"
+ }
+ },
+ "members": {
+ "label": "メンバー",
+ "project_lead": "プロジェクトリーダー",
+ "default_assignee": "デフォルトの担当者",
+ "guest_super_permissions": {
+ "title": "ゲストユーザーにすべての作業項目の閲覧権限を付与:",
+ "sub_heading": "これにより、ゲストはプロジェクトのすべての作業項目を閲覧できるようになります。"
+ },
+ "invite_members": {
+ "title": "メンバーを招待",
+ "sub_heading": "プロジェクトに参加するメンバーを招待します。",
+ "select_co_worker": "共同作業者を選択"
+ }
+ },
+ "states": {
+ "describe_this_state_for_your_members": "このステータスについてメンバーに説明してください。",
+ "empty_state": {
+ "title": "{groupKey}グループのステータスがありません",
+ "description": "新しいステータスを作成してください"
+ }
+ },
+ "labels": {
+ "label_title": "ラベルタイトル",
+ "label_title_is_required": "ラベルタイトルは必須です",
+ "label_max_char": "ラベル名は255文字を超えることはできません",
+ "toast": {
+ "error": "ラベルの更新中にエラーが発生しました"
+ }
+ },
+ "estimates": {
+ "title": "プロジェクトの見積もりを有効にする",
+ "description": "チームの複雑さと作業負荷を伝えるのに役立ちます。"
+ },
+ "automations": {
+ "label": "自動化",
+ "auto-archive": {
+ "title": "完了した作業項目を自動アーカイブ",
+ "description": "Planeは完了またはキャンセルされた作業項目を自動的にアーカイブします。",
+ "duration": "次の期間完了している作業項目を自動アーカイブ"
+ },
+ "auto-close": {
+ "title": "作業項目を自動クローズ",
+ "description": "Planeは完了またはキャンセルされていない作業項目を自動的にクローズします。",
+ "duration": "次の期間非アクティブな作業項目を自動クローズ",
+ "auto_close_status": "自動クローズステータス"
+ }
+ },
+
+ "empty_state": {
+ "labels": {
+ "title": "ラベルがまだありません",
+ "description": "プロジェクトの作業項目を整理してフィルタリングするためのラベルを作成します。"
+ },
+ "estimates": {
+ "title": "見積もりシステムがまだありません",
+ "description": "作業項目ごとの作業量を伝えるための見積もりセットを作成します。",
+ "primary_button": "見積もりシステムを追加"
+ }
+ }
+ },
+
+ "project_cycles": {
+ "add_cycle": "サイクルを追加",
+ "more_details": "詳細情報",
+ "cycle": "サイクル",
+ "update_cycle": "サイクルを更新",
+ "create_cycle": "サイクルを作成",
+ "no_matching_cycles": "一致するサイクルがありません",
+ "remove_filters_to_see_all_cycles": "すべてのサイクルを表示するにはフィルターを解除してください",
+ "remove_search_criteria_to_see_all_cycles": "すべてのサイクルを表示するには検索条件を解除してください",
+ "only_completed_cycles_can_be_archived": "完了したサイクルのみアーカイブできます",
+ "active_cycle": {
+ "label": "アクティブなサイクル",
+ "progress": "進捗",
+ "chart": "バーンダウンチャート",
+ "priority_issue": "優先作業項目",
+ "assignees": "担当者",
+ "issue_burndown": "作業項目バーンダウン",
+ "ideal": "理想",
+ "current": "現在",
+ "labels": "ラベル"
+ },
+ "upcoming_cycle": {
+ "label": "今後のサイクル"
+ },
+ "completed_cycle": {
+ "label": "完了したサイクル"
+ },
+ "status": {
+ "days_left": "残り日数",
+ "completed": "完了",
+ "yet_to_start": "開始前",
+ "in_progress": "進行中",
+ "draft": "下書き"
+ },
+ "action": {
+ "restore": {
+ "title": "サイクルを復元",
+ "success": {
+ "title": "サイクルが復元されました",
+ "description": "サイクルが復元されました。"
+ },
+ "failed": {
+ "title": "サイクルの復元に失敗",
+ "description": "サイクルを復元できませんでした。もう一度お試しください。"
+ }
+ },
+ "favorite": {
+ "loading": "お気に入りにサイクルを追加中",
+ "success": {
+ "description": "サイクルがお気に入りに追加されました。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "サイクルをお気に入りに追加できませんでした。もう一度お試しください。",
+ "title": "エラー!"
+ }
+ },
+ "unfavorite": {
+ "loading": "お気に入りからサイクルを削除中",
+ "success": {
+ "description": "サイクルがお気に入りから削除されました。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "サイクルをお気に入りから削除できませんでした。もう一度お試しください。",
+ "title": "エラー!"
+ }
+ },
+ "update": {
+ "loading": "サイクルを更新中",
+ "success": {
+ "description": "サイクルが正常に更新されました。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "サイクルの更新中にエラーが発生しました。もう一度お試しください。",
+ "title": "エラー!"
+ },
+ "error": {
+ "already_exists": "指定した日付のサイクルは既に存在します。下書きサイクルを作成する場合は、両方の日付を削除してください。"
+ }
+ }
+ },
+ "empty_state": {
+ "general": {
+ "title": "サイクルで作業をグループ化してタイムボックス化します。",
+ "description": "作業をタイムボックス化された単位に分割し、プロジェクトの期限から逆算して日付を設定し、チームとして具体的な進捗を作ります。",
+ "primary_button": {
+ "text": "最初のサイクルを設定",
+ "comic": {
+ "title": "サイクルは繰り返されるタイムボックスです。",
+ "description": "スプリント、イテレーション、または週次や隔週の作業追跡に使用するその他の用語がサイクルです。"
+ }
+ }
+ },
+ "no_issues": {
+ "title": "サイクルに作業項目が追加されていません",
+ "description": "このサイクル内でタイムボックス化して提供したい作業項目を追加または作成します",
+ "primary_button": {
+ "text": "新しい作業項目を作成"
+ },
+ "secondary_button": {
+ "text": "既存の作業項目を追加"
+ }
+ },
+ "completed_no_issues": {
+ "title": "サイクルに作業項目がありません",
+ "description": "サイクルに作業項目がありません。作業項目は転送されたか非表示になっています。非表示の作業項目がある場合は、表示プロパティを更新して確認してください。"
+ },
+ "active": {
+ "title": "アクティブなサイクルがありません",
+ "description": "アクティブなサイクルには、その期間内に今日の日付が含まれるものが該当します。アクティブなサイクルの進捗と詳細をここで確認できます。"
+ },
+ "archived": {
+ "title": "アーカイブされたサイクルがまだありません",
+ "description": "プロジェクトを整理するために、完了したサイクルをアーカイブします。アーカイブ後はここで確認できます。"
+ }
+ }
+ },
+
+ "project_issues": {
+ "empty_state": {
+ "no_issues": {
+ "title": "作業項目を作成して誰かに割り当てましょう。自分自身でも構いません",
+ "description": "作業項目は、仕事、タスク、作業、またはJTBD(私たちが好む用語)と考えてください。作業項目とそのサブ作業項目は通常、チームメンバーに割り当てられる時間ベースのアクションアイテムです。チームは作業項目を作成、割り当て、完了することでプロジェクトの目標に向かって進みます。",
+ "primary_button": {
+ "text": "最初の作業項目を作成",
+ "comic": {
+ "title": "作業項目はPlaneの構成要素です。",
+ "description": "PlaneのUIの再設計、会社のリブランド、新しい燃料噴射システムの立ち上げなどは、サブ作業項目を持つ可能性が高い作業項目の例です。"
+ }
+ }
+ },
+ "no_archived_issues": {
+ "title": "アーカイブされた作業項目がまだありません",
+ "description": "手動または自動化を通じて、完了またはキャンセルされた作業項目をアーカイブできます。アーカイブ後はここで確認できます。",
+ "primary_button": {
+ "text": "自動化を設定"
+ }
+ },
+ "issues_empty_filter": {
+ "title": "適用されたフィルターに一致する作業項目が見つかりません",
+ "secondary_button": {
+ "text": "すべてのフィルターをクリア"
+ }
+ }
+ }
+ },
+
+ "project_module": {
+ "add_module": "モジュールを追加",
+ "update_module": "モジュールを更新",
+ "create_module": "モジュールを作成",
+ "archive_module": "モジュールをアーカイブ",
+ "restore_module": "モジュールを復元",
+ "delete_module": "モジュールを削除",
+ "empty_state": {
+ "general": {
+ "title": "プロジェクトのマイルストーンをモジュールにマッピングし、集計された作業を簡単に追跡できます。",
+ "description": "論理的で階層的な親に属する作業項目のグループがモジュールを形成します。プロジェクトのマイルストーンで作業を追跡する方法として考えてください。期間や期限があり、マイルストーンまでの進捗状況を確認できる分析機能も備えています。",
+ "primary_button": {
+ "text": "最初のモジュールを作成",
+ "comic": {
+ "title": "モジュールは階層的に作業をグループ化するのに役立ちます。",
+ "description": "カートモジュール、シャーシモジュール、倉庫モジュールは、このグループ化の良い例です。"
+ }
+ }
+ },
+ "no_issues": {
+ "title": "モジュールに作業項目がありません",
+ "description": "このモジュールの一部として達成したい作業項目を作成または追加してください",
+ "primary_button": {
+ "text": "新しい作業項目を作成"
+ },
+ "secondary_button": {
+ "text": "既存の作業項目を追加"
+ }
+ },
+ "archived": {
+ "title": "アーカイブされたモジュールがまだありません",
+ "description": "プロジェクトを整理するために、完了またはキャンセルされたモジュールをアーカイブします。アーカイブ後はここで確認できます。"
+ },
+ "sidebar": {
+ "in_active": "このモジュールはまだアクティブではありません。",
+ "invalid_date": "無効な日付です。有効な日付を入力してください。"
+ }
+ },
+ "quick_actions": {
+ "archive_module": "モジュールをアーカイブ",
+ "archive_module_description": "完了またはキャンセルされた\nモジュールのみアーカイブできます。",
+ "delete_module": "モジュールを削除"
+ },
+ "toast": {
+ "copy": {
+ "success": "モジュールのリンクがクリップボードにコピーされました"
+ },
+ "delete": {
+ "success": "モジュールが正常に削除されました",
+ "error": "モジュールを削除できませんでした"
+ }
+ }
+ },
+
+ "project_views": {
+ "empty_state": {
+ "general": {
+ "title": "プロジェクトのフィルター付きビューを保存します。必要な数だけ作成できます",
+ "description": "ビューは、頻繁に使用するフィルターや簡単にアクセスしたいフィルターの集合です。プロジェクト内のすべての同僚が全員のビューを確認でき、自分のニーズに最も合うものを選択できます。",
+ "primary_button": {
+ "text": "最初のビューを作成",
+ "comic": {
+ "title": "ビューは作業項目のプロパティの上で機能します。",
+ "description": "ここから、必要に応じて多くのプロパティやフィルターを使用してビューを作成できます。"
+ }
+ }
+ },
+ "filter": {
+ "title": "一致するビューがありません",
+ "description": "検索条件に一致するビューがありません。\n代わりに新しいビューを作成してください。"
+ }
+ }
+ },
+
+ "project_page": {
+ "empty_state": {
+ "general": {
+ "title": "メモ、ドキュメント、または完全なナレッジベースを作成しましょう。PlaneのAIアシスタントGalileoが開始をサポートします",
+ "description": "ページはPlaneの思考整理スペースです。会議のメモを取り、簡単に整形し、作業項目を埋め込み、コンポーネントライブラリを使用してレイアウトし、すべてをプロジェクトのコンテキストに保存できます。ドキュメントを素早く作成するには、ショートカットまたはボタンのクリックでPlaneのAI、Galileoを呼び出してください。",
+ "primary_button": {
+ "text": "最初のページを作成"
+ }
+ },
+ "private": {
+ "title": "プライベートページがまだありません",
+ "description": "プライベートな考えをここに保存しましょう。共有する準備ができたら、チームはクリック一つで共有できます。",
+ "primary_button": {
+ "text": "最初のページを作成"
+ }
+ },
+ "public": {
+ "title": "公開ページがまだありません",
+ "description": "プロジェクト内の全員と共有されているページをここで確認できます。",
+ "primary_button": {
+ "text": "最初のページを作成"
+ }
+ },
+ "archived": {
+ "title": "アーカイブされたページがまだありません",
+ "description": "注目していないページをアーカイブします。必要な時にここでアクセスできます。"
+ }
+ }
+ },
+
+ "command_k": {
+ "empty_state": {
+ "search": {
+ "title": "結果が見つかりません"
+ }
+ }
+ },
+
+ "issue_relation": {
+ "empty_state": {
+ "search": {
+ "title": "一致する作業項目が見つかりません"
+ },
+ "no_issues": {
+ "title": "作業項目が見つかりません"
+ }
+ }
+ },
+
+ "issue_comment": {
+ "empty_state": {
+ "general": {
+ "title": "コメントがまだありません",
+ "description": "コメントは作業項目のディスカッションとフォローアップのスペースとして使用できます"
+ }
+ }
+ },
+
+ "notification": {
+ "label": "受信トレイ",
+ "page_label": "{workspace} - 受信トレイ",
+ "options": {
+ "mark_all_as_read": "すべて既読にする",
+ "mark_read": "既読にする",
+ "mark_unread": "未読にする",
+ "refresh": "更新",
+ "filters": "受信トレイフィルター",
+ "show_unread": "未読を表示",
+ "show_snoozed": "スヌーズを表示",
+ "show_archived": "アーカイブを表示",
+ "mark_archive": "アーカイブ",
+ "mark_unarchive": "アーカイブ解除",
+ "mark_snooze": "スヌーズ",
+ "mark_unsnooze": "スヌーズ解除"
+ },
+ "toasts": {
+ "read": "通知を既読にしました",
+ "unread": "通知を未読にしました",
+ "archived": "通知をアーカイブしました",
+ "unarchived": "通知をアーカイブ解除しました",
+ "snoozed": "通知をスヌーズしました",
+ "unsnoozed": "通知のスヌーズを解除しました"
+ },
+ "empty_state": {
+ "detail": {
+ "title": "詳細を表示するには選択してください。"
+ },
+ "all": {
+ "title": "割り当てられた作業項目がありません",
+ "description": "あなたに割り当てられた作業項目の更新が\nここに表示されます"
+ },
+ "mentions": {
+ "title": "割り当てられた作業項目がありません",
+ "description": "あなたに割り当てられた作業項目の更新が\nここに表示されます"
+ }
+ },
+ "tabs": {
+ "all": "すべて",
+ "mentions": "メンション"
+ },
+ "filter": {
+ "assigned": "自分に割り当て",
+ "created": "自分が作成",
+ "subscribed": "自分が購読"
+ },
+ "snooze": {
+ "1_day": "1日",
+ "3_days": "3日",
+ "5_days": "5日",
+ "1_week": "1週間",
+ "2_weeks": "2週間",
+ "custom": "カスタム"
+ }
+ },
+
+ "active_cycle": {
+ "empty_state": {
+ "progress": {
+ "title": "サイクルの進捗を表示するには作業項目を追加してください"
+ },
+ "chart": {
+ "title": "バーンダウンチャートを表示するには作業項目を追加してください。"
+ },
+ "priority_issue": {
+ "title": "サイクルで取り組まれている優先度の高い作業項目を一目で確認できます。"
+ },
+ "assignee": {
+ "title": "担当者別の作業の内訳を確認するには、作業項目に担当者を追加してください。"
+ },
+ "label": {
+ "title": "ラベル別の作業の内訳を確認するには、作業項目にラベルを追加してください。"
+ }
+ }
+ },
+
+ "disabled_project": {
+ "empty_state": {
+ "inbox": {
+ "title": "インテークがプロジェクトで有効になっていません。",
+ "description": "インテークは、プロジェクトへの受信リクエストを管理し、ワークフローに作業項目として追加するのに役立ちます。リクエストを管理するには、プロジェクト設定でインテークを有効にしてください。",
+ "primary_button": {
+ "text": "機能を管理"
+ }
+ },
+ "cycle": {
+ "title": "サイクルがこのプロジェクトで有効になっていません。",
+ "description": "時間枠で作業を分割し、プロジェクトの期限から逆算して日付を設定し、チームとして具体的な進捗を作ります。サイクルを使用するには、プロジェクトでサイクル機能を有効にしてください。",
+ "primary_button": {
+ "text": "機能を管理"
+ }
+ },
+ "module": {
+ "title": "モジュールがプロジェクトで有効になっていません。",
+ "description": "モジュールはプロジェクトの構成要素です。モジュールを使用するには、プロジェクト設定でモジュールを有効にしてください。",
+ "primary_button": {
+ "text": "機能を管理"
+ }
+ },
+ "page": {
+ "title": "ページがプロジェクトで有効になっていません。",
+ "description": "ページはプロジェクトの構成要素です。ページを使用するには、プロジェクト設定でページを有効にしてください。",
+ "primary_button": {
+ "text": "機能を管理"
+ }
+ },
+ "view": {
+ "title": "ビューがプロジェクトで有効になっていません。",
+ "description": "ビューはプロジェクトの構成要素です。ビューを使用するには、プロジェクト設定でビューを有効にしてください。",
+ "primary_button": {
+ "text": "機能を管理"
+ }
+ }
+ }
+ },
+ "workspace_draft_issues": {
+ "draft_an_issue": "作業項目の下書き",
+ "empty_state": {
+ "title": "書きかけの作業項目、そしてまもなくコメントがここに表示されます。",
+ "description": "試してみるには、作業項目の追加を開始して途中で中断するか、以下で最初の下書きを作成してください。😉",
+ "primary_button": {
+ "text": "最初の下書きを作成"
+ }
+ },
+ "delete_modal": {
+ "title": "下書きを削除",
+ "description": "この下書きを削除してもよろしいですか?この操作は取り消せません。"
+ },
+ "toasts": {
+ "created": {
+ "success": "下書きを作成しました",
+ "error": "作業項目を作成できませんでした。もう一度お試しください。"
+ },
+ "deleted": {
+ "success": "下書きを削除しました"
+ }
+ }
+ },
+
+ "stickies": {
+ "title": "あなたの付箋",
+ "placeholder": "ここをクリックして入力",
+ "all": "すべての付箋",
+ "no-data": "アイデアをメモしたり、ひらめきをキャプチャしたり、閃きを記録したりしましょう。付箋を追加して始めましょう。",
+ "add": "付箋を追加",
+ "search_placeholder": "タイトルで検索",
+ "delete": "付箋を削除",
+ "delete_confirmation": "この付箋を削除してもよろしいですか?",
+ "empty_state": {
+ "simple": "アイデアをメモしたり、ひらめきをキャプチャしたり、閃きを記録したりしましょう。付箋を追加して始めましょう。",
+ "general": {
+ "title": "付箋は、その場で素早く取るメモやToDoです。",
+ "description": "いつでもどこからでもアクセスできる付箋を作成して、思考やアイデアを簡単にキャプチャできます。",
+ "primary_button": {
+ "text": "付箋を追加"
+ }
+ },
+ "search": {
+ "title": "付箋に一致するものがありません。",
+ "description": "別の用語を試すか、検索が正しいと\n確信がある場合はお知らせください。",
+ "primary_button": {
+ "text": "付箋を追加"
+ }
+ }
+ },
+ "toasts": {
+ "errors": {
+ "wrong_name": "付箋の名前は100文字を超えることはできません。",
+ "already_exists": "説明のない付箋がすでに存在します"
+ },
+ "created": {
+ "title": "付箋を作成しました",
+ "message": "付箋が正常に作成されました"
+ },
+ "not_created": {
+ "title": "付箋を作成できませんでした",
+ "message": "付箋を作成できませんでした"
+ },
+ "updated": {
+ "title": "付箋を更新しました",
+ "message": "付箋が正常に更新されました"
+ },
+ "not_updated": {
+ "title": "付箋を更新できませんでした",
+ "message": "付箋を更新できませんでした"
+ },
+ "removed": {
+ "title": "付箋を削除しました",
+ "message": "付箋が正常に削除されました"
+ },
+ "not_removed": {
+ "title": "付箋を削除できませんでした",
+ "message": "付箋を削除できませんでした"
+ }
+ }
+ },
+
+ "role_details": {
+ "guest": {
+ "title": "ゲスト",
+ "description": "組織の外部メンバーをゲストとして招待できます。"
+ },
+ "member": {
+ "title": "メンバー",
+ "description": "プロジェクト、サイクル、モジュール内のエンティティの読み取り、書き込み、編集、削除が可能"
+ },
+ "admin": {
+ "title": "管理者",
+ "description": "ワークスペース内のすべての権限が有効。"
+ }
+ },
+
+ "user_roles": {
+ "product_or_project_manager": "プロダクト/プロジェクトマネージャー",
+ "development_or_engineering": "開発/エンジニアリング",
+ "founder_or_executive": "創業者/エグゼクティブ",
+ "freelancer_or_consultant": "フリーランス/コンサルタント",
+ "marketing_or_growth": "マーケティング/グロース",
+ "sales_or_business_development": "営業/ビジネス開発",
+ "support_or_operations": "サポート/オペレーション",
+ "student_or_professor": "学生/教授",
+ "human_resources": "人事",
+ "other": "その他"
+ },
+
+ "importer": {
+ "github": {
+ "title": "GitHub",
+ "description": "GitHubリポジトリから作業項目をインポートして同期します。"
+ },
+ "jira": {
+ "title": "Jira",
+ "description": "Jiraプロジェクトとエピックから作業項目とエピックをインポートします。"
+ }
+ },
+
+ "exporter": {
+ "csv": {
+ "title": "CSV",
+ "description": "作業項目をCSVファイルにエクスポートします。",
+ "short_description": "CSVとしてエクスポート"
+ },
+ "excel": {
+ "title": "Excel",
+ "description": "作業項目をExcelファイルにエクスポートします。",
+ "short_description": "Excelとしてエクスポート"
+ },
+ "xlsx": {
+ "title": "Excel",
+ "description": "作業項目をExcelファイルにエクスポートします。",
+ "short_description": "Excelとしてエクスポート"
+ },
+ "json": {
+ "title": "JSON",
+ "description": "作業項目をJSONファイルにエクスポートします。",
+ "short_description": "JSONとしてエクスポート"
+ }
+ },
+ "default_global_view": {
+ "all_issues": "すべての作業項目",
+ "assigned": "割り当て済み",
+ "created": "作成済み",
+ "subscribed": "購読中"
+ },
+
+ "themes": {
+ "theme_options": {
+ "system_preference": {
+ "label": "システム設定"
+ },
+ "light": {
+ "label": "ライト"
+ },
+ "dark": {
+ "label": "ダーク"
+ },
+ "light_contrast": {
+ "label": "ライトハイコントラスト"
+ },
+ "dark_contrast": {
+ "label": "ダークハイコントラスト"
+ },
+ "custom": {
+ "label": "カスタムテーマ"
+ }
+ }
+ },
+ "project_modules": {
+ "status": {
+ "backlog": "バックログ",
+ "planned": "計画済み",
+ "in_progress": "進行中",
+ "paused": "一時停止",
+ "completed": "完了",
+ "cancelled": "キャンセル"
+ },
+ "layout": {
+ "list": "リスト表示",
+ "board": "ギャラリー表示",
+ "timeline": "タイムライン表示"
+ },
+ "order_by": {
+ "name": "名前",
+ "progress": "進捗",
+ "issues": "作業項目数",
+ "due_date": "期限",
+ "created_at": "作成日",
+ "manual": "手動"
+ }
+ },
+
+ "cycle": {
+ "label": "{count, plural, one {サイクル} other {サイクル}}",
+ "no_cycle": "サイクルなし"
+ },
+
+ "module": {
+ "label": "{count, plural, one {モジュール} other {モジュール}}",
+ "no_module": "モジュールなし"
+ }
+}
diff --git a/packages/i18n/src/locales/zh-CN/translations.json b/packages/i18n/src/locales/zh-CN/translations.json
new file mode 100644
index 000000000..aca47f21a
--- /dev/null
+++ b/packages/i18n/src/locales/zh-CN/translations.json
@@ -0,0 +1,2362 @@
+{
+ "sidebar": {
+ "projects": "项目",
+ "pages": "页面",
+ "new_work_item": "新工作项",
+ "home": "主页",
+ "your_work": "我的工作",
+ "inbox": "收件箱",
+ "workspace": "工作区",
+ "views": "视图",
+ "analytics": "分析",
+ "work_items": "工作项",
+ "cycles": "周期",
+ "modules": "模块",
+ "intake": "收集",
+ "drafts": "草稿",
+ "favorites": "收藏",
+ "pro": "专业版",
+ "upgrade": "升级"
+ },
+
+ "auth": {
+ "common": {
+ "email": {
+ "label": "邮箱",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "邮箱是必填项",
+ "invalid": "邮箱格式无效"
+ }
+ },
+ "password": {
+ "label": "密码",
+ "set_password": "设置密码",
+ "placeholder": "输入密码",
+ "confirm_password": {
+ "label": "确认密码",
+ "placeholder": "确认密码"
+ },
+ "current_password": {
+ "label": "当前密码"
+ },
+ "new_password": {
+ "label": "新密码",
+ "placeholder": "输入新密码"
+ },
+ "change_password": {
+ "label": {
+ "default": "修改密码",
+ "submitting": "正在修改密码"
+ }
+ },
+ "errors": {
+ "match": "密码不匹配",
+ "empty": "请输入密码",
+ "length": "密码长度应超过8个字符",
+ "strength": {
+ "weak": "密码强度较弱",
+ "strong": "密码强度较强"
+ }
+ },
+ "submit": "设置密码",
+ "toast": {
+ "change_password": {
+ "success": {
+ "title": "成功!",
+ "message": "密码修改成功。"
+ },
+ "error": {
+ "title": "错误!",
+ "message": "出现错误。请重试。"
+ }
+ }
+ }
+ },
+ "unique_code": {
+ "label": "唯一码",
+ "placeholder": "gets-sets-flys",
+ "paste_code": "粘贴发送到您邮箱的验证码",
+ "requesting_new_code": "正在请求新验证码",
+ "sending_code": "正在发送验证码"
+ },
+ "already_have_an_account": "已有账号?",
+ "login": "登录",
+ "create_account": "创建账号",
+ "new_to_plane": "首次使用 Plane?",
+ "back_to_sign_in": "返回登录",
+ "resend_in": "{seconds} 秒后重新发送",
+ "sign_in_with_unique_code": "使用唯一码登录",
+ "forgot_password": "忘记密码?"
+ },
+ "sign_up": {
+ "header": {
+ "label": "创建账号以开始与团队一起管理工作。",
+ "step": {
+ "email": {
+ "header": "注册",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "注册",
+ "sub_header": "使用邮箱-密码组合注册。"
+ },
+ "unique_code": {
+ "header": "注册",
+ "sub_header": "使用发送到上述邮箱的唯一码注册。"
+ }
+ }
+ },
+ "errors": {
+ "password": {
+ "strength": "请设置一个强密码以继续"
+ }
+ }
+ },
+ "sign_in": {
+ "header": {
+ "label": "登录以开始与团队一起管理工作。",
+ "step": {
+ "email": {
+ "header": "登录或注册",
+ "sub_header": ""
+ },
+ "password": {
+ "header": "登录或注册",
+ "sub_header": "使用您的邮箱-密码组合登录。"
+ },
+ "unique_code": {
+ "header": "登录或注册",
+ "sub_header": "使用发送到上述邮箱的唯一码登录。"
+ }
+ }
+ }
+ },
+ "forgot_password": {
+ "title": "重置密码",
+ "description": "输入您的用户账号已验证的邮箱地址,我们将向您发送密码重置链接。",
+ "email_sent": "我们已将重置链接发送到您的邮箱",
+ "send_reset_link": "发送重置链接",
+ "errors": {
+ "smtp_not_enabled": "我们发现您的管理员未启用 SMTP,我们将无法发送密码重置链接"
+ },
+ "toast": {
+ "success": {
+ "title": "邮件已发送",
+ "message": "请查看您的收件箱以获取重置密码的链接。如果几分钟内未收到,请检查垃圾邮件文件夹。"
+ },
+ "error": {
+ "title": "错误!",
+ "message": "出现错误。请重试。"
+ }
+ }
+ },
+ "reset_password": {
+ "title": "设置新密码",
+ "description": "使用强密码保护您的账号"
+ },
+ "set_password": {
+ "title": "保护您的账号",
+ "description": "设置密码有助于您安全登录"
+ },
+ "sign_out": {
+ "toast": {
+ "error": {
+ "title": "错误!",
+ "message": "登出失败。请重试。"
+ }
+ }
+ }
+ },
+
+ "submit": "提交",
+ "cancel": "取消",
+ "loading": "加载中",
+ "error": "错误",
+ "success": "成功",
+ "warning": "警告",
+ "info": "信息",
+ "close": "关闭",
+ "yes": "是",
+ "no": "否",
+ "ok": "确定",
+ "name": "名称",
+ "description": "描述",
+ "search": "搜索",
+ "add_member": "添加成员",
+ "adding_members": "正在添加成员",
+ "remove_member": "移除成员",
+ "add_members": "添加成员",
+ "adding_member": "正在添加成员",
+ "remove_members": "移除成员",
+ "add": "添加",
+ "adding": "添加中",
+ "remove": "移除",
+ "add_new": "添加新的",
+ "remove_selected": "移除所选",
+ "first_name": "名",
+ "last_name": "姓",
+ "email": "邮箱",
+ "display_name": "显示名称",
+ "role": "角色",
+ "timezone": "时区",
+ "avatar": "头像",
+ "cover_image": "封面图片",
+ "password": "密码",
+ "change_cover": "更改封面",
+ "language": "语言",
+ "saving": "保存中",
+ "save_changes": "保存更改",
+ "deactivate_account": "停用账号",
+ "deactivate_account_description": "停用账号后,该账号内的所有数据和资源将被永久删除且无法恢复。",
+ "profile_settings": "个人资料设置",
+ "your_account": "您的账号",
+ "security": "安全",
+ "activity": "活动",
+ "appearance": "外观",
+ "notifications": "通知",
+ "workspaces": "工作区",
+ "create_workspace": "创建工作区",
+ "invitations": "邀请",
+ "summary": "摘要",
+ "assigned": "已分配",
+ "created": "已创建",
+ "subscribed": "已订阅",
+ "you_do_not_have_the_permission_to_access_this_page": "您没有访问此页面的权限。",
+ "something_went_wrong_please_try_again": "出现错误。请重试。",
+ "load_more": "加载更多",
+ "select_or_customize_your_interface_color_scheme": "选择或自定义您的界面配色方案。",
+ "theme": "主题",
+ "system_preference": "系统偏好",
+ "light": "浅色",
+ "dark": "深色",
+ "light_contrast": "浅色高对比度",
+ "dark_contrast": "深色高对比度",
+ "custom": "自定义主题",
+ "select_your_theme": "选择您的主题",
+ "customize_your_theme": "自定义您的主题",
+ "background_color": "背景颜色",
+ "text_color": "文字颜色",
+ "primary_color": "主要(主题)颜色",
+ "sidebar_background_color": "侧边栏背景颜色",
+ "sidebar_text_color": "侧边栏文字颜色",
+ "set_theme": "设置主题",
+ "enter_a_valid_hex_code_of_6_characters": "输入有效的6位十六进制代码",
+ "background_color_is_required": "背景颜色为必填项",
+ "text_color_is_required": "文字颜色为必填项",
+ "primary_color_is_required": "主要颜色为必填项",
+ "sidebar_background_color_is_required": "侧边栏背景颜色为必填项",
+ "sidebar_text_color_is_required": "侧边栏文字颜色为必填项",
+ "updating_theme": "正在更新主题",
+ "theme_updated_successfully": "主题更新成功",
+ "failed_to_update_the_theme": "主题更新失败",
+ "email_notifications": "邮件通知",
+ "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "及时了解您订阅的工作项。启用此功能以获取通知。",
+ "email_notification_setting_updated_successfully": "邮件通知设置更新成功",
+ "failed_to_update_email_notification_setting": "邮件通知设置更新失败",
+ "notify_me_when": "在以下情况通知我",
+ "property_changes": "属性变更",
+ "property_changes_description": "当工作项的属性(如负责人、优先级、估算等)发生变更时通知我。",
+ "state_change": "状态变更",
+ "state_change_description": "当工作项移动到不同状态时通知我",
+ "issue_completed": "工作项完成",
+ "issue_completed_description": "仅当工作项完成时通知我",
+ "comments": "评论",
+ "comments_description": "当有人在工作项上发表评论时通知我",
+ "mentions": "提及",
+ "mentions_description": "仅当有人在评论或描述中提及我时通知我",
+ "old_password": "旧密码",
+ "general_settings": "常规设置",
+ "sign_out": "退出登录",
+ "signing_out": "正在退出登录",
+ "active_cycles": "活动周期",
+ "active_cycles_description": "监控各个项目的周期,跟踪高优先级工作项,并关注需要注意的周期。",
+ "on_demand_snapshots_of_all_your_cycles": "所有周期的实时快照",
+ "upgrade": "升级",
+ "10000_feet_view": "所有活动周期的全局视图。",
+ "10000_feet_view_description": "放大视角,一次性查看所有项目中正在进行的周期,而不是在每个项目中逐个查看周期。",
+ "get_snapshot_of_each_active_cycle": "获取每个活动周期的快照。",
+ "get_snapshot_of_each_active_cycle_description": "跟踪所有活动周期的高级指标,查看其进度状态,并了解与截止日期相关的范围。",
+ "compare_burndowns": "比较燃尽图。",
+ "compare_burndowns_description": "通过查看每个周期的燃尽报告,监控每个团队的表现。",
+ "quickly_see_make_or_break_issues": "快速查看关键工作项。",
+ "quickly_see_make_or_break_issues_description": "预览每个周期中与截止日期相关的高优先级工作项。一键查看每个周期的所有工作项。",
+ "zoom_into_cycles_that_need_attention": "关注需要注意的周期。",
+ "zoom_into_cycles_that_need_attention_description": "一键调查任何不符合预期的周期状态。",
+ "stay_ahead_of_blockers": "提前预防阻塞。",
+ "stay_ahead_of_blockers_description": "发现从一个项目到另一个项目的挑战,并查看从其他视图中不易发现的周期间依赖关系。",
+ "analytics": "分析",
+ "workspace_invites": "工作区邀请",
+ "enter_god_mode": "进入管理员模式",
+ "workspace_logo": "工作区标志",
+ "new_issue": "新工作项",
+ "your_work": "我的工作",
+ "drafts": "草稿",
+ "projects": "项目",
+ "views": "视图",
+ "workspace": "工作区",
+ "archives": "归档",
+ "settings": "设置",
+ "failed_to_move_favorite": "移动收藏失败",
+ "favorites": "收藏",
+ "no_favorites_yet": "暂无收藏",
+ "create_folder": "创建文件夹",
+ "new_folder": "新建文件夹",
+ "favorite_updated_successfully": "收藏更新成功",
+ "favorite_created_successfully": "收藏创建成功",
+ "folder_already_exists": "文件夹已存在",
+ "folder_name_cannot_be_empty": "文件夹名称不能为空",
+ "something_went_wrong": "出现错误",
+ "failed_to_reorder_favorite": "重新排序收藏失败",
+ "favorite_removed_successfully": "收藏移除成功",
+ "failed_to_create_favorite": "创建收藏失败",
+ "failed_to_rename_favorite": "重命名收藏失败",
+ "project_link_copied_to_clipboard": "项目链接已复制到剪贴板",
+ "link_copied": "链接已复制",
+ "add_project": "添加项目",
+ "create_project": "创建项目",
+ "failed_to_remove_project_from_favorites": "无法从收藏中移除项目。请重试。",
+ "project_created_successfully": "项目创建成功",
+ "project_created_successfully_description": "项目创建成功。您现在可以开始添加工作项了。",
+ "project_cover_image_alt": "项目封面图片",
+ "name_is_required": "名称为必填项",
+ "title_should_be_less_than_255_characters": "标题应少于255个字符",
+ "project_name": "项目名称",
+ "project_id_must_be_at_least_1_character": "项目ID至少需要1个字符",
+ "project_id_must_be_at_most_5_characters": "项目ID最多只能有5个字符",
+ "project_id": "项目ID",
+ "project_id_tooltip_content": "帮助您唯一标识项目中的工作项。最多5个字符。",
+ "description_placeholder": "描述",
+ "only_alphanumeric_non_latin_characters_allowed": "仅允许字母数字和非拉丁字符。",
+ "project_id_is_required": "项目ID为必填项",
+ "project_id_allowed_char": "仅允许字母数字和非拉丁字符。",
+ "project_id_min_char": "项目ID至少需要1个字符",
+ "project_id_max_char": "项目ID最多只能有5个字符",
+ "project_description_placeholder": "输入项目描述",
+ "select_network": "选择网络",
+ "lead": "负责人",
+ "date_range": "日期范围",
+ "private": "私有",
+ "public": "公开",
+ "accessible_only_by_invite": "仅受邀者可访问",
+ "anyone_in_the_workspace_except_guests_can_join": "除访客外的工作区所有成员都可以加入",
+ "creating": "创建中",
+ "creating_project": "正在创建项目",
+ "adding_project_to_favorites": "正在将项目添加到收藏",
+ "project_added_to_favorites": "项目已添加到收藏",
+ "couldnt_add_the_project_to_favorites": "无法将项目添加到收藏。请重试。",
+ "removing_project_from_favorites": "正在从收藏中移除项目",
+ "project_removed_from_favorites": "项目已从收藏中移除",
+ "couldnt_remove_the_project_from_favorites": "无法从收藏中移除项目。请重试。",
+ "add_to_favorites": "添加到收藏",
+ "remove_from_favorites": "从收藏中移除",
+ "publish_settings": "发布设置",
+ "publish": "发布",
+ "copy_link": "复制链接",
+ "leave_project": "离开项目",
+ "join_the_project_to_rearrange": "加入项目以重新排列",
+ "drag_to_rearrange": "拖动以重新排列",
+ "congrats": "恭喜!",
+ "open_project": "打开项目",
+ "issues": "工作项",
+ "cycles": "周期",
+ "modules": "模块",
+ "pages": "页面",
+ "intake": "收集",
+ "time_tracking": "时间跟踪",
+ "work_management": "工作管理",
+ "projects_and_issues": "项目和工作项",
+ "projects_and_issues_description": "在此项目中开启或关闭这些功能。",
+ "cycles_description": "根据项目需要设置时间框,可以根据不同时期更改频率。",
+ "modules_description": "将工作分组为类似子项目的设置,具有各自的负责人和分配者。",
+ "views_description": "保存排序、筛选和显示选项以供后续使用或分享。",
+ "pages_description": "像写任何东西一样写任何东西。",
+ "intake_description": "及时了解您订阅的工作项。启用此功能以获取通知。",
+ "time_tracking_description": "跟踪工作项和项目的时间消耗。",
+ "work_management_description": "轻松管理您的工作和项目。",
+ "documentation": "文档",
+ "message_support": "联系支持",
+ "contact_sales": "联系销售",
+ "hyper_mode": "超级模式",
+ "keyboard_shortcuts": "键盘快捷键",
+ "whats_new": "新功能",
+ "version": "版本",
+ "we_are_having_trouble_fetching_the_updates": "我们在获取更新时遇到问题。",
+ "our_changelogs": "我们的更新日志",
+ "for_the_latest_updates": "获取最新更新。",
+ "please_visit": "请访问",
+ "docs": "文档",
+ "full_changelog": "完整更新日志",
+ "support": "支持",
+ "discord": "Discord",
+ "powered_by_plane_pages": "由Plane Pages提供支持",
+ "please_select_at_least_one_invitation": "请至少选择一个邀请。",
+ "please_select_at_least_one_invitation_description": "请至少选择一个加入工作区的邀请。",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace": "我们看到有人邀请您加入工作区",
+ "join_a_workspace": "加入工作区",
+ "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "我们看到有人邀请您加入工作区",
+ "join_a_workspace_description": "加入工作区",
+ "accept_and_join": "接受并加入",
+ "go_home": "返回首页",
+ "no_pending_invites": "没有待处理的邀请",
+ "you_can_see_here_if_someone_invites_you_to_a_workspace": "如果有人邀请您加入工作区,您可以在这里看到",
+ "back_to_home": "返回首页",
+ "workspace_name": "工作区名称",
+ "deactivate_your_account": "停用您的账户",
+ "deactivate_your_account_description": "一旦停用,您将无法被分配工作项,也不会被计入工作区的账单。要重新激活您的账户,您需要收到发送到此电子邮件地址的工作区邀请。",
+ "deactivating": "正在停用",
+ "confirm": "确认",
+ "confirming": "确认中",
+ "draft_created": "草稿已创建",
+ "issue_created_successfully": "工作项创建成功",
+ "draft_creation_failed": "草稿创建失败",
+ "issue_creation_failed": "工作项创建失败",
+ "draft_issue": "草稿工作项",
+ "issue_updated_successfully": "工作项更新成功",
+ "issue_could_not_be_updated": "工作项无法更新",
+ "create_a_draft": "创建草稿",
+ "save_to_drafts": "保存到草稿",
+ "save": "保存",
+ "update": "更新",
+ "updating": "更新中",
+ "create_new_issue": "创建新工作项",
+ "editor_is_not_ready_to_discard_changes": "编辑器尚未准备好放弃更改",
+ "failed_to_move_issue_to_project": "无法将工作项移动到项目",
+ "create_more": "创建更多",
+ "add_to_project": "添加到项目",
+ "discard": "放弃",
+ "duplicate_issue_found": "发现重复的工作项",
+ "duplicate_issues_found": "发现重复的工作项",
+ "no_matching_results": "没有匹配的结果",
+ "title_is_required": "标题为必填项",
+ "title": "标题",
+ "state": "状态",
+ "priority": "优先级",
+ "none": "无",
+ "urgent": "紧急",
+ "high": "高",
+ "medium": "中",
+ "low": "低",
+ "members": "成员",
+ "assignee": "负责人",
+ "assignees": "负责人",
+ "you": "您",
+ "labels": "标签",
+ "create_new_label": "创建新标签",
+ "start_date": "开始日期",
+ "end_date": "结束日期",
+ "due_date": "截止日期",
+ "estimate": "估算",
+ "change_parent_issue": "更改父工作项",
+ "remove_parent_issue": "移除父工作项",
+ "add_parent": "添加父项",
+ "loading_members": "正在加载成员",
+ "view_link_copied_to_clipboard": "视图链接已复制到剪贴板",
+ "required": "必填",
+ "optional": "可选",
+ "Cancel": "取消",
+ "edit": "编辑",
+ "archive": "归档",
+ "restore": "恢复",
+ "open_in_new_tab": "在新标签页中打开",
+ "delete": "删除",
+ "deleting": "删除中",
+ "make_a_copy": "创建副本",
+ "move_to_project": "移动到项目",
+ "good": "早上",
+ "morning": "早上",
+ "afternoon": "下午",
+ "evening": "晚上",
+ "show_all": "显示全部",
+ "show_less": "显示更少",
+ "no_data_yet": "暂无数据",
+ "syncing": "同步中",
+ "add_work_item": "添加工作项",
+ "advanced_description_placeholder": "按'/'使用命令",
+ "create_work_item": "创建工作项",
+ "attachments": "附件",
+ "declining": "拒绝中",
+ "declined": "已拒绝",
+ "decline": "拒绝",
+ "unassigned": "未分配",
+ "work_items": "工作项",
+ "add_link": "添加链接",
+ "points": "点数",
+ "no_assignee": "无负责人",
+ "no_assignees_yet": "暂无负责人",
+ "no_labels_yet": "暂无标签",
+ "ideal": "理想",
+ "current": "当前",
+ "no_matching_members": "没有匹配的成员",
+ "leaving": "离开中",
+ "removing": "移除中",
+ "leave": "离开",
+ "refresh": "刷新",
+ "refreshing": "刷新中",
+ "refresh_status": "刷新状态",
+ "prev": "上一个",
+ "next": "下一个",
+ "re_generating": "重新生成中",
+ "re_generate": "重新生成",
+ "re_generate_key": "重新生成密钥",
+ "export": "导出",
+ "member": "{count, plural, other{# 成员}}",
+
+ "project_view": {
+ "sort_by": {
+ "created_at": "创建时间",
+ "updated_at": "更新时间",
+ "name": "名称"
+ }
+ },
+
+ "toast": {
+ "success": "成功!",
+ "error": "错误!"
+ },
+
+ "links": {
+ "toasts": {
+ "created": {
+ "title": "链接已创建",
+ "message": "链接已成功创建"
+ },
+ "not_created": {
+ "title": "链接未创建",
+ "message": "无法创建链接"
+ },
+ "updated": {
+ "title": "链接已更新",
+ "message": "链接已成功更新"
+ },
+ "not_updated": {
+ "title": "链接未更新",
+ "message": "无法更新链接"
+ },
+ "removed": {
+ "title": "链接已移除",
+ "message": "链接已成功移除"
+ },
+ "not_removed": {
+ "title": "链接未移除",
+ "message": "无法移除链接"
+ }
+ }
+ },
+
+ "home": {
+ "empty": {
+ "quickstart_guide": "快速入门指南",
+ "not_right_now": "暂时不要",
+ "create_project": {
+ "title": "创建项目",
+ "description": "在Plane中,大多数事情都从项目开始。",
+ "cta": "开始使用"
+ },
+ "invite_team": {
+ "title": "邀请您的团队",
+ "description": "与同事一起构建、发布和管理。",
+ "cta": "邀请他们加入"
+ },
+ "configure_workspace": {
+ "title": "设置您的工作区",
+ "description": "开启或关闭功能,或进行更多设置。",
+ "cta": "配置此工作区"
+ },
+ "personalize_account": {
+ "title": "让Plane更适合您",
+ "description": "选择您的头像、颜色等。",
+ "cta": "立即个性化"
+ },
+ "widgets": {
+ "title": "没有小部件看起来很安静,开启它们吧",
+ "description": "看起来您的所有小部件都已关闭。现在启用它们\n来提升您的体验!",
+ "primary_button": {
+ "text": "管理小部件"
+ }
+ }
+ },
+ "quick_links": {
+ "empty": "保存您想要方便访问的工作相关链接。",
+ "add": "添加快速链接",
+ "title": "快速链接",
+ "title_plural": "快速链接"
+ },
+ "recents": {
+ "title": "最近",
+ "empty": {
+ "project": "访问项目后,您的最近项目将显示在这里。",
+ "page": "访问页面后,您的最近页面将显示在这里。",
+ "issue": "访问工作项后,您的最近工作项将显示在这里。",
+ "default": "您还没有任何最近项目。"
+ },
+ "filters": {
+ "all": "所有项目",
+ "projects": "项目",
+ "pages": "页面",
+ "issues": "工作项"
+ }
+ },
+ "new_at_plane": {
+ "title": "Plane新功能"
+ },
+ "quick_tutorial": {
+ "title": "快速教程"
+ },
+ "widget": {
+ "reordered_successfully": "小部件重新排序成功。",
+ "reordering_failed": "重新排序小部件时出错。"
+ },
+ "manage_widgets": "管理小部件",
+ "title": "首页",
+ "star_us_on_github": "在GitHub上为我们加星"
+ },
+
+ "link": {
+ "modal": {
+ "url": {
+ "text": "URL",
+ "required": "URL无效",
+ "placeholder": "输入或粘贴URL"
+ },
+ "title": {
+ "text": "显示标题",
+ "placeholder": "您希望如何显示此链接"
+ }
+ }
+ },
+
+ "common": {
+ "all": "全部",
+ "states": "状态",
+ "state": "状态",
+ "state_groups": "状态组",
+ "priority": "优先级",
+ "team_project": "团队项目",
+ "project": "项目",
+ "cycle": "周期",
+ "cycles": "周期",
+ "module": "模块",
+ "modules": "模块",
+ "labels": "标签",
+ "assignees": "负责人",
+ "assignee": "负责人",
+ "created_by": "创建者",
+ "none": "无",
+ "link": "链接",
+ "estimate": "估算",
+ "layout": "布局",
+ "filters": "筛选",
+ "display": "显示",
+ "load_more": "加载更多",
+ "activity": "活动",
+ "analytics": "分析",
+ "dates": "日期",
+ "success": "成功!",
+ "something_went_wrong": "出现错误",
+ "error": {
+ "label": "错误!",
+ "message": "发生错误。请重试。"
+ },
+ "group_by": "分组方式",
+ "epic": "史诗",
+ "epics": "史诗",
+ "work_item": "工作项",
+ "work_items": "工作项",
+ "sub_work_item": "子工作项",
+ "add": "添加",
+ "warning": "警告",
+ "updating": "更新中",
+ "adding": "添加中",
+ "update": "更新",
+ "creating": "创建中",
+ "create": "创建",
+ "cancel": "取消",
+ "description": "描述",
+ "title": "标题",
+ "attachment": "附件",
+ "general": "常规",
+ "features": "功能",
+ "automation": "自动化",
+ "project_name": "项目名称",
+ "project_id": "项目ID",
+ "project_timezone": "项目时区",
+ "created_on": "创建于",
+ "update_project": "更新项目",
+ "identifier_already_exists": "标识符已存在",
+ "add_more": "添加更多",
+ "defaults": "默认值",
+ "add_label": "添加标签",
+ "estimates": "估算",
+ "customize_time_range": "自定义时间范围",
+ "loading": "加载中",
+ "attachments": "附件",
+ "property": "属性",
+ "properties": "属性",
+ "parent": "父项",
+ "remove": "移除",
+ "archiving": "归档中",
+ "archive": "归档",
+ "access": {
+ "public": "公开",
+ "private": "私有"
+ },
+ "done": "完成",
+ "sub_work_items": "子工作项",
+ "comment": "评论",
+ "workspace_level": "工作区级别",
+ "order_by": {
+ "label": "排序方式",
+ "manual": "手动",
+ "last_created": "最近创建",
+ "last_updated": "最近更新",
+ "start_date": "开始日期",
+ "due_date": "截止日期",
+ "asc": "升序",
+ "desc": "降序",
+ "updated_on": "更新时间"
+ },
+ "sort": {
+ "asc": "升序",
+ "desc": "降序",
+ "created_on": "创建时间",
+ "updated_on": "更新时间"
+ },
+ "comments": "评论",
+ "updates": "更新",
+ "clear_all": "清除全部",
+ "copied": "已复制!",
+ "link_copied": "链接已复制!",
+ "link_copied_to_clipboard": "链接已复制到剪贴板",
+ "copied_to_clipboard": "工作项链接已复制到剪贴板",
+ "is_copied_to_clipboard": "工作项已复制到剪贴板",
+ "no_links_added_yet": "暂无添加的链接",
+ "add_link": "添加链接",
+ "links": "链接",
+ "go_to_workspace": "前往工作区",
+ "progress": "进度",
+ "optional": "可选",
+ "join": "加入",
+ "go_back": "返回",
+ "continue": "继续",
+ "resend": "重新发送",
+ "relations": "关系",
+ "errors": {
+ "default": {
+ "title": "错误!",
+ "message": "发生错误。请重试。"
+ },
+ "required": "此字段为必填项",
+ "entity_required": "{entity}为必填项"
+ },
+ "update_link": "更新链接",
+ "attach": "附加",
+ "create_new": "创建新的",
+ "add_existing": "添加现有",
+ "type_or_paste_a_url": "输入或粘贴URL",
+ "url_is_invalid": "URL无效",
+ "display_title": "显示标题",
+ "link_title_placeholder": "您希望如何显示此链接",
+ "url": "URL",
+ "side_peek": "侧边预览",
+ "modal": "模态框",
+ "full_screen": "全屏",
+ "close_peek_view": "关闭预览视图",
+ "toggle_peek_view_layout": "切换预览视图布局",
+ "options": "选项",
+ "duration": "持续时间",
+ "today": "今天",
+ "week": "周",
+ "month": "月",
+ "quarter": "季度",
+ "press_for_commands": "按'/'使用命令",
+ "click_to_add_description": "点击添加描述",
+ "search": {
+ "label": "搜索",
+ "placeholder": "输入搜索内容",
+ "no_matches_found": "未找到匹配项",
+ "no_matching_results": "没有匹配的结果"
+ },
+ "actions": {
+ "edit": "编辑",
+ "make_a_copy": "创建副本",
+ "open_in_new_tab": "在新标签页中打开",
+ "copy_link": "复制链接",
+ "archive": "归档",
+ "delete": "删除",
+ "remove_relation": "移除关系",
+ "subscribe": "订阅",
+ "unsubscribe": "取消订阅",
+ "clear_sorting": "清除排序",
+ "show_weekends": "显示周末",
+ "enable": "启用",
+ "disable": "禁用"
+ },
+ "name": "名称",
+ "discard": "放弃",
+ "confirm": "确认",
+ "confirming": "确认中",
+ "read_the_docs": "阅读文档",
+ "default": "默认",
+ "active": "活动",
+ "enabled": "已启用",
+ "disabled": "已禁用",
+ "mandate": "授权",
+ "mandatory": "必需的",
+ "yes": "是",
+ "no": "否",
+ "please_wait": "请稍候",
+ "enabling": "正在启用",
+ "disabling": "正在禁用",
+ "beta": "测试版",
+ "or": "或",
+ "next": "下一步",
+ "back": "返回",
+ "cancelling": "正在取消",
+ "configuring": "正在配置",
+ "clear": "清除",
+ "import": "导入",
+ "connect": "连接",
+ "authorizing": "正在授权",
+ "processing": "正在处理",
+ "no_data_available": "暂无数据",
+ "from": "来自 {name}",
+ "authenticated": "已认证",
+ "select": "选择",
+ "upgrade": "升级",
+ "add_seats": "添加席位",
+ "label": "标签",
+ "priorities": "优先级",
+ "projects": "项目",
+ "workspace": "工作区",
+ "workspaces": "工作区",
+ "team": "团队",
+ "teams": "团队",
+ "entity": "实体",
+ "entities": "实体",
+ "task": "任务",
+ "tasks": "任务",
+ "section": "部分",
+ "sections": "部分",
+ "edit": "编辑",
+ "connecting": "正在连接",
+ "connected": "已连接",
+ "disconnect": "断开连接",
+ "disconnecting": "正在断开连接",
+ "installing": "正在安装",
+ "install": "安装",
+ "reset": "重置",
+ "live": "实时",
+ "change_history": "变更历史",
+ "coming_soon": "即将推出",
+ "members": "成员",
+ "you": "你",
+ "upgrade_cta": {
+ "higher_subscription": "升级到更高订阅",
+ "talk_to_sales": "联系销售"
+ },
+ "category": "类别",
+ "categories": "类别",
+ "saving": "保存中",
+ "save_changes": "保存更改",
+ "delete": "删除",
+ "deleting": "删除中",
+ "pending": "待处理",
+ "invite": "邀请"
+ },
+
+ "chart": {
+ "x_axis": "X轴",
+ "y_axis": "Y轴",
+ "metric": "指标"
+ },
+
+ "form": {
+ "title": {
+ "required": "标题为必填项",
+ "max_length": "标题应少于 {length} 个字符"
+ }
+ },
+
+ "entity": {
+ "grouping_title": "{entity}分组",
+ "priority": "{entity}优先级",
+ "all": "所有{entity}",
+ "drop_here_to_move": "拖放到此处以移动{entity}",
+ "delete": {
+ "label": "删除{entity}",
+ "success": "{entity}删除成功",
+ "failed": "{entity}删除失败"
+ },
+ "update": {
+ "failed": "{entity}更新失败",
+ "success": "{entity}更新成功"
+ },
+ "link_copied_to_clipboard": "{entity}链接已复制到剪贴板",
+ "fetch": {
+ "failed": "获取{entity}时出错"
+ },
+ "add": {
+ "success": "{entity}添加成功",
+ "failed": "添加{entity}时出错"
+ }
+ },
+
+ "epic": {
+ "all": "所有史诗",
+ "label": "{count, plural, one {史诗} other {史诗}}",
+ "new": "新建史诗",
+ "adding": "正在添加史诗",
+ "create": {
+ "success": "史诗创建成功"
+ },
+ "add": {
+ "press_enter": "按'Enter'添加另一个史诗",
+ "label": "添加史诗"
+ },
+ "title": {
+ "label": "史诗标题",
+ "required": "史诗标题为必填项"
+ }
+ },
+
+ "issue": {
+ "label": "{count, plural, one {工作项} other {工作项}}",
+ "all": "所有工作项",
+ "edit": "编辑工作项",
+ "title": {
+ "label": "工作项标题",
+ "required": "工作项标题为必填项"
+ },
+ "add": {
+ "press_enter": "按'Enter'添加另一个工作项",
+ "label": "添加工作项",
+ "cycle": {
+ "failed": "无法将工作项添加到周期。请重试。",
+ "success": "{count, plural, one {工作项} other {工作项}}已成功添加到周期。",
+ "loading": "正在将{count, plural, one {工作项} other {工作项}}添加到周期"
+ },
+ "assignee": "添加负责人",
+ "start_date": "添加开始日期",
+ "due_date": "添加截止日期",
+ "parent": "添加父工作项",
+ "sub_issue": "添加子工作项",
+ "relation": "添加关系",
+ "link": "添加链接",
+ "existing": "添加现有工作项"
+ },
+ "remove": {
+ "label": "移除工作项",
+ "cycle": {
+ "loading": "正在从周期中移除工作项",
+ "success": "已成功从周期中移除工作项。",
+ "failed": "无法从周期中移除工作项。请重试。"
+ },
+ "module": {
+ "loading": "正在从模块中移除工作项",
+ "success": "已成功从模块中移除工作项。",
+ "failed": "无法从模块中移除工作项。请重试。"
+ },
+ "parent": {
+ "label": "移除父工作项"
+ }
+ },
+ "new": "新建工作项",
+ "adding": "正在添加工作项",
+ "create": {
+ "success": "工作项创建成功"
+ },
+ "priority": {
+ "urgent": "紧急",
+ "high": "高",
+ "medium": "中",
+ "low": "低"
+ },
+ "display": {
+ "properties": {
+ "label": "显示属性",
+ "id": "ID",
+ "issue_type": "工作项类型",
+ "sub_issue_count": "子工作项数量",
+ "attachment_count": "附件数量",
+ "created_on": "创建于",
+ "sub_issue": "子工作项"
+ },
+ "extra": {
+ "show_sub_issues": "显示子工作项",
+ "show_empty_groups": "显示空组"
+ }
+ },
+ "layouts": {
+ "ordered_by_label": "此布局按以下方式排序",
+ "list": "列表",
+ "kanban": "看板",
+ "calendar": "日历",
+ "spreadsheet": "表格",
+ "gantt": "时间线",
+ "title": {
+ "list": "列表布局",
+ "kanban": "看板布局",
+ "calendar": "日历布局",
+ "spreadsheet": "表格布局",
+ "gantt": "时间线布局"
+ }
+ },
+ "states": {
+ "active": "活动",
+ "backlog": "待办"
+ },
+ "comments": {
+ "placeholder": "添加评论",
+ "switch": {
+ "private": "切换为私密评论",
+ "public": "切换为公开评论"
+ },
+ "create": {
+ "success": "评论创建成功",
+ "error": "评论创建失败。请稍后重试。"
+ },
+ "update": {
+ "success": "评论更新成功",
+ "error": "评论更新失败。请稍后重试。"
+ },
+ "remove": {
+ "success": "评论删除成功",
+ "error": "评论删除失败。请稍后重试。"
+ },
+ "upload": {
+ "error": "资源上传失败。请稍后重试。"
+ }
+ },
+ "empty_state": {
+ "issue_detail": {
+ "title": "工作项不存在",
+ "description": "您查找的工作项不存在、已归档或已删除。",
+ "primary_button": {
+ "text": "查看其他工作项"
+ }
+ }
+ },
+ "sibling": {
+ "label": "同级工作项"
+ },
+ "archive": {
+ "description": "只有已完成或已取消的\n工作项可以归档",
+ "label": "归档工作项",
+ "confirm_message": "您确定要归档此工作项吗?所有已归档的工作项稍后可以恢复。",
+ "success": {
+ "label": "归档成功",
+ "message": "您的归档可以在项目归档中找到。"
+ },
+ "failed": {
+ "message": "无法归档工作项。请重试。"
+ }
+ },
+ "restore": {
+ "success": {
+ "title": "恢复成功",
+ "message": "您的工作项可以在项目工作项中找到。"
+ },
+ "failed": {
+ "message": "无法恢复工作项。请重试。"
+ }
+ },
+ "relation": {
+ "relates_to": "关联到",
+ "duplicate": "重复于",
+ "blocked_by": "被阻止于",
+ "blocking": "阻止"
+ },
+ "copy_link": "复制工作项链接",
+ "delete": {
+ "label": "删除工作项",
+ "error": "删除工作项时出错"
+ },
+ "subscription": {
+ "actions": {
+ "subscribed": "工作项订阅成功",
+ "unsubscribed": "工作项取消订阅成功"
+ }
+ },
+ "select": {
+ "error": "请至少选择一个工作项",
+ "empty": "未选择工作项",
+ "add_selected": "添加所选工作项"
+ },
+ "open_in_full_screen": "在全屏中打开工作项"
+ },
+
+ "attachment": {
+ "error": "无法附加文件。请重新上传。",
+ "only_one_file_allowed": "一次只能上传一个文件。",
+ "file_size_limit": "文件大小必须小于或等于 {size}MB。",
+ "drag_and_drop": "拖放到任意位置以上传",
+ "delete": "删除附件"
+ },
+
+ "label": {
+ "select": "选择标签",
+ "create": {
+ "success": "标签创建成功",
+ "failed": "标签创建失败",
+ "already_exists": "标签已存在",
+ "type": "输入以添加新标签"
+ }
+ },
+
+ "sub_work_item": {
+ "update": {
+ "success": "子工作项更新成功",
+ "error": "更新子工作项时出错"
+ },
+ "remove": {
+ "success": "子工作项移除成功",
+ "error": "移除子工作项时出错"
+ }
+ },
+
+ "view": {
+ "label": "{count, plural, one {视图} other {视图}}",
+ "create": {
+ "label": "创建视图"
+ },
+ "update": {
+ "label": "更新视图"
+ }
+ },
+
+ "inbox_issue": {
+ "status": {
+ "pending": {
+ "title": "待处理",
+ "description": "待处理"
+ },
+ "declined": {
+ "title": "已拒绝",
+ "description": "已拒绝"
+ },
+ "snoozed": {
+ "title": "已暂停",
+ "description": "还剩{days, plural, one{# 天} other{# 天}}"
+ },
+ "accepted": {
+ "title": "已接受",
+ "description": "已接受"
+ },
+ "duplicate": {
+ "title": "重复",
+ "description": "重复"
+ }
+ },
+ "modals": {
+ "decline": {
+ "title": "拒绝工作项",
+ "content": "您确定要拒绝工作项 {value} 吗?"
+ },
+ "delete": {
+ "title": "删除工作项",
+ "content": "您确定要删除工作项 {value} 吗?",
+ "success": "工作项删除成功"
+ }
+ },
+ "errors": {
+ "snooze_permission": "只有项目管理员可以暂停/取消暂停工作项",
+ "accept_permission": "只有项目管理员可以接受工作项",
+ "decline_permission": "只有项目管理员可以拒绝工作项"
+ },
+ "actions": {
+ "accept": "接受",
+ "decline": "拒绝",
+ "snooze": "暂停",
+ "unsnooze": "取消暂停",
+ "copy": "复制工作项链接",
+ "delete": "删除",
+ "open": "打开工作项",
+ "mark_as_duplicate": "标记为重复",
+ "move": "将 {value} 移至项目工作项"
+ },
+ "source": {
+ "in-app": "应用内"
+ },
+ "order_by": {
+ "created_at": "创建时间",
+ "updated_at": "更新时间",
+ "id": "ID"
+ },
+ "label": "收集",
+ "page_label": "{workspace} - 收集",
+ "modal": {
+ "title": "创建收集工作项"
+ },
+ "tabs": {
+ "open": "未处理",
+ "closed": "已处理"
+ },
+ "empty_state": {
+ "sidebar_open_tab": {
+ "title": "没有未处理的工作项",
+ "description": "在此处查找未处理的工作项。创建新工作项。"
+ },
+ "sidebar_closed_tab": {
+ "title": "没有已处理的工作项",
+ "description": "所有已接受或已拒绝的工作项都可以在这里找到。"
+ },
+ "sidebar_filter": {
+ "title": "没有匹配的工作项",
+ "description": "收集中没有符合筛选条件的工作项。创建新工作项。"
+ },
+ "detail": {
+ "title": "选择一个工作项以查看其详细信息。"
+ }
+ }
+ },
+
+ "workspace_creation": {
+ "heading": "创建您的工作区",
+ "subheading": "要开始使用 Plane,您需要创建或加入一个工作区。",
+ "form": {
+ "name": {
+ "label": "为您的工作区命名",
+ "placeholder": "熟悉且易于识别的名称总是最好的。"
+ },
+ "url": {
+ "label": "设置您的工作区 URL",
+ "placeholder": "输入或粘贴 URL",
+ "edit_slug": "您只能编辑 URL 的标识符部分"
+ },
+ "organization_size": {
+ "label": "有多少人将使用这个工作区?",
+ "placeholder": "选择一个范围"
+ }
+ },
+ "errors": {
+ "creation_disabled": {
+ "title": "只有您的实例管理员可以创建工作区",
+ "description": "如果您知道实例管理员的电子邮件地址,请点击下方按钮与他们联系。",
+ "request_button": "请求实例管理员"
+ },
+ "validation": {
+ "name_alphanumeric": "工作区名称只能包含 (' '), ('-'), ('_') 和字母数字字符。",
+ "name_length": "名称限制在 80 个字符以内。",
+ "url_alphanumeric": "URL 只能包含 ('-') 和字母数字字符。",
+ "url_length": "URL 限制在 48 个字符以内。",
+ "url_already_taken": "工作区 URL 已被占用!"
+ }
+ },
+ "request_email": {
+ "subject": "请求新工作区",
+ "body": "您好,实例管理员:\n\n请为 [创建工作区的目的] 创建一个 URL 为 [/workspace-name] 的新工作区。\n\n谢谢,\n{firstName} {lastName}\n{email}"
+ },
+ "button": {
+ "default": "创建工作区",
+ "loading": "正在创建工作区"
+ },
+ "toast": {
+ "success": {
+ "title": "成功",
+ "message": "工作区创建成功"
+ },
+ "error": {
+ "title": "错误",
+ "message": "工作区创建失败。请重试。"
+ }
+ }
+ },
+
+ "workspace_dashboard": {
+ "empty_state": {
+ "general": {
+ "title": "项目、活动和指标概览",
+ "description": "欢迎使用 Plane,我们很高兴您能来到这里。创建您的第一个项目并跟踪您的工作项,这个页面将转变为帮助您进展的空间。管理员还将看到帮助团队进展的项目。",
+ "primary_button": {
+ "text": "构建您的第一个项目",
+ "comic": {
+ "title": "在 Plane 中一切都从项目开始",
+ "description": "项目可以是产品路线图、营销活动或新车发布。"
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_analytics": {
+ "label": "分析",
+ "page_label": "{workspace} - 分析",
+ "open_tasks": "总开放任务",
+ "error": "获取数据时出现错误。",
+ "work_items_closed_in": "已关闭的工作项",
+ "selected_projects": "已选择的项目",
+ "total_members": "总成员数",
+ "total_cycles": "总周期数",
+ "total_modules": "总模块数",
+ "pending_work_items": {
+ "title": "待处理工作项",
+ "empty_state": "同事的待处理工作项分析将显示在这里。"
+ },
+ "work_items_closed_in_a_year": {
+ "title": "一年内关闭的工作项",
+ "empty_state": "关闭工作项以查看以图表形式显示的分析。"
+ },
+ "most_work_items_created": {
+ "title": "创建最多工作项",
+ "empty_state": "同事及其创建的工作项数量将显示在这里。"
+ },
+ "most_work_items_closed": {
+ "title": "关闭最多工作项",
+ "empty_state": "同事及其关闭的工作项数量将显示在这里。"
+ },
+ "tabs": {
+ "scope_and_demand": "范围和需求",
+ "custom": "自定义分析"
+ },
+ "empty_state": {
+ "general": {
+ "title": "跟踪进度、工作量和分配。发现趋势、消除障碍并加快工作进度",
+ "description": "查看范围与需求、估算和范围蔓延。获取团队成员和团队的表现,确保您的项目按时运行。",
+ "primary_button": {
+ "text": "开始您的第一个项目",
+ "comic": {
+ "title": "分析在周期 + 模块中效果最佳",
+ "description": "首先,将您的工作项限定在周期中,如果可能的话,将跨越多个周期的工作项分组到模块中。在左侧导航栏中查看这两项。"
+ }
+ }
+ }
+ }
+ },
+
+ "workspace_projects": {
+ "label": "{count, plural, one {项目} other {项目}}",
+ "create": {
+ "label": "添加项目"
+ },
+ "network": {
+ "private": {
+ "title": "私有",
+ "description": "仅限邀请访问"
+ },
+ "public": {
+ "title": "公开",
+ "description": "工作区中除访客外的任何人都可以加入"
+ }
+ },
+ "error": {
+ "permission": "您没有执行此操作的权限。",
+ "cycle_delete": "删除周期失败",
+ "module_delete": "删除模块失败",
+ "issue_delete": "删除工作项失败"
+ },
+ "state": {
+ "backlog": "待办",
+ "unstarted": "未开始",
+ "started": "进行中",
+ "completed": "已完成",
+ "cancelled": "已取消"
+ },
+ "sort": {
+ "manual": "手动",
+ "name": "名称",
+ "created_at": "创建日期",
+ "members_length": "成员数量"
+ },
+ "scope": {
+ "my_projects": "我的项目",
+ "archived_projects": "已归档"
+ },
+ "common": {
+ "months_count": "{months, plural, one{# 个月} other{# 个月}}"
+ },
+ "empty_state": {
+ "general": {
+ "title": "没有活动项目",
+ "description": "将每个项目视为目标导向工作的父级。项目是工作项、周期和模块所在的地方,与您的同事一起帮助您实现目标。创建新项目或筛选已归档的项目。",
+ "primary_button": {
+ "text": "开始您的第一个项目",
+ "comic": {
+ "title": "在 Plane 中一切都从项目开始",
+ "description": "项目可以是产品路线图、营销活动或新车发布。"
+ }
+ }
+ },
+ "no_projects": {
+ "title": "没有项目",
+ "description": "要创建工作项或管理您的工作,您需要创建一个项目或成为项目的一部分。",
+ "primary_button": {
+ "text": "开始您的第一个项目",
+ "comic": {
+ "title": "在 Plane 中一切都从项目开始",
+ "description": "项目可以是产品路线图、营销活动或新车发布。"
+ }
+ }
+ },
+ "filter": {
+ "title": "没有匹配的项目",
+ "description": "未检测到符合匹配条件的项目。\n创建一个新项目。"
+ },
+ "search": {
+ "description": "未检测到符合匹配条件的项目。\n创建一个新项目"
+ }
+ }
+ },
+
+ "workspace_views": {
+ "add_view": "添加视图",
+ "empty_state": {
+ "all-issues": {
+ "title": "项目中没有工作项",
+ "description": "第一个项目完成!现在,将您的工作分解成可跟踪的工作项。让我们开始吧!",
+ "primary_button": {
+ "text": "创建新工作项"
+ }
+ },
+ "assigned": {
+ "title": "还没有工作项",
+ "description": "可以在这里跟踪分配给您的工作项。",
+ "primary_button": {
+ "text": "创建新工作项"
+ }
+ },
+ "created": {
+ "title": "还没有工作项",
+ "description": "您创建的所有工作项都会出现在这里,直接在这里跟踪它们。",
+ "primary_button": {
+ "text": "创建新工作项"
+ }
+ },
+ "subscribed": {
+ "title": "还没有工作项",
+ "description": "订阅您感兴趣的工作项,在这里跟踪所有这些工作项。"
+ },
+ "custom-view": {
+ "title": "还没有工作项",
+ "description": "符合筛选条件的工作项,在这里跟踪所有这些工作项。"
+ }
+ }
+ },
+
+ "workspace_settings": {
+ "label": "工作区设置",
+ "page_label": "{workspace} - 常规设置",
+ "key_created": "密钥已创建",
+ "copy_key": "复制并将此密钥保存在 Plane Pages 中。关闭后您将无法看到此密钥。包含密钥的 CSV 文件已下载。",
+ "token_copied": "令牌已复制到剪贴板。",
+ "settings": {
+ "general": {
+ "title": "常规",
+ "upload_logo": "上传标志",
+ "edit_logo": "编辑标志",
+ "name": "工作区名称",
+ "company_size": "公司规模",
+ "url": "工作区网址",
+ "update_workspace": "更新工作区",
+ "delete_workspace": "删除此工作区",
+ "delete_workspace_description": "删除工作区时,该工作区内的所有数据和资源将被永久删除,且无法恢复。",
+ "delete_btn": "删除此工作区",
+ "delete_modal": {
+ "title": "确定要删除此工作区吗?",
+ "description": "您目前正在试用我们的付费方案。请先取消试用后再继续。",
+ "dismiss": "关闭",
+ "cancel": "取消试用",
+ "success_title": "工作区已删除。",
+ "success_message": "即将跳转到您的个人资料页面。",
+ "error_title": "操作失败。",
+ "error_message": "请重试。"
+ },
+ "errors": {
+ "name": {
+ "required": "名称为必填项",
+ "max_length": "工作区名称不应超过80个字符"
+ },
+ "company_size": {
+ "required": "公司规模为必填项"
+ }
+ }
+ },
+ "members": {
+ "title": "成员",
+ "add_member": "添加成员",
+ "pending_invites": "待处理邀请",
+ "invitations_sent_successfully": "邀请发送成功",
+ "leave_confirmation": "您确定要离开工作区吗?您将无法再访问此工作区。此操作无法撤消。",
+ "details": {
+ "full_name": "全名",
+ "display_name": "显示名称",
+ "email_address": "电子邮件地址",
+ "account_type": "账户类型",
+ "authentication": "身份验证",
+ "joining_date": "加入日期"
+ },
+ "modal": {
+ "title": "邀请人员协作",
+ "description": "邀请人员在您的工作区中协作。",
+ "button": "发送邀请",
+ "button_loading": "正在发送邀请",
+ "placeholder": "name@company.com",
+ "errors": {
+ "required": "我们需要一个电子邮件地址来邀请他们。",
+ "invalid": "电子邮件无效"
+ }
+ }
+ },
+ "billing_and_plans": {
+ "title": "账单与计划",
+ "current_plan": "当前计划",
+ "free_plan": "您目前使用的是免费计划",
+ "view_plans": "查看计划"
+ },
+ "exports": {
+ "title": "导出",
+ "exporting": "导出中",
+ "previous_exports": "以前的导出",
+ "export_separate_files": "将数据导出为单独的文件",
+ "modal": {
+ "title": "导出到",
+ "toasts": {
+ "success": {
+ "title": "导出成功",
+ "message": "您可以从之前的导出中下载导出的{entity}"
+ },
+ "error": {
+ "title": "导出失败",
+ "message": "导出未成功。请重试。"
+ }
+ }
+ }
+ },
+ "webhooks": {
+ "title": "Webhooks",
+ "add_webhook": "添加 webhook",
+ "modal": {
+ "title": "创建 webhook",
+ "details": "Webhook 详情",
+ "payload": "负载 URL",
+ "question": "您希望触发此 webhook 的事件有哪些?",
+ "error": "URL 为必填项"
+ },
+ "secret_key": {
+ "title": "密钥",
+ "message": "生成令牌以登录 webhook 负载"
+ },
+ "options": {
+ "all": "发送所有内容",
+ "individual": "选择单个事件"
+ },
+ "toasts": {
+ "created": {
+ "title": "Webhook 已创建",
+ "message": "Webhook 已成功创建"
+ },
+ "not_created": {
+ "title": "Webhook 未创建",
+ "message": "无法创建 webhook"
+ },
+ "updated": {
+ "title": "Webhook 已更新",
+ "message": "Webhook 已成功更新"
+ },
+ "not_updated": {
+ "title": "Webhook 未更新",
+ "message": "无法更新 webhook"
+ },
+ "removed": {
+ "title": "Webhook 已移除",
+ "message": "Webhook 已成功移除"
+ },
+ "not_removed": {
+ "title": "Webhook 未移除",
+ "message": "无法移除 webhook"
+ },
+ "secret_key_copied": {
+ "message": "密钥已复制到剪贴板。"
+ },
+ "secret_key_not_copied": {
+ "message": "复制密钥时出错。"
+ }
+ }
+ },
+ "api_tokens": {
+ "title": "API 令牌",
+ "add_token": "添加 API 令牌",
+ "create_token": "创建令牌",
+ "never_expires": "永不过期",
+ "generate_token": "生成令牌",
+ "generating": "生成中",
+ "delete": {
+ "title": "删除 API 令牌",
+ "description": "使用此令牌的任何应用程序将无法再访问 Plane 数据。此操作无法撤消。",
+ "success": {
+ "title": "成功!",
+ "message": "API 令牌已成功删除"
+ },
+ "error": {
+ "title": "错误!",
+ "message": "无法删除 API 令牌"
+ }
+ }
+ }
+ },
+ "empty_state": {
+ "api_tokens": {
+ "title": "尚未创建 API 令牌",
+ "description": "Plane API 可用于将您在 Plane 中的数据与任何外部系统集成。创建令牌以开始使用。"
+ },
+ "webhooks": {
+ "title": "尚未添加 webhook",
+ "description": "创建 webhook 以接收实时更新并自动执行操作。"
+ },
+ "exports": {
+ "title": "尚无导出",
+ "description": "每次导出时,您都会在这里有一个副本以供参考。"
+ },
+ "imports": {
+ "title": "尚无导入",
+ "description": "在这里查找所有以前的导入并下载它们。"
+ }
+ }
+ },
+
+ "profile": {
+ "label": "个人资料",
+ "page_label": "您的工作",
+ "work": "工作",
+ "details": {
+ "joined_on": "加入时间",
+ "time_zone": "时区"
+ },
+ "stats": {
+ "workload": "工作量",
+ "overview": "概览",
+ "created": "已创建的工作项",
+ "assigned": "已分配的工作项",
+ "subscribed": "已订阅的工作项",
+ "state_distribution": {
+ "title": "按状态分类的工作项",
+ "empty": "创建工作项以在图表中查看按状态分类的工作项,以便更好地分析。"
+ },
+ "priority_distribution": {
+ "title": "按优先级分类的工作项",
+ "empty": "创建工作项以在图表中查看按优先级分类的工作项,以便更好地分析。"
+ },
+ "recent_activity": {
+ "title": "最近活动",
+ "empty": "我们找不到数据。请查看您的输入",
+ "button": "下载今天的活动",
+ "button_loading": "下载中"
+ }
+ },
+ "actions": {
+ "profile": "个人资料",
+ "security": "安全",
+ "activity": "活动",
+ "appearance": "外观",
+ "notifications": "通知"
+ },
+ "tabs": {
+ "summary": "摘要",
+ "assigned": "已分配",
+ "created": "已创建",
+ "subscribed": "已订阅",
+ "activity": "活动"
+ },
+ "empty_state": {
+ "activity": {
+ "title": "尚无活动",
+ "description": "通过创建新工作项开始!为其添加详细信息和属性。在 Plane 中探索更多内容以查看您的活动。"
+ },
+ "assigned": {
+ "title": "没有分配给您的工作项",
+ "description": "可以从这里跟踪分配给您的工作项。"
+ },
+ "created": {
+ "title": "尚无工作项",
+ "description": "您创建的所有工作项都会出现在这里,直接在这里跟踪它们。"
+ },
+ "subscribed": {
+ "title": "尚无工作项",
+ "description": "订阅您感兴趣的工作项,在这里跟踪所有这些工作项。"
+ }
+ }
+ },
+
+ "project_settings": {
+ "general": {
+ "enter_project_id": "输入项目 ID",
+ "please_select_a_timezone": "请选择时区",
+ "archive_project": {
+ "title": "归档项目",
+ "description": "归档项目将从您的侧边导航中取消列出您的项目,但您仍然可以从项目页面访问它。您可以随时恢复或删除项目。",
+ "button": "归档项目"
+ },
+ "delete_project": {
+ "title": "删除项目",
+ "description": "删除项目时,该项目内的所有数据和资源将被永久删除且无法恢复。",
+ "button": "删除我的项目"
+ },
+ "toast": {
+ "success": "项目更新成功",
+ "error": "项目无法更新。请重试。"
+ }
+ },
+ "members": {
+ "label": "成员",
+ "project_lead": "项目负责人",
+ "default_assignee": "默认受理人",
+ "guest_super_permissions": {
+ "title": "为访客用户授予查看所有工作项的权限:",
+ "sub_heading": "这将允许访客查看所有项目工作项。"
+ },
+ "invite_members": {
+ "title": "邀请成员",
+ "sub_heading": "邀请成员参与您的项目。",
+ "select_co_worker": "选择同事"
+ }
+ },
+ "states": {
+ "describe_this_state_for_your_members": "为您的成员描述此状态。",
+ "empty_state": {
+ "title": "{groupKey} 组中没有状态",
+ "description": "请创建一个新状态"
+ }
+ },
+ "labels": {
+ "label_title": "标签标题",
+ "label_title_is_required": "标签标题为必填项",
+ "label_max_char": "标签名称不应超过255个字符",
+ "toast": {
+ "error": "更新标签时出错"
+ }
+ },
+ "estimates": {
+ "title": "为我的项目启用估算",
+ "description": "它们有助于您传达团队的复杂性和工作量。"
+ },
+ "automations": {
+ "label": "自动化",
+ "auto-archive": {
+ "title": "自动归档已关闭的工作项",
+ "description": "Plane 将自动归档已完成或已取消的工作项。",
+ "duration": "自动归档已关闭"
+ },
+ "auto-close": {
+ "title": "自动关闭工作项",
+ "description": "Plane 将自动关闭尚未完成或取消的工作项。",
+ "duration": "自动关闭不活跃",
+ "auto_close_status": "自动关闭状态"
+ }
+ },
+
+ "empty_state": {
+ "labels": {
+ "title": "尚无标签",
+ "description": "创建标签以帮助组织和筛选项目中的工作项。"
+ },
+ "estimates": {
+ "title": "尚无估算系统",
+ "description": "创建一组估算以传达每个工作项的工作量。",
+ "primary_button": "添加估算系统"
+ }
+ }
+ },
+
+ "project_cycles": {
+ "add_cycle": "添加周期",
+ "more_details": "更多详情",
+ "cycle": "周期",
+ "update_cycle": "更新周期",
+ "create_cycle": "创建周期",
+ "no_matching_cycles": "没有匹配的周期",
+ "remove_filters_to_see_all_cycles": "移除筛选器以查看所有周期",
+ "remove_search_criteria_to_see_all_cycles": "移除搜索条件以查看所有周期",
+ "only_completed_cycles_can_be_archived": "只能归档已完成的周期",
+ "active_cycle": {
+ "label": "活动周期",
+ "progress": "进度",
+ "chart": "燃尽图",
+ "priority_issue": "优先工作项",
+ "assignees": "受理人",
+ "issue_burndown": "工作项燃尽",
+ "ideal": "理想",
+ "current": "当前",
+ "labels": "标签"
+ },
+ "upcoming_cycle": {
+ "label": "即将到来的周期"
+ },
+ "completed_cycle": {
+ "label": "已完成的周期"
+ },
+ "status": {
+ "days_left": "剩余天数",
+ "completed": "已完成",
+ "yet_to_start": "尚未开始",
+ "in_progress": "进行中",
+ "draft": "草稿"
+ },
+ "action": {
+ "restore": {
+ "title": "恢复周期",
+ "success": {
+ "title": "周期已恢复",
+ "description": "周期已被恢复。"
+ },
+ "failed": {
+ "title": "周期恢复失败",
+ "description": "无法恢复周期。请重试。"
+ }
+ },
+ "favorite": {
+ "loading": "正在将周期添加到收藏",
+ "success": {
+ "description": "周期已添加到收藏。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "无法将周期添加到收藏。请重试。",
+ "title": "错误!"
+ }
+ },
+ "unfavorite": {
+ "loading": "正在从收藏中移除周期",
+ "success": {
+ "description": "周期已从收藏中移除。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "无法从收藏中移除周期。请重试。",
+ "title": "错误!"
+ }
+ },
+ "update": {
+ "loading": "正在更新周期",
+ "success": {
+ "description": "周期更新成功。",
+ "title": "成功!"
+ },
+ "failed": {
+ "description": "更新周期时出错。请重试。",
+ "title": "错误!"
+ },
+ "error": {
+ "already_exists": "在给定日期范围内已存在周期,如果您想创建草稿周期,可以通过移除两个日期来实现。"
+ }
+ }
+ },
+ "empty_state": {
+ "general": {
+ "title": "在周期中分组和时间框定您的工作。",
+ "description": "将工作按时间框分解,从项目截止日期倒推设置日期,并作为团队取得切实的进展。",
+ "primary_button": {
+ "text": "设置您的第一个周期",
+ "comic": {
+ "title": "周期是重复的时间框。",
+ "description": "冲刺、迭代或您用于每周或每两周跟踪工作的任何其他术语都是一个周期。"
+ }
+ }
+ },
+ "no_issues": {
+ "title": "尚未向周期添加工作项",
+ "description": "添加或创建您希望在此周期内时间框定和交付的工作项",
+ "primary_button": {
+ "text": "创建新工作项"
+ },
+ "secondary_button": {
+ "text": "添加现有工作项"
+ }
+ },
+ "completed_no_issues": {
+ "title": "周期中没有工作项",
+ "description": "周期中没有工作项。工作项已被转移或隐藏。要查看隐藏的工作项(如果有),请相应更新您的显示属性。"
+ },
+ "active": {
+ "title": "没有活动周期",
+ "description": "活动周期包括其范围内包含今天日期的任何时期。在这里查找活动周期的进度和详细信息。"
+ },
+ "archived": {
+ "title": "尚无已归档的周期",
+ "description": "为了整理您的项目,归档已完成的周期。归档后可以在这里找到它们。"
+ }
+ }
+ },
+
+ "project_issues": {
+ "empty_state": {
+ "no_issues": {
+ "title": "创建工作项并将其分配给某人,甚至是您自己",
+ "description": "将工作项视为工作、任务或待完成的工作。工作项及其子工作项通常是基于时间的、分配给团队成员的可执行项。您的团队通过创建、分配和完成工作项来推动项目实现其目标。",
+ "primary_button": {
+ "text": "创建您的第一个工作项",
+ "comic": {
+ "title": "工作项是 Plane 中的基本构建块。",
+ "description": "重新设计 Plane 界面、重塑公司品牌或启动新的燃料喷射系统都是可能包含子工作项的工作项示例。"
+ }
+ }
+ },
+ "no_archived_issues": {
+ "title": "尚无已归档的工作项",
+ "description": "通过手动或自动化方式,您可以归档已完成或已取消的工作项。归档后可以在这里找到它们。",
+ "primary_button": {
+ "text": "设置自动化"
+ }
+ },
+ "issues_empty_filter": {
+ "title": "未找到符合筛选条件的工作项",
+ "secondary_button": {
+ "text": "清除所有筛选条件"
+ }
+ }
+ }
+ },
+
+ "project_module": {
+ "add_module": "添加模块",
+ "update_module": "更新模块",
+ "create_module": "创建模块",
+ "archive_module": "归档模块",
+ "restore_module": "恢复模块",
+ "delete_module": "删除模块",
+ "empty_state": {
+ "general": {
+ "title": "将项目里程碑映射到模块,轻松跟踪汇总工作。",
+ "description": "属于逻辑层次结构父级的一组工作项形成一个模块。将其视为按项目里程碑跟踪工作的方式。它们有自己的周期和截止日期以及分析功能,帮助您了解距离里程碑的远近。",
+ "primary_button": {
+ "text": "构建您的第一个模块",
+ "comic": {
+ "title": "模块帮助按层次结构对工作进行分组。",
+ "description": "购物车模块、底盘模块和仓库模块都是这种分组的好例子。"
+ }
+ }
+ },
+ "no_issues": {
+ "title": "模块中没有工作项",
+ "description": "创建或添加您想作为此模块一部分完成的工作项",
+ "primary_button": {
+ "text": "创建新工作项"
+ },
+ "secondary_button": {
+ "text": "添加现有工作项"
+ }
+ },
+ "archived": {
+ "title": "尚无已归档的模块",
+ "description": "为了整理您的项目,归档已完成或已取消的模块。归档后可以在这里找到它们。"
+ },
+ "sidebar": {
+ "in_active": "此模块尚未激活。",
+ "invalid_date": "日期无效。请输入有效日期。"
+ }
+ },
+ "quick_actions": {
+ "archive_module": "归档模块",
+ "archive_module_description": "只有已完成或已取消的\n模块可以归档。",
+ "delete_module": "删除模块"
+ },
+ "toast": {
+ "copy": {
+ "success": "模块链接已复制到剪贴板"
+ },
+ "delete": {
+ "success": "模块删除成功",
+ "error": "删除模块失败"
+ }
+ }
+ },
+
+ "project_views": {
+ "empty_state": {
+ "general": {
+ "title": "为您的项目保存筛选视图。根据需要创建任意数量",
+ "description": "视图是您经常使用或想要轻松访问的一组已保存的筛选条件。项目中的所有同事都可以看到每个人的视图,并选择最适合他们需求的视图。",
+ "primary_button": {
+ "text": "创建您的第一个视图",
+ "comic": {
+ "title": "视图基于工作项属性运作。",
+ "description": "您可以在此处创建一个视图,根据需要使用任意数量的属性作为筛选条件。"
+ }
+ }
+ },
+ "filter": {
+ "title": "没有匹配的视图",
+ "description": "没有符合搜索条件的视图。\n创建一个新视图。"
+ }
+ }
+ },
+
+ "project_page": {
+ "empty_state": {
+ "general": {
+ "title": "写笔记、文档或完整的知识库。让 Plane 的 AI 助手 Galileo 帮助您开始",
+ "description": "页面是 Plane 中的思维记录空间。记录会议笔记,轻松格式化,嵌入工作项,使用组件库进行布局,并将它们全部保存在项目上下文中。要快速完成任何文档,可以通过快捷键或点击按钮调用 Plane 的 AI Galileo。",
+ "primary_button": {
+ "text": "创建您的第一个页面"
+ }
+ },
+ "private": {
+ "title": "尚无私人页面",
+ "description": "在这里保存您的私人想法。准备好分享时,团队就在一键之遥。",
+ "primary_button": {
+ "text": "创建您的第一个页面"
+ }
+ },
+ "public": {
+ "title": "尚无公共页面",
+ "description": "在这里查看与项目中所有人共享的页面。",
+ "primary_button": {
+ "text": "创建您的第一个页面"
+ }
+ },
+ "archived": {
+ "title": "尚无已归档的页面",
+ "description": "归档不在您关注范围内的页面。需要时可以在这里访问它们。"
+ }
+ }
+ },
+
+ "command_k": {
+ "empty_state": {
+ "search": {
+ "title": "未找到结果"
+ }
+ }
+ },
+
+ "issue_relation": {
+ "empty_state": {
+ "search": {
+ "title": "未找到匹配的工作项"
+ },
+ "no_issues": {
+ "title": "未找到工作项"
+ }
+ }
+ },
+
+ "issue_comment": {
+ "empty_state": {
+ "general": {
+ "title": "尚无评论",
+ "description": "评论可用作工作项的讨论和跟进空间"
+ }
+ }
+ },
+
+ "notification": {
+ "label": "收件箱",
+ "page_label": "{workspace} - 收件箱",
+ "options": {
+ "mark_all_as_read": "全部标记为已读",
+ "mark_read": "标记为已读",
+ "mark_unread": "标记为未读",
+ "refresh": "刷新",
+ "filters": "收件箱筛选",
+ "show_unread": "显示未读",
+ "show_snoozed": "显示已暂停",
+ "show_archived": "显示已归档",
+ "mark_archive": "归档",
+ "mark_unarchive": "取消归档",
+ "mark_snooze": "暂停",
+ "mark_unsnooze": "取消暂停"
+ },
+ "toasts": {
+ "read": "通知已标记为已读",
+ "unread": "通知已标记为未读",
+ "archived": "通知已标记为已归档",
+ "unarchived": "通知已标记为未归档",
+ "snoozed": "通知已暂停",
+ "unsnoozed": "通知已取消暂停"
+ },
+ "empty_state": {
+ "detail": {
+ "title": "选择以查看详情。"
+ },
+ "all": {
+ "title": "没有分配的工作项",
+ "description": "在这里可以看到分配给您的工作项的更新"
+ },
+ "mentions": {
+ "title": "没有分配的工作项",
+ "description": "在这里可以看到分配给您的工作项的更新"
+ }
+ },
+ "tabs": {
+ "all": "全部",
+ "mentions": "提及"
+ },
+ "filter": {
+ "assigned": "分配给我",
+ "created": "由我创建",
+ "subscribed": "由我订阅"
+ },
+ "snooze": {
+ "1_day": "1 天",
+ "3_days": "3 天",
+ "5_days": "5 天",
+ "1_week": "1 周",
+ "2_weeks": "2 周",
+ "custom": "自定义"
+ }
+ },
+
+ "active_cycle": {
+ "empty_state": {
+ "progress": {
+ "title": "向周期添加工作项以查看其进度"
+ },
+ "chart": {
+ "title": "向周期添加工作项以查看燃尽图。"
+ },
+ "priority_issue": {
+ "title": "一目了然地观察周期中处理的高优先级工作项。"
+ },
+ "assignee": {
+ "title": "为工作项添加负责人以查看按负责人划分的工作明细。"
+ },
+ "label": {
+ "title": "为工作项添加标签以查看按标签划分的工作明细。"
+ }
+ }
+ },
+
+ "disabled_project": {
+ "empty_state": {
+ "inbox": {
+ "title": "项目未启用收集功能。",
+ "description": "收集功能帮助您管理项目的传入请求,并将其添加为工作流中的工作项。从项目设置启用收集功能以管理请求。",
+ "primary_button": {
+ "text": "管理功能"
+ }
+ },
+ "cycle": {
+ "title": "此项目未启用周期功能。",
+ "description": "按时间框将工作分解,从项目截止日期倒推设置日期,并作为团队取得切实的进展。为您的项目启用周期功能以开始使用它们。",
+ "primary_button": {
+ "text": "管理功能"
+ }
+ },
+ "module": {
+ "title": "项目未启用模块功能。",
+ "description": "模块是项目的基本构建块。从项目设置启用模块以开始使用它们。",
+ "primary_button": {
+ "text": "管理功能"
+ }
+ },
+ "page": {
+ "title": "项目未启用页面功能。",
+ "description": "页面是项目的基本构建块。从项目设置启用页面以开始使用它们。",
+ "primary_button": {
+ "text": "管理功能"
+ }
+ },
+ "view": {
+ "title": "项目未启用视图功能。",
+ "description": "视图是项目的基本构建块。从项目设置启用视图以开始使用它们。",
+ "primary_button": {
+ "text": "管理功能"
+ }
+ }
+ }
+ },
+ "workspace_draft_issues": {
+ "draft_an_issue": "起草工作项",
+ "empty_state": {
+ "title": "半写的工作项,以及即将推出的评论将在这里显示。",
+ "description": "要试用此功能,请开始添加工作项并中途离开,或在下方创建您的第一个草稿。😉",
+ "primary_button": {
+ "text": "创建您的第一个草稿"
+ }
+ },
+ "delete_modal": {
+ "title": "删除草稿",
+ "description": "您确定要删除此草稿吗?此操作无法撤消。"
+ },
+ "toasts": {
+ "created": {
+ "success": "草稿已创建",
+ "error": "无法创建工作项。请重试。"
+ },
+ "deleted": {
+ "success": "草稿已删除"
+ }
+ }
+ },
+
+ "stickies": {
+ "title": "您的便签",
+ "placeholder": "点击此处输入",
+ "all": "所有便签",
+ "no-data": "记下一个想法,捕捉一个灵感,或记录一个突发奇想。添加便签开始使用。",
+ "add": "添加便签",
+ "search_placeholder": "按标题搜索",
+ "delete": "删除便签",
+ "delete_confirmation": "您确定要删除此便签吗?",
+ "empty_state": {
+ "simple": "记下一个想法,捕捉一个灵感,或记录一个突发奇想。添加便签开始使用。",
+ "general": {
+ "title": "便签是您随手记下的快速笔记和待办事项。",
+ "description": "通过创建随时随地都可以访问的便签,轻松捕捉您的想法和创意。",
+ "primary_button": {
+ "text": "添加便签"
+ }
+ },
+ "search": {
+ "title": "这与您的任何便签都不匹配。",
+ "description": "尝试使用不同的术语,或如果您确定\n搜索是正确的,请告诉我们。",
+ "primary_button": {
+ "text": "添加便签"
+ }
+ }
+ },
+ "toasts": {
+ "errors": {
+ "wrong_name": "便签名称不能超过100个字符。",
+ "already_exists": "已存在一个没有描述的便签"
+ },
+ "created": {
+ "title": "便签已创建",
+ "message": "便签已成功创建"
+ },
+ "not_created": {
+ "title": "便签未创建",
+ "message": "无法创建便签"
+ },
+ "updated": {
+ "title": "便签已更新",
+ "message": "便签已成功更新"
+ },
+ "not_updated": {
+ "title": "便签未更新",
+ "message": "无法更新便签"
+ },
+ "removed": {
+ "title": "便签已移除",
+ "message": "便签已成功移除"
+ },
+ "not_removed": {
+ "title": "便签未移除",
+ "message": "无法移除便签"
+ }
+ }
+ },
+
+ "role_details": {
+ "guest": {
+ "title": "访客",
+ "description": "组织的外部成员可以被邀请为访客。"
+ },
+ "member": {
+ "title": "成员",
+ "description": "可以在项目、周期和模块内读取、写入、编辑和删除实体"
+ },
+ "admin": {
+ "title": "管理员",
+ "description": "在工作区内所有权限均设置为允许。"
+ }
+ },
+
+ "user_roles": {
+ "product_or_project_manager": "产品/项目经理",
+ "development_or_engineering": "开发/工程",
+ "founder_or_executive": "创始人/高管",
+ "freelancer_or_consultant": "自由职业者/顾问",
+ "marketing_or_growth": "市场/增长",
+ "sales_or_business_development": "销售/业务发展",
+ "support_or_operations": "支持/运营",
+ "student_or_professor": "学生/教授",
+ "human_resources": "人力资源",
+ "other": "其他"
+ },
+
+ "importer": {
+ "github": {
+ "title": "GitHub",
+ "description": "从 GitHub 仓库导入工作项并同步。"
+ },
+ "jira": {
+ "title": "Jira",
+ "description": "从 Jira 项目和史诗导入工作项和史诗。"
+ }
+ },
+
+ "exporter": {
+ "csv": {
+ "title": "CSV",
+ "description": "将工作项导出为 CSV 文件。",
+ "short_description": "导出为 CSV"
+ },
+ "excel": {
+ "title": "Excel",
+ "description": "将工作项导出为 Excel 文件。",
+ "short_description": "导出为 Excel"
+ },
+ "xlsx": {
+ "title": "Excel",
+ "description": "将工作项导出为 Excel 文件。",
+ "short_description": "导出为 Excel"
+ },
+ "json": {
+ "title": "JSON",
+ "description": "将工作项导出为 JSON 文件。",
+ "short_description": "导出为 JSON"
+ }
+ },
+ "default_global_view": {
+ "all_issues": "所有工作项",
+ "assigned": "已分配",
+ "created": "已创建",
+ "subscribed": "已订阅"
+ },
+
+ "themes": {
+ "theme_options": {
+ "system_preference": {
+ "label": "系统偏好"
+ },
+ "light": {
+ "label": "浅色"
+ },
+ "dark": {
+ "label": "深色"
+ },
+ "light_contrast": {
+ "label": "浅色高对比度"
+ },
+ "dark_contrast": {
+ "label": "深色高对比度"
+ },
+ "custom": {
+ "label": "自定义主题"
+ }
+ }
+ },
+ "project_modules": {
+ "status": {
+ "backlog": "待办",
+ "planned": "已计划",
+ "in_progress": "进行中",
+ "paused": "已暂停",
+ "completed": "已完成",
+ "cancelled": "已取消"
+ },
+ "layout": {
+ "list": "列表布局",
+ "board": "画廊布局",
+ "timeline": "时间线布局"
+ },
+ "order_by": {
+ "name": "名称",
+ "progress": "进度",
+ "issues": "工作项数量",
+ "due_date": "截止日期",
+ "created_at": "创建日期",
+ "manual": "手动"
+ }
+ },
+
+ "cycle": {
+ "label": "{count, plural, one {周期} other {周期}}",
+ "no_cycle": "无周期"
+ },
+
+ "module": {
+ "label": "{count, plural, one {模块} other {模块}}",
+ "no_module": "无模块"
+ }
+}
diff --git a/packages/i18n/src/store/index.ts b/packages/i18n/src/store/index.ts
new file mode 100644
index 000000000..78e1311ad
--- /dev/null
+++ b/packages/i18n/src/store/index.ts
@@ -0,0 +1,298 @@
+import IntlMessageFormat from "intl-messageformat";
+import get from "lodash/get";
+import merge from "lodash/merge";
+import { makeAutoObservable, runInAction } from "mobx";
+// constants
+import { FALLBACK_LANGUAGE, SUPPORTED_LANGUAGES, STORAGE_KEY } from "../constants";
+// core translations imports
+import coreEn from "../locales/en/core.json";
+// types
+import { TLanguage, ILanguageOption, ITranslations } from "../types";
+
+/**
+ * Mobx store class for handling translations and language changes in the application
+ * Provides methods to translate keys with params and change the language
+ * Uses IntlMessageFormat to format the translations
+ */
+export class TranslationStore {
+ // Core translations that are always loaded
+ private coreTranslations: ITranslations = {
+ en: coreEn,
+ };
+ // List of translations for each language
+ private translations: ITranslations = {};
+ // Cache for IntlMessageFormat instances
+ private messageCache: Map = new Map();
+ // Current language
+ currentLocale: TLanguage = FALLBACK_LANGUAGE;
+ // Loading state
+ isLoading: boolean = true;
+ isInitialized: boolean = false;
+ // Set of loaded languages
+ private loadedLanguages: Set = new Set();
+
+ /**
+ * Constructor for the TranslationStore class
+ */
+ constructor() {
+ makeAutoObservable(this);
+ // Initialize with core translations immediately
+ this.translations = this.coreTranslations;
+ // Initialize language
+ this.initializeLanguage();
+ // Load all the translations
+ this.loadTranslations();
+ }
+
+ /** Initializes the language based on the local storage or browser language */
+ private initializeLanguage() {
+ if (typeof window === "undefined") return;
+
+ const savedLocale = localStorage.getItem(STORAGE_KEY) as TLanguage;
+ if (this.isValidLanguage(savedLocale)) {
+ this.setLanguage(savedLocale);
+ return;
+ }
+
+ const browserLang = this.getBrowserLanguage();
+ this.setLanguage(browserLang);
+ }
+
+ /** Loads the translations for the current language */
+ private async loadTranslations(): Promise {
+ try {
+ // Set initialized to true (Core translations are already loaded)
+ runInAction(() => {
+ this.isInitialized = true;
+ });
+ // Load current and fallback languages in parallel
+ await this.loadPrimaryLanguages();
+ // Load all remaining languages in parallel
+ this.loadRemainingLanguages();
+ } catch (error) {
+ console.error("Failed in translation initialization:", error);
+ runInAction(() => {
+ this.isLoading = false;
+ });
+ }
+ }
+
+ private async loadPrimaryLanguages(): Promise {
+ try {
+ // Load current and fallback languages in parallel
+ const languagesToLoad = new Set([this.currentLocale]);
+ // Add fallback language only if different from current
+ if (this.currentLocale !== FALLBACK_LANGUAGE) {
+ languagesToLoad.add(FALLBACK_LANGUAGE);
+ }
+ // Load all primary languages in parallel
+ const loadPromises = Array.from(languagesToLoad).map((lang) => this.loadLanguageTranslations(lang));
+ await Promise.all(loadPromises);
+ // Update loading state
+ runInAction(() => {
+ this.isLoading = false;
+ });
+ } catch (error) {
+ console.error("Failed to load primary languages:", error);
+ runInAction(() => {
+ this.isLoading = false;
+ });
+ }
+ }
+
+ private loadRemainingLanguages(): void {
+ const remainingLanguages = SUPPORTED_LANGUAGES.map((lang) => lang.value).filter(
+ (lang) =>
+ !this.loadedLanguages.has(lang as TLanguage) && lang !== this.currentLocale && lang !== FALLBACK_LANGUAGE
+ );
+ // Load all remaining languages in parallel
+ Promise.all(remainingLanguages.map((lang) => this.loadLanguageTranslations(lang as TLanguage))).catch((error) => {
+ console.error("Failed to load some remaining languages:", error);
+ });
+ }
+
+ private async loadLanguageTranslations(language: TLanguage): Promise {
+ // Skip if already loaded
+ if (this.loadedLanguages.has(language)) return;
+
+ try {
+ const translations = await this.importLanguageFile(language);
+ runInAction(() => {
+ // Use lodash merge for deep merging
+ this.translations[language] = merge({}, this.coreTranslations[language] || {}, translations.default);
+ // Add to loaded languages
+ this.loadedLanguages.add(language);
+ // Clear cache
+ this.messageCache.clear();
+ });
+ } catch (error) {
+ console.error(`Failed to load translations for ${language}:`, error);
+ }
+ }
+
+ /**
+ * Imports the translations for the given language
+ * @param language - The language to import the translations for
+ * @returns {Promise}
+ */
+ private importLanguageFile(language: TLanguage): Promise {
+ switch (language) {
+ case "en":
+ return import("../locales/en/translations.json");
+ case "fr":
+ return import("../locales/fr/translations.json");
+ case "es":
+ return import("../locales/es/translations.json");
+ case "ja":
+ return import("../locales/ja/translations.json");
+ case "zh-CN":
+ return import("../locales/zh-CN/translations.json");
+ default:
+ throw new Error(`Unsupported language: ${language}`);
+ }
+ }
+
+ /** Checks if the language is valid based on the supported languages */
+ private isValidLanguage(lang: string | null): lang is TLanguage {
+ return lang !== null && this.availableLanguages.some((l) => l.value === lang);
+ }
+
+ /** Checks if a language code is similar to any supported language */
+ private findSimilarLanguage(lang: string): TLanguage | null {
+ // Convert to lowercase for case-insensitive comparison
+ const normalizedLang = lang.toLowerCase();
+
+ // Find a supported language that includes or is included in the browser language
+ const similarLang = this.availableLanguages.find(
+ (l) => normalizedLang.includes(l.value.toLowerCase()) || l.value.toLowerCase().includes(normalizedLang)
+ );
+
+ return similarLang ? similarLang.value : null;
+ }
+
+ /** Gets the browser language based on the navigator.language */
+ private getBrowserLanguage(): TLanguage {
+ const browserLang = navigator.language;
+
+ // Check exact match first
+ if (this.isValidLanguage(browserLang)) {
+ return browserLang;
+ }
+
+ // Check base language without region code
+ const baseLang = browserLang.split("-")[0];
+ if (this.isValidLanguage(baseLang)) {
+ return baseLang as TLanguage;
+ }
+
+ // Try to find a similar language
+ const similarLang = this.findSimilarLanguage(browserLang) || this.findSimilarLanguage(baseLang);
+
+ return similarLang || FALLBACK_LANGUAGE;
+ }
+
+ /**
+ * Gets the cache key for the given key and locale
+ * @param key - the key to get the cache key for
+ * @param locale - the locale to get the cache key for
+ * @returns the cache key for the given key and locale
+ */
+ private getCacheKey(key: string, locale: TLanguage): string {
+ return `${locale}:${key}`;
+ }
+
+ /**
+ * Gets the IntlMessageFormat instance for the given key and locale
+ * Returns cached instance if available
+ * Throws an error if the key is not found in the translations
+ */
+ private getMessageInstance(key: string, locale: TLanguage): IntlMessageFormat | null {
+ const cacheKey = this.getCacheKey(key, locale);
+
+ // Check if the cache already has the key
+ if (this.messageCache.has(cacheKey)) {
+ return this.messageCache.get(cacheKey) || null;
+ }
+
+ // Get the message from the translations
+ const message = get(this.translations[locale], key);
+ if (!message) return null;
+
+ try {
+ const formatter = new IntlMessageFormat(message as any, locale);
+ this.messageCache.set(cacheKey, formatter);
+ return formatter;
+ } catch (error) {
+ console.error(`Failed to create message formatter for key "${key}":`, error);
+ return null;
+ }
+ }
+
+ /**
+ * Translates a key with params using the current locale
+ * Falls back to the default language if the translation is not found
+ * Returns the key itself if the translation is not found
+ * @param key - The key to translate
+ * @param params - The params to format the translation with
+ * @returns The translated string
+ */
+ t(key: string, params?: Record): string {
+ try {
+ // Try current locale
+ let formatter = this.getMessageInstance(key, this.currentLocale);
+
+ // Fallback to default language if necessary
+ if (!formatter && this.currentLocale !== FALLBACK_LANGUAGE) {
+ formatter = this.getMessageInstance(key, FALLBACK_LANGUAGE);
+ }
+
+ // If we have a formatter, use it
+ if (formatter) {
+ return formatter.format(params || {}) as string;
+ }
+
+ // Last resort: return the key itself
+ return key;
+ } catch (error) {
+ console.error(`Translation error for key "${key}":`, error);
+ return key;
+ }
+ }
+
+ /**
+ * Sets the current language and updates the translations
+ * @param lng - The new language
+ */
+ async setLanguage(lng: TLanguage): Promise {
+ try {
+ if (!this.isValidLanguage(lng)) {
+ throw new Error(`Invalid language: ${lng}`);
+ }
+
+ // Safeguard in case background loading failed
+ if (!this.loadedLanguages.has(lng)) {
+ await this.loadLanguageTranslations(lng);
+ }
+
+ if (typeof window !== "undefined") {
+ localStorage.setItem(STORAGE_KEY, lng);
+ document.documentElement.lang = lng;
+ }
+
+ runInAction(() => {
+ this.currentLocale = lng;
+ this.messageCache.clear(); // Clear cache when language changes
+ });
+ } catch (error) {
+ console.error("Failed to set language:", error);
+ }
+ }
+
+ /**
+ * Gets the available language options for the dropdown
+ * @returns An array of language options
+ */
+ get availableLanguages(): ILanguageOption[] {
+ return SUPPORTED_LANGUAGES;
+ }
+}
diff --git a/packages/i18n/src/types/index.ts b/packages/i18n/src/types/index.ts
new file mode 100644
index 000000000..d56ad1e16
--- /dev/null
+++ b/packages/i18n/src/types/index.ts
@@ -0,0 +1,2 @@
+export * from "./language";
+export * from "./translation";
diff --git a/packages/i18n/src/types/language.ts b/packages/i18n/src/types/language.ts
new file mode 100644
index 000000000..86e141ff5
--- /dev/null
+++ b/packages/i18n/src/types/language.ts
@@ -0,0 +1,6 @@
+export type TLanguage = "en" | "fr" | "es" | "ja" | "zh-CN";
+
+export interface ILanguageOption {
+ label: string;
+ value: TLanguage;
+}
diff --git a/packages/i18n/src/types/translation.ts b/packages/i18n/src/types/translation.ts
new file mode 100644
index 000000000..b75705552
--- /dev/null
+++ b/packages/i18n/src/types/translation.ts
@@ -0,0 +1,7 @@
+export interface ITranslation {
+ [key: string]: string | ITranslation;
+}
+
+export interface ITranslations {
+ [locale: string]: ITranslation;
+}
diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json
new file mode 100644
index 000000000..6599e6e82
--- /dev/null
+++ b/packages/i18n/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "@plane/typescript-config/react-library.json",
+ "compilerOptions": {
+ "jsx": "react",
+ "lib": ["esnext", "dom"],
+ "resolveJsonModule": true
+ },
+ "include": ["./src"],
+ "exclude": ["dist", "build", "node_modules"]
+}
diff --git a/packages/logger/.eslintignore b/packages/logger/.eslintignore
new file mode 100644
index 000000000..6019047c3
--- /dev/null
+++ b/packages/logger/.eslintignore
@@ -0,0 +1,3 @@
+build/*
+dist/*
+out/*
\ No newline at end of file
diff --git a/packages/logger/.eslintrc.js b/packages/logger/.eslintrc.js
new file mode 100644
index 000000000..558b8f76e
--- /dev/null
+++ b/packages/logger/.eslintrc.js
@@ -0,0 +1,9 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@plane/eslint-config/library.js"],
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
+};
diff --git a/packages/logger/.prettierrc b/packages/logger/.prettierrc
new file mode 100644
index 000000000..87d988f1b
--- /dev/null
+++ b/packages/logger/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "tabWidth": 2,
+ "trailingComma": "es5"
+}
diff --git a/packages/logger/README.md b/packages/logger/README.md
new file mode 100644
index 000000000..6b83eb52b
--- /dev/null
+++ b/packages/logger/README.md
@@ -0,0 +1,59 @@
+# Logger Package
+
+This package provides a logger and a request logger utility built using [Winston](https://github.com/winstonjs/winston). It offers customizable log levels using env and supports structured logging for general application logs and HTTP requests.
+
+## Features.
+- Dynamic log level configuration using env.
+- Pre-configured winston logger for general usage (`logger`).
+- Request logger middleware that logs incoming request
+
+## Usage
+
+### Adding as a package
+Add this package as a dependency in package.json
+```typescript
+dependency: {
+ ...
+ @plane/logger":"*",
+ ...
+}
+```
+
+### Importing the Logger
+```typescript
+import { logger, requestLogger } from '@plane/logger'
+```
+### Usage
+### `logger`: General Logger
+Use this for general application logs.
+
+```typescript
+logger.info("This is an info log");
+logger.warn("This is a warning");
+logger.error("This is an error");
+```
+
+### `requestLogger`: Request Logger Middleware
+Use this as a middleware for incoming requests
+
+```typescript
+const app = express()
+app.use(requestLogger)
+```
+
+## Available Log Levels
+- `error`
+- `warn`
+- `info` (default)
+- `http`
+- `verbose`
+- `debug`
+- `silly`
+
+## Log file
+- Log files are stored in logs folder of current working directory. Error logs are stored in files with format `error-%DATE%.log` and combined logs are stored with format `combined-%DATE%.log`.
+- Log files have a 7 day rotation period defined.
+
+## Configuration
+- By default, the log level is set to `info`.
+- You can specify a log level by adding a LOG_LEVEL in .env.
\ No newline at end of file
diff --git a/packages/logger/package.json b/packages/logger/package.json
new file mode 100644
index 000000000..7a374004f
--- /dev/null
+++ b/packages/logger/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@plane/logger",
+ "version": "0.24.1",
+ "description": "Logger shared across multiple apps internally",
+ "private": true,
+ "main": "./src/index.ts",
+ "types": "./src/index.ts",
+ "scripts": {
+ "lint": "eslint src --ext .ts,.tsx",
+ "lint:errors": "eslint src --ext .ts,.tsx --quiet"
+ },
+ "dependencies": {
+ "winston": "^3.17.0",
+ "winston-daily-rotate-file": "^5.0.0"
+ },
+ "devDependencies": {
+ "@plane/eslint-config": "*",
+ "@types/node": "^22.5.4",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/packages/logger/src/config.ts b/packages/logger/src/config.ts
new file mode 100644
index 000000000..fd918e59e
--- /dev/null
+++ b/packages/logger/src/config.ts
@@ -0,0 +1,66 @@
+import winston from "winston";
+import DailyRotateFile from "winston-daily-rotate-file";
+import path from "path";
+
+// Define log levels
+const levels = {
+ error: 0,
+ warn: 1,
+ info: 2,
+ http: 3,
+ debug: 4,
+};
+
+// Define colors for each level
+const colors = {
+ error: "red",
+ warn: "yellow",
+ info: "green",
+ http: "magenta",
+ debug: "white",
+};
+
+// Tell winston about our colors
+winston.addColors(colors);
+
+// Custom format for logging
+const format = winston.format.combine(
+ winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss:ms" }),
+ winston.format.colorize({ all: true }),
+ winston.format.printf(
+ (info: winston.Logform.TransformableInfo) => `[${info?.timestamp}] ${info.level}: ${info.message}`
+ )
+);
+
+// Define which transports to use
+const transports = [
+ // Console transport
+ new winston.transports.Console(),
+
+ // Rotating file transport for errors
+ new DailyRotateFile({
+ filename: path.join(process.cwd(), "logs", "error-%DATE%.log"),
+ datePattern: "YYYY-MM-DD",
+ zippedArchive: true,
+ maxSize: process.env.LOG_MAX_SIZE || "20m",
+ maxFiles: process.env.LOG_RETENTION || "7d",
+ level: "error",
+ }),
+
+ // Rotating file transport for all logs
+ new DailyRotateFile({
+ filename: path.join(process.cwd(), "logs", "combined-%DATE%.log"),
+ datePattern: "YYYY-MM-DD",
+ zippedArchive: true,
+ maxSize: process.env.LOG_MAX_SIZE || "20m",
+ maxFiles: process.env.LOG_RETENTION || "7d",
+ }),
+];
+
+// Create the logger
+export const logger = winston.createLogger({
+ level: process.env.LOG_LEVEL || "info",
+ levels,
+ format,
+ transports,
+});
diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts
new file mode 100644
index 000000000..715301ad3
--- /dev/null
+++ b/packages/logger/src/index.ts
@@ -0,0 +1,2 @@
+export * from "./config";
+export * from "./middleware";
diff --git a/packages/logger/src/middleware.ts b/packages/logger/src/middleware.ts
new file mode 100644
index 000000000..e251a5837
--- /dev/null
+++ b/packages/logger/src/middleware.ts
@@ -0,0 +1,23 @@
+import { Request, Response, NextFunction } from "express";
+import { logger } from "./config";
+
+export const requestLogger = (req: Request, res: Response, next: NextFunction) => {
+ // Log when the request starts
+ const startTime = Date.now();
+
+ // Log request details
+ logger.http(`Incoming ${req.method} request to ${req.url} from ${req.ip}`);
+
+ // Log request body if present
+ if (Object.keys(req.body).length > 0) {
+ logger.debug("Request body:", req.body);
+ }
+
+ // Capture response
+ res.on("finish", () => {
+ const duration = Date.now() - startTime;
+ logger.http(`Completed ${req.method} ${req.url} with status ${res.statusCode} in ${duration}ms`);
+ });
+
+ next();
+};
diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json
new file mode 100644
index 000000000..2ba1c9799
--- /dev/null
+++ b/packages/logger/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "@plane/typescript-config/base.json",
+ "compilerOptions": {
+ "module": "ESNext",
+ "target": "ESNext",
+ "moduleResolution": "node",
+ "esModuleInterop": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "experimentalDecorators": true,
+ "sourceMap": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/propel/.eslintignore b/packages/propel/.eslintignore
new file mode 100644
index 000000000..6019047c3
--- /dev/null
+++ b/packages/propel/.eslintignore
@@ -0,0 +1,3 @@
+build/*
+dist/*
+out/*
\ No newline at end of file
diff --git a/packages/propel/.eslintrc.js b/packages/propel/.eslintrc.js
new file mode 100644
index 000000000..b11b7bb6d
--- /dev/null
+++ b/packages/propel/.eslintrc.js
@@ -0,0 +1,6 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@plane/eslint-config/library.js"],
+ parser: "@typescript-eslint/parser",
+};
diff --git a/packages/propel/.prettierignore b/packages/propel/.prettierignore
new file mode 100644
index 000000000..e841c6b32
--- /dev/null
+++ b/packages/propel/.prettierignore
@@ -0,0 +1,5 @@
+.next
+.turbo
+out/
+dist/
+build/
\ No newline at end of file
diff --git a/packages/propel/.prettierrc b/packages/propel/.prettierrc
new file mode 100644
index 000000000..87d988f1b
--- /dev/null
+++ b/packages/propel/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "tabWidth": 2,
+ "trailingComma": "es5"
+}
diff --git a/packages/propel/package.json b/packages/propel/package.json
new file mode 100644
index 000000000..1f2ebbeea
--- /dev/null
+++ b/packages/propel/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@plane/propel",
+ "version": "0.24.1",
+ "private": true,
+ "scripts": {
+ "lint": "eslint src --ext .ts,.tsx",
+ "lint:errors": "eslint src --ext .ts,.tsx --quiet"
+ },
+ "exports": {
+ "./ui/*": "./src/ui/*.tsx",
+ "./charts/*": "./src/charts/*/index.ts"
+ },
+ "dependencies": {
+ "@radix-ui/react-slot": "^1.1.1",
+ "class-variance-authority": "^0.7.1",
+ "lucide-react": "^0.469.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "recharts": "^2.15.1",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@plane/eslint-config": "*",
+ "@plane/tailwind-config": "*",
+ "@plane/typescript-config": "*",
+ "@types/react": "18.3.1",
+ "@types/react-dom": "18.3.0",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/packages/propel/postcss.config.js b/packages/propel/postcss.config.js
new file mode 100644
index 000000000..12a703d90
--- /dev/null
+++ b/packages/propel/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/packages/propel/src/charts/area-chart/index.ts b/packages/propel/src/charts/area-chart/index.ts
new file mode 100644
index 000000000..1efe34c51
--- /dev/null
+++ b/packages/propel/src/charts/area-chart/index.ts
@@ -0,0 +1 @@
+export * from "./root";
diff --git a/packages/propel/src/charts/area-chart/root.tsx b/packages/propel/src/charts/area-chart/root.tsx
new file mode 100644
index 000000000..710c5f70d
--- /dev/null
+++ b/packages/propel/src/charts/area-chart/root.tsx
@@ -0,0 +1,124 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+"use client";
+
+import React, { useMemo } from "react";
+import { AreaChart as CoreAreaChart, Area, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
+// plane imports
+import { AXIS_LINE_CLASSNAME, LABEL_CLASSNAME } from "@plane/constants";
+import { TAreaChartProps } from "@plane/types";
+// local components
+import { CustomXAxisTick, CustomYAxisTick } from "../tick";
+import { CustomTooltip } from "../tooltip";
+
+export const AreaChart = React.memo((props: TAreaChartProps) => {
+ const {
+ data,
+ areas,
+ xAxis,
+ yAxis,
+ className = "w-full h-96",
+ tickCount = {
+ x: undefined,
+ y: 10,
+ },
+ showTooltip = true,
+ } = props;
+ // derived values
+ const itemKeys = useMemo(() => areas.map((area) => area.key), [areas]);
+ const itemDotClassNames = useMemo(
+ () => areas.reduce((acc, area) => ({ ...acc, [area.key]: area.dotClassName }), {}),
+ [areas]
+ );
+
+ const renderAreas = useMemo(
+ () =>
+ areas.map((area) => (
+
+ )),
+ [areas]
+ );
+
+ return (
+
+
+
+ }
+ tickLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ axisLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ label={{
+ value: xAxis.label,
+ dy: 28,
+ className: LABEL_CLASSNAME,
+ }}
+ tickCount={tickCount.x}
+ />
+ }
+ tickCount={tickCount.y}
+ allowDecimals={!!yAxis.allowDecimals}
+ />
+ {showTooltip && (
+ (
+
+ )}
+ />
+ )}
+ {renderAreas}
+
+
+
+ );
+});
+AreaChart.displayName = "AreaChart";
diff --git a/packages/propel/src/charts/bar-chart/bar.tsx b/packages/propel/src/charts/bar-chart/bar.tsx
new file mode 100644
index 000000000..339be704d
--- /dev/null
+++ b/packages/propel/src/charts/bar-chart/bar.tsx
@@ -0,0 +1,70 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import React from "react";
+// plane imports
+import { TChartData } from "@plane/types";
+import { cn } from "@plane/utils";
+
+// Helper to calculate percentage
+const calculatePercentage = (
+ data: TChartData,
+ stackKeys: T[],
+ currentKey: T
+): number => {
+ const total = stackKeys.reduce((sum, key) => sum + data[key], 0);
+ return total === 0 ? 0 : Math.round((data[currentKey] / total) * 100);
+};
+
+const MIN_BAR_HEIGHT_FOR_INTERNAL_TEXT = 14; // Minimum height needed to show text inside
+const BAR_BORDER_RADIUS = 2; // Border radius for each bar
+
+export const CustomBar = React.memo((props: any) => {
+ const { fill, x, y, width, height, dataKey, stackKeys, payload, textClassName, showPercentage } = props;
+ // Calculate text position
+ const TEXT_PADDING_Y = Math.min(6, Math.abs(MIN_BAR_HEIGHT_FOR_INTERNAL_TEXT - height / 2));
+ const textY = y + height - TEXT_PADDING_Y; // Position inside bar if tall enough
+ // derived values
+ const currentBarPercentage = calculatePercentage(payload, stackKeys, dataKey);
+ const showText =
+ // from props
+ showPercentage &&
+ // height of the bar is greater than or equal to the minimum height required to show the text
+ height >= MIN_BAR_HEIGHT_FOR_INTERNAL_TEXT &&
+ // bar percentage text has some value
+ currentBarPercentage !== undefined &&
+ // bar percentage is a number
+ !Number.isNaN(currentBarPercentage);
+
+ if (!height) return null;
+ return (
+
+
+ {showText && (
+
+ {currentBarPercentage}%
+
+ )}
+
+ );
+});
+CustomBar.displayName = "CustomBar";
diff --git a/packages/propel/src/charts/bar-chart/index.ts b/packages/propel/src/charts/bar-chart/index.ts
new file mode 100644
index 000000000..1efe34c51
--- /dev/null
+++ b/packages/propel/src/charts/bar-chart/index.ts
@@ -0,0 +1 @@
+export * from "./root";
diff --git a/packages/propel/src/charts/bar-chart/root.tsx b/packages/propel/src/charts/bar-chart/root.tsx
new file mode 100644
index 000000000..883a0621c
--- /dev/null
+++ b/packages/propel/src/charts/bar-chart/root.tsx
@@ -0,0 +1,125 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+"use client";
+
+import React, { useMemo } from "react";
+import { BarChart as CoreBarChart, Bar, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
+// plane imports
+import { AXIS_LINE_CLASSNAME, LABEL_CLASSNAME } from "@plane/constants";
+import { TBarChartProps } from "@plane/types";
+// local components
+import { CustomXAxisTick, CustomYAxisTick } from "../tick";
+import { CustomTooltip } from "../tooltip";
+import { CustomBar } from "./bar";
+
+export const BarChart = React.memo((props: TBarChartProps) => {
+ const {
+ data,
+ bars,
+ xAxis,
+ yAxis,
+ barSize = 40,
+ className = "w-full h-96",
+ tickCount = {
+ x: undefined,
+ y: 10,
+ },
+ showTooltip = true,
+ } = props;
+ // derived values
+ const stackKeys = useMemo(() => bars.map((bar) => bar.key), [bars]);
+ const stackDotClassNames = useMemo(
+ () => bars.reduce((acc, bar) => ({ ...acc, [bar.key]: bar.dotClassName }), {}),
+ [bars]
+ );
+
+ const renderBars = useMemo(
+ () =>
+ bars.map((bar) => (
+ (
+
+ )}
+ />
+ )),
+ [stackKeys, bars]
+ );
+
+ return (
+
+
+
+ }
+ tickLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ axisLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ label={{
+ value: xAxis.label,
+ dy: 28,
+ className: LABEL_CLASSNAME,
+ }}
+ tickCount={tickCount.x}
+ />
+ }
+ tickCount={tickCount.y}
+ allowDecimals={!!yAxis.allowDecimals}
+ />
+ {showTooltip && (
+ (
+
+ )}
+ />
+ )}
+ {renderBars}
+
+
+
+ );
+});
+BarChart.displayName = "BarChart";
diff --git a/packages/propel/src/charts/line-chart/index.ts b/packages/propel/src/charts/line-chart/index.ts
new file mode 100644
index 000000000..1efe34c51
--- /dev/null
+++ b/packages/propel/src/charts/line-chart/index.ts
@@ -0,0 +1 @@
+export * from "./root";
diff --git a/packages/propel/src/charts/line-chart/root.tsx b/packages/propel/src/charts/line-chart/root.tsx
new file mode 100644
index 000000000..c689fe9ba
--- /dev/null
+++ b/packages/propel/src/charts/line-chart/root.tsx
@@ -0,0 +1,115 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+"use client";
+
+import React, { useMemo } from "react";
+import { LineChart as CoreLineChart, Line, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
+// plane imports
+import { AXIS_LINE_CLASSNAME, LABEL_CLASSNAME } from "@plane/constants";
+import { TLineChartProps } from "@plane/types";
+// local components
+import { CustomXAxisTick, CustomYAxisTick } from "../tick";
+import { CustomTooltip } from "../tooltip";
+
+export const LineChart = React.memo((props: TLineChartProps) => {
+ const {
+ data,
+ lines,
+ xAxis,
+ yAxis,
+ className = "w-full h-96",
+ tickCount = {
+ x: undefined,
+ y: 10,
+ },
+ showTooltip = true,
+ } = props;
+ // derived values
+ const itemKeys = useMemo(() => lines.map((line) => line.key), [lines]);
+ const itemDotClassNames = useMemo(
+ () => lines.reduce((acc, line) => ({ ...acc, [line.key]: line.dotClassName }), {}),
+ [lines]
+ );
+
+ const renderLines = useMemo(
+ () =>
+ lines.map((line) => (
+
+ )),
+ [lines]
+ );
+
+ return (
+
+
+
+ }
+ tickLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ axisLine={{
+ stroke: "currentColor",
+ className: AXIS_LINE_CLASSNAME,
+ }}
+ label={{
+ value: xAxis.label,
+ dy: 28,
+ className: LABEL_CLASSNAME,
+ }}
+ tickCount={tickCount.x}
+ />
+ }
+ tickCount={tickCount.y}
+ allowDecimals={!!yAxis.allowDecimals}
+ />
+ {showTooltip && (
+ (
+
+ )}
+ />
+ )}
+ {renderLines}
+
+
+
+ );
+});
+LineChart.displayName = "LineChart";
diff --git a/packages/propel/src/charts/pie-chart/index.ts b/packages/propel/src/charts/pie-chart/index.ts
new file mode 100644
index 000000000..1efe34c51
--- /dev/null
+++ b/packages/propel/src/charts/pie-chart/index.ts
@@ -0,0 +1 @@
+export * from "./root";
diff --git a/packages/propel/src/charts/pie-chart/root.tsx b/packages/propel/src/charts/pie-chart/root.tsx
new file mode 100644
index 000000000..d9e2558ed
--- /dev/null
+++ b/packages/propel/src/charts/pie-chart/root.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import React, { useMemo } from "react";
+import { Cell, PieChart as CorePieChart, Pie, ResponsiveContainer, Tooltip } from "recharts";
+// plane imports
+import { TPieChartProps } from "@plane/types";
+// local components
+import { CustomPieChartTooltip } from "./tooltip";
+
+export const PieChart = React.memo((props: TPieChartProps) => {
+ const { data, dataKey, cells, className = "w-full h-96", innerRadius, outerRadius, showTooltip = true } = props;
+
+ const renderCells = useMemo(
+ () => cells.map((cell) => | ),
+ [cells]
+ );
+
+ return (
+
+
+
+
+ {renderCells}
+
+ {showTooltip && (
+ {
+ if (!active || !payload || !payload.length) return null;
+ const cellData = cells.find((c) => c.key === payload[0].name);
+ if (!cellData) return null;
+ return ;
+ }}
+ />
+ )}
+
+
+
+ );
+});
+PieChart.displayName = "PieChart";
diff --git a/packages/propel/src/charts/pie-chart/tooltip.tsx b/packages/propel/src/charts/pie-chart/tooltip.tsx
new file mode 100644
index 000000000..56c7fa34c
--- /dev/null
+++ b/packages/propel/src/charts/pie-chart/tooltip.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { NameType, Payload, ValueType } from "recharts/types/component/DefaultTooltipContent";
+// plane imports
+import { Card, ECardSpacing } from "@plane/ui";
+import { cn } from "@plane/utils";
+
+type Props = {
+ dotClassName?: string;
+ label: string;
+ payload: Payload[];
+};
+
+export const CustomPieChartTooltip = React.memo((props: Props) => {
+ const { dotClassName, label, payload } = props;
+
+ return (
+
+
+ {label}
+
+ {payload?.map((item) => (
+
+
+
{item?.name}:
+
{item?.value}
+
+ ))}
+
+ );
+});
+CustomPieChartTooltip.displayName = "CustomPieChartTooltip";
diff --git a/packages/propel/src/charts/tick.tsx b/packages/propel/src/charts/tick.tsx
new file mode 100644
index 000000000..c631d7d6e
--- /dev/null
+++ b/packages/propel/src/charts/tick.tsx
@@ -0,0 +1,23 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import React from "react";
+
+// Common classnames
+const AXIS_TICK_CLASSNAME = "fill-custom-text-400 text-sm capitalize";
+
+export const CustomXAxisTick = React.memo(({ x, y, payload }: any) => (
+
+
+ {payload.value}
+
+
+));
+CustomXAxisTick.displayName = "CustomXAxisTick";
+
+export const CustomYAxisTick = React.memo(({ x, y, payload }: any) => (
+
+
+ {payload.value}
+
+
+));
+CustomYAxisTick.displayName = "CustomYAxisTick";
diff --git a/packages/propel/src/charts/tooltip.tsx b/packages/propel/src/charts/tooltip.tsx
new file mode 100644
index 000000000..e7f92a9cb
--- /dev/null
+++ b/packages/propel/src/charts/tooltip.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { NameType, Payload, ValueType } from "recharts/types/component/DefaultTooltipContent";
+// plane imports
+import { Card, ECardSpacing } from "@plane/ui";
+import { cn } from "@plane/utils";
+
+type Props = {
+ active: boolean | undefined;
+ label: string | undefined;
+ payload: Payload[] | undefined;
+ itemKeys: string[];
+ itemDotClassNames: Record;
+};
+
+export const CustomTooltip = React.memo((props: Props) => {
+ const { active, label, payload, itemKeys, itemDotClassNames } = props;
+ // derived values
+ const filteredPayload = payload?.filter((item) => item.dataKey && itemKeys.includes(`${item.dataKey}`));
+
+ if (!active || !filteredPayload || !filteredPayload.length) return null;
+ return (
+
+
+ {label}
+
+ {filteredPayload.map((item) => {
+ if (!item.dataKey) return null;
+ return (
+
+ {itemDotClassNames[item?.dataKey] && (
+
+ )}
+
{item?.name}:
+
{item?.value}
+
+ );
+ })}
+
+ );
+});
+CustomTooltip.displayName = "CustomTooltip";
diff --git a/packages/propel/src/charts/tree-map/index.ts b/packages/propel/src/charts/tree-map/index.ts
new file mode 100644
index 000000000..1efe34c51
--- /dev/null
+++ b/packages/propel/src/charts/tree-map/index.ts
@@ -0,0 +1 @@
+export * from "./root";
diff --git a/packages/propel/src/charts/tree-map/map-content.tsx b/packages/propel/src/charts/tree-map/map-content.tsx
new file mode 100644
index 000000000..f3b062cb0
--- /dev/null
+++ b/packages/propel/src/charts/tree-map/map-content.tsx
@@ -0,0 +1,276 @@
+import React, { useMemo } from "react";
+// plane imports
+import { TBottomSectionConfig, TContentVisibility, TTopSectionConfig } from "@plane/types";
+import { cn } from "@plane/utils";
+
+const LAYOUT = {
+ PADDING: 2,
+ RADIUS: 6,
+ TEXT: {
+ PADDING_LEFT: 8,
+ PADDING_RIGHT: 8,
+ VERTICAL_OFFSET: 20,
+ ELLIPSIS_OFFSET: -4,
+ FONT_SIZES: {
+ SM: 12.6,
+ XS: 10.8,
+ },
+ },
+ ICON: {
+ SIZE: 16,
+ GAP: 6,
+ },
+ MIN_DIMENSIONS: {
+ HEIGHT_FOR_BOTH: 42,
+ HEIGHT_FOR_TOP: 35,
+ HEIGHT_FOR_DOTS: 20,
+ WIDTH_FOR_ICON: 30,
+ WIDTH_FOR_DOTS: 15,
+ },
+};
+
+const calculateContentWidth = (text: string | number, fontSize: number): number => String(text).length * fontSize * 0.7;
+
+const calculateTopSectionConfig = (effectiveWidth: number, name: string, hasIcon: boolean): TTopSectionConfig => {
+ const iconWidth = hasIcon ? LAYOUT.ICON.SIZE + LAYOUT.ICON.GAP : 0;
+ const nameWidth = calculateContentWidth(name, LAYOUT.TEXT.FONT_SIZES.SM);
+ const totalPadding = LAYOUT.TEXT.PADDING_LEFT + LAYOUT.TEXT.PADDING_RIGHT;
+
+ // First check if we can show icon
+ const canShowIcon = hasIcon && effectiveWidth >= LAYOUT.MIN_DIMENSIONS.WIDTH_FOR_ICON;
+
+ // If we can't even show icon, check if we can show dots
+ if (!canShowIcon) {
+ return {
+ showIcon: false,
+ showName: effectiveWidth >= LAYOUT.MIN_DIMENSIONS.WIDTH_FOR_DOTS,
+ nameTruncated: true,
+ };
+ }
+
+ // We can show icon, now check if we have space for name
+ const availableWidthForName = effectiveWidth - (canShowIcon ? iconWidth : 0) - totalPadding;
+ const canShowFullName = availableWidthForName >= nameWidth;
+
+ return {
+ showIcon: canShowIcon,
+ showName: availableWidthForName > 0,
+ nameTruncated: !canShowFullName,
+ };
+};
+
+const calculateBottomSectionConfig = (
+ effectiveWidth: number,
+ effectiveHeight: number,
+ value: number | undefined,
+ label: string | undefined
+): TBottomSectionConfig => {
+ // If height is not enough for bottom section
+ if (effectiveHeight < LAYOUT.MIN_DIMENSIONS.HEIGHT_FOR_BOTH) {
+ return {
+ show: false,
+ showValue: false,
+ showLabel: false,
+ labelTruncated: false,
+ };
+ }
+
+ // Calculate widths
+ const totalPadding = LAYOUT.TEXT.PADDING_LEFT + LAYOUT.TEXT.PADDING_RIGHT;
+ const valueWidth = value ? calculateContentWidth(value, LAYOUT.TEXT.FONT_SIZES.XS) : 0;
+ const labelWidth = label ? calculateContentWidth(label, LAYOUT.TEXT.FONT_SIZES.XS) + 4 : 0; // 4px for spacing
+ const availableWidth = effectiveWidth - totalPadding;
+
+ // If we can't even show value
+ if (availableWidth < Math.max(valueWidth, LAYOUT.MIN_DIMENSIONS.WIDTH_FOR_DOTS)) {
+ return {
+ show: true,
+ showValue: false,
+ showLabel: false,
+ labelTruncated: false,
+ };
+ }
+
+ // If we can show value but not full label
+ const canShowFullLabel = availableWidth >= valueWidth + labelWidth;
+
+ return {
+ show: true,
+ showValue: true,
+ showLabel: true,
+ labelTruncated: !canShowFullLabel,
+ };
+};
+
+const calculateVisibility = (
+ width: number,
+ height: number,
+ hasIcon: boolean,
+ name: string,
+ value: number | undefined,
+ label: string | undefined
+): TContentVisibility => {
+ const effectiveWidth = width - LAYOUT.PADDING * 2;
+ const effectiveHeight = height - LAYOUT.PADDING * 2;
+
+ // If extremely small, show only dots
+ if (
+ effectiveHeight < LAYOUT.MIN_DIMENSIONS.HEIGHT_FOR_DOTS ||
+ effectiveWidth < LAYOUT.MIN_DIMENSIONS.WIDTH_FOR_DOTS
+ ) {
+ return {
+ top: { showIcon: false, showName: false, nameTruncated: false },
+ bottom: { show: false, showValue: false, showLabel: false, labelTruncated: false },
+ };
+ }
+
+ const topSection = calculateTopSectionConfig(effectiveWidth, name, hasIcon);
+ const bottomSection = calculateBottomSectionConfig(effectiveWidth, effectiveHeight, value, label);
+
+ return {
+ top: topSection,
+ bottom: bottomSection,
+ };
+};
+
+const truncateText = (text: string | number, maxWidth: number, fontSize: number, reservedWidth: number = 0): string => {
+ const availableWidth = maxWidth - reservedWidth;
+ if (availableWidth <= 0) return "";
+
+ const avgCharWidth = fontSize * 0.7;
+ const maxChars = Math.floor(availableWidth / avgCharWidth);
+ const stringText = String(text);
+
+ if (maxChars <= 3) return "";
+ if (stringText.length <= maxChars) return stringText;
+ return `${stringText.slice(0, maxChars - 3)}...`;
+};
+
+export const CustomTreeMapContent: React.FC = ({
+ x,
+ y,
+ width,
+ height,
+ name,
+ value,
+ label,
+ fillColor,
+ fillClassName,
+ textClassName,
+ icon,
+}) => {
+ const dimensions = useMemo(() => {
+ const pX = x + LAYOUT.PADDING;
+ const pY = y + LAYOUT.PADDING;
+ const pWidth = Math.max(0, width - LAYOUT.PADDING * 2);
+ const pHeight = Math.max(0, height - LAYOUT.PADDING * 2);
+ return { pX, pY, pWidth, pHeight };
+ }, [x, y, width, height]);
+
+ const visibility = useMemo(
+ () => calculateVisibility(width, height, !!icon, name, value, label),
+ [width, height, icon, name, value, label]
+ );
+
+ if (!name || width <= 0 || height <= 0) return null;
+
+ const renderContent = () => {
+ const { pX, pY, pWidth, pHeight } = dimensions;
+ const { top, bottom } = visibility;
+
+ const availableTextWidth = pWidth - LAYOUT.TEXT.PADDING_LEFT - LAYOUT.TEXT.PADDING_RIGHT;
+ const iconSpace = top.showIcon ? LAYOUT.ICON.SIZE + LAYOUT.ICON.GAP : 0;
+
+ return (
+
+ {/* Background shape */}
+
+
+ {/* Top section */}
+
+ {top.showIcon && icon && (
+
+ {React.cloneElement(icon, {
+ className: cn("size-4", icon?.props?.className),
+ "aria-hidden": true,
+ })}
+
+ )}
+ {top.showName && (
+
+ {top.nameTruncated ? truncateText(name, availableTextWidth, LAYOUT.TEXT.FONT_SIZES.SM, iconSpace) : name}
+
+ )}
+
+
+ {/* Bottom section */}
+ {bottom.show && (
+
+ {bottom.showValue && value !== undefined && (
+
+ {value.toLocaleString()}
+ {bottom.showLabel && label && (
+
+ {bottom.labelTruncated
+ ? truncateText(
+ label,
+ availableTextWidth - calculateContentWidth(value, LAYOUT.TEXT.FONT_SIZES.SM) - 4,
+ LAYOUT.TEXT.FONT_SIZES.SM
+ )
+ : label}
+
+ )}
+ {!bottom.showLabel && label && ... }
+
+ )}
+
+ )}
+
+ );
+ };
+
+ return (
+
+
+ {renderContent()}
+
+ );
+};
diff --git a/packages/propel/src/charts/tree-map/root.tsx b/packages/propel/src/charts/tree-map/root.tsx
new file mode 100644
index 000000000..47ea21d72
--- /dev/null
+++ b/packages/propel/src/charts/tree-map/root.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { Treemap, ResponsiveContainer, Tooltip } from "recharts";
+// plane imports
+import { TreeMapChartProps } from "@plane/types";
+import { cn } from "@plane/utils";
+// local imports
+import { CustomTreeMapContent } from "./map-content";
+import { TreeMapTooltip } from "./tooltip";
+
+export const TreeMapChart = React.memo((props: TreeMapChartProps) => {
+ const { data, className = "w-full h-96", isAnimationActive = false, showTooltip = true } = props;
+ return (
+
+
+ }
+ animationEasing="ease-out"
+ isUpdateAnimationActive={isAnimationActive}
+ animationBegin={100}
+ animationDuration={500}
+ >
+ {showTooltip && (
+ }
+ cursor={{
+ fill: "currentColor",
+ className: "text-custom-background-90/80 cursor-pointer",
+ }}
+ />
+ )}
+
+
+
+ );
+});
+TreeMapChart.displayName = "TreeMapChart";
diff --git a/packages/propel/src/charts/tree-map/tooltip.tsx b/packages/propel/src/charts/tree-map/tooltip.tsx
new file mode 100644
index 000000000..55c6e687e
--- /dev/null
+++ b/packages/propel/src/charts/tree-map/tooltip.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+// plane imports
+import { Card, ECardSpacing } from "@plane/ui";
+
+interface TreeMapTooltipProps {
+ active: boolean | undefined;
+ payload: any[] | undefined;
+}
+
+export const TreeMapTooltip = React.memo(({ active, payload }: TreeMapTooltipProps) => {
+ if (!active || !payload || !payload[0]?.payload) return null;
+
+ const data = payload[0].payload;
+
+ return (
+
+
+ {data?.icon}
+
{data?.name}
+
+
+ {data?.value.toLocaleString()}
+ {data.label && ` ${data.label}`}
+
+
+ );
+});
+
+TreeMapTooltip.displayName = "TreeMapTooltip";
diff --git a/packages/propel/src/globals.css b/packages/propel/src/globals.css
new file mode 100644
index 000000000..ee2896808
--- /dev/null
+++ b/packages/propel/src/globals.css
@@ -0,0 +1,12 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply font-sans antialiased bg-background text-foreground;
+ }
+}
diff --git a/packages/propel/src/index.ts b/packages/propel/src/index.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/propel/tailwind.config.ts b/packages/propel/tailwind.config.ts
new file mode 100644
index 000000000..859971bab
--- /dev/null
+++ b/packages/propel/tailwind.config.ts
@@ -0,0 +1,7 @@
+import type { Config } from "tailwindcss";
+import sharedConfig from "@plane/tailwind-config/tailwind.config";
+
+export default {
+ ...sharedConfig,
+ content: ["./src/**/*.{ts,tsx}"],
+} satisfies Config;
diff --git a/packages/propel/tsconfig.json b/packages/propel/tsconfig.json
new file mode 100644
index 000000000..1f695a242
--- /dev/null
+++ b/packages/propel/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "@plane/typescript-config/react-library.json",
+ "compilerOptions": {
+ "outDir": "dist"
+ },
+ "include": ["src"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/packages/services/.eslintignore b/packages/services/.eslintignore
new file mode 100644
index 000000000..6019047c3
--- /dev/null
+++ b/packages/services/.eslintignore
@@ -0,0 +1,3 @@
+build/*
+dist/*
+out/*
\ No newline at end of file
diff --git a/packages/services/.eslintrc.js b/packages/services/.eslintrc.js
new file mode 100644
index 000000000..558b8f76e
--- /dev/null
+++ b/packages/services/.eslintrc.js
@@ -0,0 +1,9 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@plane/eslint-config/library.js"],
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
+};
diff --git a/packages/services/.prettierrc b/packages/services/.prettierrc
new file mode 100644
index 000000000..87d988f1b
--- /dev/null
+++ b/packages/services/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "tabWidth": 2,
+ "trailingComma": "es5"
+}
diff --git a/packages/services/package.json b/packages/services/package.json
new file mode 100644
index 000000000..792186de7
--- /dev/null
+++ b/packages/services/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@plane/services",
+ "version": "0.24.1",
+ "private": true,
+ "main": "./src/index.ts",
+ "scripts": {
+ "lint": "eslint src --ext .ts,.tsx",
+ "lint:errors": "eslint src --ext .ts,.tsx --quiet"
+ },
+ "dependencies": {
+ "@plane/constants": "*",
+ "axios": "^1.7.9"
+ }
+}
diff --git a/packages/services/src/ai/ai.service.ts b/packages/services/src/ai/ai.service.ts
new file mode 100644
index 000000000..261cf9df5
--- /dev/null
+++ b/packages/services/src/ai/ai.service.ts
@@ -0,0 +1,67 @@
+// plane web constants
+import { AI_EDITOR_TASKS, API_BASE_URL } from "@plane/constants";
+// services
+import { APIService } from "../api.service";
+
+/**
+ * Payload type for AI editor tasks
+ * @typedef {Object} TTaskPayload
+ * @property {number} [casual_score] - Optional score for casual tone analysis
+ * @property {number} [formal_score] - Optional score for formal tone analysis
+ * @property {AI_EDITOR_TASKS} task - Type of AI editor task to perform
+ * @property {string} text_input - The input text to be processed
+ */
+export type TTaskPayload = {
+ casual_score?: number;
+ formal_score?: number;
+ task: AI_EDITOR_TASKS;
+ text_input: string;
+};
+
+/**
+ * Service class for handling AI-related API operations
+ * Extends the base APIService class to interact with AI endpoints
+ * @extends {APIService}
+ */
+export class AIService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Creates a GPT-based task for a specific workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {Object} data - The data payload for the GPT task
+ * @param {string} data.prompt - The prompt text for the GPT model
+ * @param {string} data.task - The type of task to be performed
+ * @returns {Promise} The response data from the GPT task
+ * @throws {Error} Throws the response error if the request fails
+ */
+ async prompt(workspaceSlug: string, data: { prompt: string; task: string }): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/ai-assistant/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response;
+ });
+ }
+
+ /**
+ * Performs an editor-specific AI task for text processing
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {TTaskPayload} data - The task payload containing text and processing parameters
+ * @returns {Promise<{response: string}>} The processed text response
+ * @throws {Error} Throws the response data if the request fails
+ */
+ async rephraseGrammar(
+ workspaceSlug: string,
+ data: TTaskPayload
+ ): Promise<{
+ response: string;
+ }> {
+ return this.post(`/api/workspaces/${workspaceSlug}/rephrase-grammar/`, data)
+ .then((res) => res?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/ai/index.ts b/packages/services/src/ai/index.ts
new file mode 100644
index 000000000..bce346aa6
--- /dev/null
+++ b/packages/services/src/ai/index.ts
@@ -0,0 +1 @@
+export * from "./ai.service";
diff --git a/packages/services/src/analytics/analytics.service.ts b/packages/services/src/analytics/analytics.service.ts
new file mode 100644
index 000000000..c012fd26f
--- /dev/null
+++ b/packages/services/src/analytics/analytics.service.ts
@@ -0,0 +1,93 @@
+// constants
+import { API_BASE_URL } from "@plane/constants";
+// types
+import {
+ IAnalyticsParams,
+ IAnalyticsResponse,
+ IDefaultAnalyticsResponse,
+ IExportAnalyticsFormData,
+ ISaveAnalyticsFormData,
+} from "@plane/types";
+// services
+import { APIService } from "../api.service";
+
+export class AnalyticsService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves analytics data for a specific workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {IAnalyticsParams} params - Parameters for filtering analytics data
+ * @param {string|number} [params.project] - Optional project identifier that will be converted to string
+ * @returns {Promise} The analytics data for the workspace
+ * @throws {Error} Throws response data if the request fails
+ */
+ async getAnalytics(workspaceSlug: string, params: IAnalyticsParams): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/analytics/`, {
+ params: {
+ ...params,
+ project: params?.project ? params.project.toString() : null,
+ },
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves default analytics data for a workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {Partial} [params] - Optional parameters for filtering default analytics
+ * @param {string|number} [params.project] - Optional project identifier that will be converted to string
+ * @returns {Promise} The default analytics data
+ * @throws {Error} Throws response data if the request fails
+ */
+ async getDefaultAnalytics(
+ workspaceSlug: string,
+ params?: Partial
+ ): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/default-analytics/`, {
+ params: {
+ ...params,
+ project: params?.project ? params.project.toString() : null,
+ },
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Saves analytics view configuration for a workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {ISaveAnalyticsFormData} data - The analytics configuration data to save
+ * @returns {Promise} The response from saving the analytics view
+ * @throws {Error} Throws response data if the request fails
+ */
+ async save(workspaceSlug: string, data: ISaveAnalyticsFormData): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/analytic-view/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Exports analytics data for a workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {IExportAnalyticsFormData} data - Configuration for the analytics export
+ * @returns {Promise} The exported analytics data
+ * @throws {Error} Throws response data if the request fails
+ */
+ async export(workspaceSlug: string, data: IExportAnalyticsFormData): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/export-analytics/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/analytics/index.ts b/packages/services/src/analytics/index.ts
new file mode 100644
index 000000000..7655bd442
--- /dev/null
+++ b/packages/services/src/analytics/index.ts
@@ -0,0 +1 @@
+export * from "./analytics.service";
diff --git a/packages/services/src/api.service.ts b/packages/services/src/api.service.ts
new file mode 100644
index 000000000..619a0d4ec
--- /dev/null
+++ b/packages/services/src/api.service.ts
@@ -0,0 +1,123 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
+import { IndexedDBService } from "./indexedDB.service";
+
+/**
+ * Abstract base class for making HTTP requests using axios
+ * @abstract
+ */
+export abstract class APIService {
+ protected baseURL: string;
+ private axiosInstance: AxiosInstance;
+
+ /**
+ * Creates an instance of APIService
+ * @param {string} baseURL - The base URL for all HTTP requests
+ */
+ constructor(baseURL: string) {
+ this.baseURL = baseURL;
+ this.axiosInstance = axios.create({
+ baseURL,
+ withCredentials: true,
+ });
+
+ this.setupInterceptors();
+ }
+
+ /**
+ * Sets up axios interceptors for handling responses
+ * Currently handles 401 unauthorized responses by redirecting to login
+ * @private
+ */
+ private setupInterceptors() {
+ this.axiosInstance.interceptors.response.use(
+ (response) => response,
+ (error) => {
+ if (error.response && error.response.status === 401) {
+ const currentPath = window.location.pathname;
+ let prefix = "/";
+ let updatedPath = currentPath;
+
+ // Check for special path prefixes
+ if (currentPath.startsWith("/god-mode")) {
+ prefix = "/god-mode";
+ updatedPath = currentPath.replace("/god-mode", "");
+ } else if (currentPath.startsWith("/spaces")) {
+ prefix = "/spaces";
+ updatedPath = currentPath.replace("/spaces", "");
+ }
+
+ window.location.replace(`${prefix}${updatedPath ? `?next_path=${updatedPath}` : ""}`);
+ }
+ return Promise.reject(error);
+ }
+ );
+ }
+
+ /**
+ * Makes a GET request to the specified URL
+ * @param {string} url - The endpoint URL
+ * @param {object} [params={}] - URL parameters
+ * @param {AxiosRequestConfig} [config={}] - Additional axios configuration
+ * @returns {Promise} Axios response promise
+ */
+ get(url: string, params = {}, config: AxiosRequestConfig = {}) {
+ return this.axiosInstance.get(url, {
+ ...params,
+ ...config,
+ });
+ }
+
+ /**
+ * Makes a POST request to the specified URL
+ * @param {string} url - The endpoint URL
+ * @param {object} [data={}] - Request body data
+ * @param {AxiosRequestConfig} [config={}] - Additional axios configuration
+ * @returns {Promise} Axios response promise
+ */
+ post(url: string, data = {}, config: AxiosRequestConfig = {}) {
+ return this.axiosInstance.post(url, data, config);
+ }
+
+ /**
+ * Makes a PUT request to the specified URL
+ * @param {string} url - The endpoint URL
+ * @param {object} [data={}] - Request body data
+ * @param {AxiosRequestConfig} [config={}] - Additional axios configuration
+ * @returns {Promise} Axios response promise
+ */
+ put(url: string, data = {}, config: AxiosRequestConfig = {}) {
+ return this.axiosInstance.put(url, data, config);
+ }
+
+ /**
+ * Makes a PATCH request to the specified URL
+ * @param {string} url - The endpoint URL
+ * @param {object} [data={}] - Request body data
+ * @param {AxiosRequestConfig} [config={}] - Additional axios configuration
+ * @returns {Promise} Axios response promise
+ */
+ patch(url: string, data = {}, config: AxiosRequestConfig = {}) {
+ return this.axiosInstance.patch(url, data, config);
+ }
+
+ /**
+ * Makes a DELETE request to the specified URL
+ * @param {string} url - The endpoint URL
+ * @param {any} [data] - Request body data
+ * @param {AxiosRequestConfig} [config={}] - Additional axios configuration
+ * @returns {Promise} Axios response promise
+ */
+ delete(url: string, data?: any, config: AxiosRequestConfig = {}) {
+ return this.axiosInstance.delete(url, { data, ...config });
+ }
+
+ /**
+ * Makes a custom request with the provided configuration
+ * @param {object} [config={}] - Axios request configuration
+ * @returns {Promise} Axios response promise
+ */
+ request(config = {}) {
+ return this.axiosInstance(config);
+ }
+}
diff --git a/packages/services/src/auth/auth.service.ts b/packages/services/src/auth/auth.service.ts
new file mode 100644
index 000000000..1890533d2
--- /dev/null
+++ b/packages/services/src/auth/auth.service.ts
@@ -0,0 +1,125 @@
+import { API_BASE_URL } from "@plane/constants";
+// types
+import { ICsrfTokenData, IEmailCheckData, IEmailCheckResponse } from "@plane/types";
+// services
+import { APIService } from "../api.service";
+
+/**
+ * Service class for handling authentication-related operations
+ * Provides methods for user authentication, password management, and session handling
+ * @extends {APIService}
+ */
+export class AuthService extends APIService {
+ /**
+ * Creates an instance of AuthService
+ * Initializes with the base API URL
+ */
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Requests a CSRF token for form submission security
+ * @returns {Promise} Object containing the CSRF token
+ * @throws {Error} Throws the complete error object if the request fails
+ * @remarks This method uses the validateStatus: null option to bypass interceptors for unauthorized errors.
+ */
+ async requestCSRFToken(): Promise {
+ return this.get("/auth/get-csrf-token/", { validateStatus: null })
+ .then((response) => response.data)
+ .catch((error) => {
+ throw error;
+ });
+ }
+
+ /**
+ * Checks if an email exists in the system
+ * @param {IEmailCheckData} data - Email data to verify
+ * @returns {Promise} Response indicating email status
+ * @throws {Error} Throws response data if the request fails
+ */
+ async emailCheck(data: IEmailCheckData): Promise {
+ return this.post("/auth/email-check/", data, { headers: {} })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Sends a password reset link to the specified email address
+ * @param {{ email: string }} data - Object containing the email address
+ * @returns {Promise} Response from the password reset request
+ * @throws {Error} Throws response object if the request fails
+ */
+ async sendResetPasswordLink(data: { email: string }): Promise {
+ return this.post(`/auth/forgot-password/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response;
+ });
+ }
+
+ /**
+ * Sets a new password using a reset token
+ * @param {string} token - CSRF token for form submission security
+ * @param {{ password: string }} data - Object containing the new password
+ * @returns {Promise} Response from the password update request
+ * @throws {Error} Throws response data if the request fails
+ */
+ async setPassword(token: string, data: { password: string }): Promise {
+ return this.post(`/auth/set-password/`, data, {
+ headers: {
+ "X-CSRFTOKEN": token,
+ },
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Generates a unique code for magic link authentication
+ * @param {{ email: string }} data - Object containing the email address
+ * @returns {Promise} Response containing the generated unique code
+ * @throws {Error} Throws response data if the request fails
+ */
+ async generateUniqueCode(data: { email: string }): Promise {
+ return this.post("/auth/magic-generate/", data, { headers: {} })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Performs user sign out by submitting a form with CSRF token
+ * Creates and submits a form dynamically to handle the sign-out process
+ * @param {string} baseUrl - Base URL for the sign-out endpoint
+ * @returns {Promise} Resolves when sign-out is complete
+ * @throws {Error} Throws error if CSRF token is not found
+ */
+ async signOut(baseUrl: string): Promise {
+ await this.requestCSRFToken().then((data) => {
+ const csrfToken = data?.csrf_token;
+
+ if (!csrfToken) throw Error("CSRF token not found");
+
+ const form = document.createElement("form");
+ const element1 = document.createElement("input");
+
+ form.method = "POST";
+ form.action = `${baseUrl}/auth/sign-out/`;
+
+ element1.value = csrfToken;
+ element1.name = "csrfmiddlewaretoken";
+ element1.type = "hidden";
+ form.appendChild(element1);
+
+ document.body.appendChild(form);
+
+ form.submit();
+ });
+ }
+}
diff --git a/packages/services/src/auth/index.ts b/packages/services/src/auth/index.ts
new file mode 100644
index 000000000..340b36856
--- /dev/null
+++ b/packages/services/src/auth/index.ts
@@ -0,0 +1,2 @@
+export * from "./auth.service";
+export * from "./sites-auth.service";
diff --git a/packages/services/src/auth/sites-auth.service.ts b/packages/services/src/auth/sites-auth.service.ts
new file mode 100644
index 000000000..638a7b6f2
--- /dev/null
+++ b/packages/services/src/auth/sites-auth.service.ts
@@ -0,0 +1,49 @@
+import { API_BASE_URL } from "@plane/constants";
+// types
+import { IEmailCheckData, IEmailCheckResponse } from "@plane/types";
+// services
+import { APIService } from "../api.service";
+
+/**
+ * Service class for handling authentication-related operations for Plane space application
+ * Provides methods for user authentication, password management, and session handling
+ * @extends {APIService}
+ * @remarks This service is only available for plane sites
+ */
+export class SitesAuthService extends APIService {
+ /**
+ * Creates an instance of SitesAuthService
+ * Initializes with the base API URL
+ */
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Checks if an email exists in the system
+ * @param {IEmailCheckData} data - Email data to verify
+ * @returns {Promise} Response indicating email status
+ * @throws {Error} Throws response data if the request fails
+ */
+ async emailCheck(data: IEmailCheckData): Promise {
+ return this.post("/auth/spaces/email-check/", data, { headers: {} })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Generates a unique code for magic link authentication
+ * @param {{ email: string }} data - Object containing the email address
+ * @returns {Promise} Response containing the generated unique code
+ * @throws {Error} Throws response data if the request fails
+ */
+ async generateUniqueCode(data: { email: string }): Promise {
+ return this.post("/auth/spaces/magic-generate/", data, { headers: {} })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/cycle/cycle-analytics.service.ts b/packages/services/src/cycle/cycle-analytics.service.ts
new file mode 100644
index 000000000..c9e14441e
--- /dev/null
+++ b/packages/services/src/cycle/cycle-analytics.service.ts
@@ -0,0 +1,78 @@
+import { API_BASE_URL } from "@plane/constants";
+import type { TCycleDistribution, TProgressSnapshot, TCycleEstimateDistribution } from "@plane/types";
+import { APIService } from "../api.service";
+
+/**
+ * Service class for managing cycles within a workspace and project context.
+ * Extends APIService to handle HTTP requests to the cycle-related endpoints.
+ * @extends {APIService}
+ */
+export class CycleAnalyticsService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves analytics for active cycles in a workspace.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @param {string} [analytic_type="points"] - The type of analytics to retrieve
+ * @returns {Promise} The cycle analytics data
+ * @throws {Error} If the request fails
+ */
+ async workspaceActiveCyclesAnalytics(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string,
+ analytic_type: string = "points"
+ ): Promise {
+ return this.get(
+ `/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/analytics?type=${analytic_type}`
+ )
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves progress data for active cycles.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @returns {Promise} The cycle progress data
+ * @throws {Error} If the request fails
+ */
+ async workspaceActiveCyclesProgress(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string
+ ): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/progress/`)
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves advanced progress data for active cycles (Pro feature).
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @returns {Promise} The detailed cycle progress data
+ * @throws {Error} If the request fails
+ */
+ async workspaceActiveCyclesProgressPro(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string
+ ): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-progress/`)
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/cycle/cycle-archive.service.ts b/packages/services/src/cycle/cycle-archive.service.ts
new file mode 100644
index 000000000..784fd32e7
--- /dev/null
+++ b/packages/services/src/cycle/cycle-archive.service.ts
@@ -0,0 +1,83 @@
+import { API_BASE_URL } from "@plane/constants";
+import { ICycle } from "@plane/types";
+import { APIService } from "../api.service";
+
+/**
+ * Service class for managing archived cycles in a project
+ * Provides methods for retrieving, archiving, and restoring project cycles
+ * @extends {APIService}
+ */
+export class CycleArchiveService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves all archived cycles for a specific project
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {string} projectId - The unique identifier for the project
+ * @returns {Promise} Array of archived cycles
+ * @throws {Error} Throws response data if the request fails
+ */
+ async list(workspaceSlug: string, projectId: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-cycles/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves details of a specific archived cycle
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {string} projectId - The unique identifier for the project
+ * @param {string} cycleId - The unique identifier for the cycle
+ * @returns {Promise} Details of the archived cycle
+ * @throws {Error} Throws response data if the request fails
+ */
+ async retrieve(workspaceSlug: string, projectId: string, cycleId: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-cycles/${cycleId}/`)
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+
+ /**
+ * Archives a specific cycle in a project
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {string} projectId - The unique identifier for the project
+ * @param {string} cycleId - The unique identifier for the cycle to archive
+ * @returns {Promise<{archived_at: string}>} Object containing the archive timestamp
+ * @throws {Error} Throws response data if the request fails
+ */
+ async archive(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string
+ ): Promise<{
+ archived_at: string;
+ }> {
+ return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/archive/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Restores a previously archived cycle
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {string} projectId - The unique identifier for the project
+ * @param {string} cycleId - The unique identifier for the cycle to restore
+ * @returns {Promise} Resolves when the cycle is successfully restored
+ * @throws {Error} Throws response data if the request fails
+ */
+ async restore(workspaceSlug: string, projectId: string, cycleId: string): Promise {
+ return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/archive/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/cycle/cycle-operations.service.ts b/packages/services/src/cycle/cycle-operations.service.ts
new file mode 100644
index 000000000..43c01396c
--- /dev/null
+++ b/packages/services/src/cycle/cycle-operations.service.ts
@@ -0,0 +1,70 @@
+import { API_BASE_URL } from "@plane/constants";
+import { APIService } from "../api.service";
+
+export class CycleOperationsService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Adds a cycle to user favorites.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {{cycle: string}} data - The favorite cycle data
+ * @returns {Promise} The response data
+ * @throws {Error} If the request fails
+ */
+ async addToFavorites(
+ workspaceSlug: string,
+ projectId: string,
+ data: {
+ cycle: string;
+ }
+ ): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/user-favorite-cycles/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Removes a cycle from user favorites.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @returns {Promise} The removal response
+ * @throws {Error} If the request fails
+ */
+ async removeFromFavorites(workspaceSlug: string, projectId: string, cycleId: string): Promise {
+ return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/user-favorite-cycles/${cycleId}/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Transfers issues between cycles.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The source cycle identifier
+ * @param {{new_cycle_id: string}} data - The target cycle data
+ * @returns {Promise} The transfer response
+ * @throws {Error} If the request fails
+ */
+ async transferIssues(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string,
+ data: {
+ new_cycle_id: string;
+ }
+ ): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/transfer-issues/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/cycle/cycle.service.ts b/packages/services/src/cycle/cycle.service.ts
new file mode 100644
index 000000000..961e5588a
--- /dev/null
+++ b/packages/services/src/cycle/cycle.service.ts
@@ -0,0 +1,184 @@
+import { API_BASE_URL } from "@plane/constants";
+import type { CycleDateCheckData, ICycle, TIssuesResponse, IWorkspaceActiveCyclesResponse } from "@plane/types";
+import { APIService } from "../api.service";
+
+/**
+ * Service class for managing cycles within a workspace and project context.
+ * Extends APIService to handle HTTP requests to the cycle-related endpoints.
+ * @extends {APIService}
+ */
+export class CycleService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves paginated list of active cycles in a workspace.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} cursor - The pagination cursor
+ * @param {number} per_page - Number of items per page
+ * @returns {Promise} Paginated active cycles data
+ * @throws {Error} If the request fails
+ */
+ async workspaceActiveCycles(
+ workspaceSlug: string,
+ cursor: string,
+ per_page: number
+ ): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/active-cycles/`, {
+ params: {
+ per_page,
+ cursor,
+ },
+ })
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+
+ /**
+ * Gets all cycles in a workspace.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @returns {Promise} Array of cycle objects
+ * @throws {Error} If the request fails
+ */
+ async getWorkspaceCycles(workspaceSlug: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/cycles/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Creates a new cycle in a project.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {any} data - The cycle creation data
+ * @returns {Promise} The created cycle object
+ * @throws {Error} If the request fails
+ */
+ async create(workspaceSlug: string, projectId: string, data: any): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves cycles with optional filtering parameters.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {"current"} [cycleType] - Optional filter for cycle type
+ * @returns {Promise} Array of filtered cycle objects
+ * @throws {Error} If the request fails
+ */
+ async getWithParams(workspaceSlug: string, projectId: string, cycleType?: "current"): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/`, {
+ params: {
+ cycle_view: cycleType,
+ },
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves detailed information for a specific cycle.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @returns {Promise} The cycle details
+ * @throws {Error} If the request fails
+ */
+ async retrieve(workspaceSlug: string, projectId: string, cycleId: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`)
+ .then((res) => res?.data)
+ .catch((err) => {
+ throw err?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves issues associated with a specific cycle.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @param {any} [queries] - Optional query parameters
+ * @param {object} [config={}] - Optional request configuration
+ * @returns {Promise} The cycle issues data
+ * @throws {Error} If the request fails
+ */
+ async getCycleIssues(
+ workspaceSlug: string,
+ projectId: string,
+ cycleId: string,
+ queries?: any,
+ config = {}
+ ): Promise {
+ return this.get(
+ `/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`,
+ {
+ params: queries,
+ },
+ config
+ )
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Updates a cycle with partial data.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @param {Partial} data - The partial cycle data to update
+ * @returns {Promise} The update response
+ * @throws {Error} If the request fails
+ */
+ async update(workspaceSlug: string, projectId: string, cycleId: string, data: Partial): Promise {
+ return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Deletes a specific cycle.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {string} cycleId - The cycle identifier
+ * @returns {Promise} The deletion response
+ * @throws {Error} If the request fails
+ */
+ async destroy(workspaceSlug: string, projectId: string, cycleId: string): Promise {
+ return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Validates cycle dates.
+ * @param {string} workspaceSlug - The workspace identifier
+ * @param {string} projectId - The project identifier
+ * @param {CycleDateCheckData} data - The date check data
+ * @returns {Promise} The validation response
+ * @throws {Error} If the request fails
+ */
+ async validateDates(workspaceSlug: string, projectId: string, data: CycleDateCheckData): Promise {
+ return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/date-check/`, data)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/cycle/index.ts b/packages/services/src/cycle/index.ts
new file mode 100644
index 000000000..6f5dbc679
--- /dev/null
+++ b/packages/services/src/cycle/index.ts
@@ -0,0 +1,5 @@
+export * from "./cycle-analytics.service";
+export * from "./cycle-archive.service";
+export * from "./cycle-operations.service";
+export * from "./cycle.service";
+export * from "./sites-cycle.service";
diff --git a/packages/services/src/cycle/sites-cycle.service.ts b/packages/services/src/cycle/sites-cycle.service.ts
new file mode 100644
index 000000000..99cf361a7
--- /dev/null
+++ b/packages/services/src/cycle/sites-cycle.service.ts
@@ -0,0 +1,31 @@
+// plane imports
+import { API_BASE_URL } from "@plane/constants";
+import { TPublicCycle } from "@plane/types";
+// api service
+import { APIService } from "../api.service";
+
+/**
+ * Service class for managing cycles within plane sites application.
+ * Extends APIService to handle HTTP requests to the cycle-related endpoints.
+ * @extends {APIService}
+ * @remarks This service is only available for plane sites
+ */
+export class SitesCycleService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves list of cycles for a specific anchor.
+ * @param anchor - The anchor identifier for the published entity
+ * @returns {Promise} The list of cycles
+ * @throws {Error} If the request fails
+ */
+ async list(anchor: string): Promise {
+ return this.get(`/api/public/anchor/${anchor}/cycles/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+}
diff --git a/packages/services/src/dashboard/dashboard.service.ts b/packages/services/src/dashboard/dashboard.service.ts
new file mode 100644
index 000000000..6a4a6bcca
--- /dev/null
+++ b/packages/services/src/dashboard/dashboard.service.ts
@@ -0,0 +1,79 @@
+import { API_BASE_URL } from "@plane/constants";
+import { THomeDashboardResponse, TWidget, TWidgetStatsResponse, TWidgetStatsRequestParams } from "@plane/types";
+import { APIService } from "../api.service";
+
+export default class DashboardService extends APIService {
+ constructor(BASE_URL?: string) {
+ super(BASE_URL || API_BASE_URL);
+ }
+
+ /**
+ * Retrieves home dashboard widgets for a specific workspace
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @returns {Promise} Promise resolving to dashboard widget data
+ * @throws {Error} If the API request fails
+ */
+ async getHomeWidgets(workspaceSlug: string): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/dashboard/`, {
+ params: {
+ dashboard_type: "home",
+ },
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Fetches statistics for a specific dashboard widget
+ * @param {string} workspaceSlug - The unique identifier for the workspace
+ * @param {string} dashboardId - The unique identifier for the dashboard
+ * @param {TWidgetStatsRequestParams} params - Parameters for filtering widget statistics
+ * @returns {Promise} Promise resolving to widget statistics data
+ * @throws {Error} If the API request fails
+ */
+ async getWidgetStats(
+ workspaceSlug: string,
+ dashboardId: string,
+ params: TWidgetStatsRequestParams
+ ): Promise {
+ return this.get(`/api/workspaces/${workspaceSlug}/dashboard/${dashboardId}/`, {
+ params,
+ })
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Retrieves detailed information about a specific dashboard
+ * @param {string} dashboardId - The unique identifier for the dashboard
+ * @returns {Promise} Promise resolving to dashboard details
+ * @throws {Error} If the API request fails
+ */
+ async retrieve(dashboardId: string): Promise {
+ return this.get(`/api/dashboard/${dashboardId}/`)
+ .then((response) => response?.data)
+ .catch((error) => {
+ throw error?.response?.data;
+ });
+ }
+
+ /**
+ * Updates a specific widget within a dashboard
+ * @param {string} dashboardId - The unique identifier for the dashboard
+ * @param {string} widgetId - The unique identifier for the widget
+ * @param {Partial} data - Partial widget data to update
+ * @returns {Promise} Promise resolving to the updated widget data
+ * @throws {Error} If the API request fails
+ */
+ async updateWidget(dashboardId: string, widgetId: string, data: Partial