diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index e115a99ec67..50c1a1fdb94 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -156,7 +156,6 @@ "enter_god_mode": "Enter god mode", "workspace_logo": "Workspace logo", "new_issue": "New issue", - "home": "Home", "your_work": "Your work", "drafts": "Drafts", "projects": "Projects", @@ -313,9 +312,137 @@ "remove_parent_issue": "Remove parent issue", "add_parent": "Add parent", "loading_members": "Loading members...", - "no_data_yet": "No Data yet", + "view_link_copied_to_clipboard": "View link copied to clipboard.", + "required": "Required", + "optional": "Optional", + "Cancel": "Cancel", + "Update": "Update", + "Add": "Add", + "Updating": "Updating", + "Adding": "Adding", + "edit": "Edit", + "open_in_new_tab": "Open in new tab", + "delete": "Delete", + "good": "Good", + "morning": "morning", + "afternoon": "afternoon", + "evening": "evening", + "show_all": "Show all", + "show_less": "Show less", + "no_data_yet": "No Data yet", "connections": "Connections", + "toast": { + "success": "Success!", + "error": "Error!" + }, + + "home": { + "empty": { + "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", + "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" + } + } + }, + "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 issues will appear here once you visit one.", + "default": "You don't have any recent items yet." + }, + "filters": { + "all": "All items", + "projects": "Projects", + "pages": "Pages", + "issues": "Issues" + } + }, + "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", @@ -932,33 +1059,60 @@ }, "stickies": { - "empty_state": { - "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" - } + "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" + } + } }, - "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" - } - } - } - }, - - "home_widgets": { - "empty_state": { - "general": { - "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" - } + "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" + } } - } } } diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json index 557abd6bb51..d46a3215472 100644 --- a/packages/i18n/src/locales/es/translations.json +++ b/packages/i18n/src/locales/es/translations.json @@ -155,7 +155,6 @@ "enter_god_mode": "Entrar en modo dios", "workspace_logo": "Logo del espacio de trabajo", "new_issue": "Nuevo problema", - "home": "Inicio", "your_work": "Tu trabajo", "drafts": "Borradores", "projects": "Proyectos", @@ -312,9 +311,142 @@ "remove_parent_issue": "Eliminar problema 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", + "Update": "Actualizar", + "Add": "Añadir", + "Updating": "Actualizando", + "Adding": "Añadiendo", + "edit": "Editar", + "open_in_new_tab": "Abrir en nueva pestaña", + "delete": "Eliminar", + "good": "Buenos", + "morning": "días", + "afternoon": "tardes", + "evening": "noches", + "show_all": "Mostrar todo", + "show_less": "Mostrar menos", "no_data_yet": "Sin datos aún", "connections": "Conexiones", + "toast": { + "success": "¡Éxito!", + "error": "¡Error!" + }, + + "home": { + "empty": { + "create_project": { + "title": "Crear un proyecto", + "description": "La mayoría de las cosas comienzan con un proyecto en Plane.", + "cta": "Comenzar" + }, + "invite_team": { + "title": "Invitar a tu equipo", + "description": "Construye, implementa y gestiona con tus compañeros.", + "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" + }, + "personalize_account": { + "title": "Haz Plane tuyo", + "description": "Elige tu foto, colores y más.", + "cta": "Personalizar ahora" + }, + "widgets": { + "title": "Está tranquilo sin widgets, actívalos", + "description": "Parece que todos tus widgets están desactivados. ¡Enciéndelos ahora 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": "Añadir enlace rápido", + "title": "Enlace rápido", + "title_plural": "Enlaces rápidos", + "toasts": { + "created": { + "title": "Enlace creado", + "message": "El enlace se ha creado exitosamente" + }, + "not_created": { + "title": "Enlace no creado", + "message": "No se pudo crear el enlace" + }, + "updated": { + "title": "Enlace actualizado", + "message": "El enlace se ha actualizado exitosamente" + }, + "not_updated": { + "title": "Enlace no actualizado", + "message": "No se pudo actualizar el enlace" + }, + "removed": { + "title": "Enlace eliminado", + "message": "El enlace se ha eliminado exitosamente" + }, + "not_removed": { + "title": "Enlace no eliminado", + "message": "No se pudo eliminar el enlace" + } + + } + }, + "recents": { + "title": "Recientes", + "empty": { + "project": "Tus proyectos recientes aparecerán aquí cuando visites uno.", + "page": "Tus páginas recientes aparecerán aquí cuando visites una.", + "issue": "Tus problemas recientes aparecerán aquí cuando visites uno.", + "default": "Aún no tienes elementos recientes." + }, + "filters": { + "all": "Todos los elementos", + "projects": "Proyectos", + "pages": "Páginas", + "issues": "Problemas" + } + }, + "my_stickies": { + "title": "Tus notas" + }, + "new_at_plane": { + "title": "Nuevo en Plane" + }, + "quick_tutorial": { + "title": "Tutorial rápido" + }, + "widget": { + "reordered_successfully": "Widget reordenado exitosamente.", + "reordering_failed": "Ocurrió un error al reordenar el widget." + }, + "manage_widgets": "Administrar 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 de visualización", + "placeholder": "Cómo te gustaría ver este enlace" + } + } + }, + "common": { "all": "Todos", "states": "Estados", @@ -931,32 +1063,59 @@ }, "stickies": { + "title": "Tus notas adhesivas", + "placeholder": "haz clic para escribir aquí", + "all": "Todas las notas", + "no-data": "Anota una idea, captura un momento eureka o registra una inspiración. Añade una nota para empezar.", + "add": "Añadir nota", + "search_placeholder": "Buscar por título", + "delete": "Eliminar nota", + "delete_confirmation": "¿Estás seguro de que quieres eliminar esta nota?", "empty_state": { + "simple": "Anota una idea, captura un momento eureka o registra una inspiración. Añade una nota para empezar.", "general": { - "title": "Los stickies son notas rápidas y tareas que tomas al vuelo.", - "description": "Captura tus pensamientos e ideas sin esfuerzo creando stickies a los que puedes acceder en cualquier momento y lugar.", + "title": "Las notas son apuntes rápidos y tareas que tomas sobre la marcha.", + "description": "Captura tus pensamientos e ideas sin esfuerzo creando notas que puedes acceder en cualquier momento y desde cualquier lugar.", "primary_button": { - "text": "Agregar sticky" + "text": "Añadir nota" } }, "search": { - "title": "Eso no coincide con ninguno de tus stickies.", - "description": "Prueba con un término diferente o avísanos si estás seguro de que tu búsqueda es correcta.", + "title": "Esto no coincide con ninguna de tus notas.", + "description": "Prueba un término diferente o avísanos\nsi estás seguro de que tu búsqueda es correcta.", "primary_button": { - "text": "Agregar sticky" + "text": "Añadir nota" } } - } - }, - - "home_widgets": { - "empty_state": { - "general": { - "title": "Está tranquilo sin widgets, actívalos", - "description": "Parece que todos tus widgets están desactivados. ¡Enciéndelos ahora para mejorar tu experiencia!", - "primary_button": { - "text": "Gestionar widgets" - } + }, + "toasts": { + "errors": { + "wrong_name": "El nombre de la nota no puede tener más de 100 caracteres.", + "already_exists": "Ya existe una nota sin descripción" + }, + "created": { + "title": "Nota creada", + "message": "La nota se ha creado con éxito" + }, + "not_created": { + "title": "Nota no creada", + "message": "No se pudo crear la nota" + }, + "updated": { + "title": "Nota actualizada", + "message": "La nota se ha actualizado con éxito" + }, + "not_updated": { + "title": "Nota no actualizada", + "message": "No se pudo actualizar la nota" + }, + "removed": { + "title": "Nota eliminada", + "message": "La nota se ha eliminado con éxito" + }, + "not_removed": { + "title": "Nota no eliminada", + "message": "No se pudo eliminar la nota" } } } diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json index 4a5594a81d9..7da5c4453a7 100644 --- a/packages/i18n/src/locales/fr/translations.json +++ b/packages/i18n/src/locales/fr/translations.json @@ -156,7 +156,6 @@ "enter_god_mode": "Entrer en mode dieu", "workspace_logo": "Logo de l'espace de travail", "new_issue": "Nouveau problème", - "home": "Accueil", "your_work": "Votre travail", "drafts": "Brouillons", "projects": "Projets", @@ -314,9 +313,140 @@ "remove_parent_issue": "Supprimer le problème parent", "add_parent": "Ajouter un parent", "loading_members": "Chargement des membres...", + + "view_link_copied_to_clipboard": "Lien de vue copié dans le presse-papiers.", + "required": "Requis", + "optional": "Optionnel", + "Cancel": "Annuler", + "Update": "Mettre à jour", + "Add": "Ajouter", + "Updating": "Mise à jour", + "Adding": "Ajout", + "edit": "Modifier", + "open_in_new_tab": "Ouvrir dans un nouvel onglet", + "delete": "Supprimer", + "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", "connections": "Connexions", + "toast": { + "success": "Succès !", + "error": "Erreur !" + }, + + "home": { + "empty": { + "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" + }, + "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 maintenant 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", + "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é" + } + } + }, + "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 problèmes 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": "Problèmes" + } + }, + "my_stickies": { + "title": "Vos notes" + }, + "new_at_plane": { + "title": "Nouveau chez 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": "Étoilez-nous 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", @@ -929,32 +1059,59 @@ }, "stickies": { + "title": "Vos pense-bêtes", + "placeholder": "cliquez pour écrire ici", + "all": "Tous les pense-bêtes", + "no-data": "Notez une idée, capturez un éclair de génie ou enregistrez une réflexion. Ajoutez un pense-bête pour commencer.", + "add": "Ajouter un pense-bête", + "search_placeholder": "Rechercher par titre", + "delete": "Supprimer le pense-bête", + "delete_confirmation": "Êtes-vous sûr de vouloir supprimer ce pense-bête ?", "empty_state": { + "simple": "Notez une idée, capturez un éclair de génie ou enregistrez une réflexion. Ajoutez un pense-bête pour commencer.", "general": { - "title": "Les stickies sont des notes rapides et des tâches que vous notez à la volée.", - "description": "Capturez vos pensées et vos idées facilement en créant des stickies accessibles à tout moment et de n'importe où.", + "title": "Les pense-bêtes sont des notes rapides et des tâches que vous prenez à la volée.", + "description": "Capturez vos pensées et vos idées sans effort en créant des pense-bêtes accessibles à tout moment et de n'importe où.", "primary_button": { - "text": "Ajouter un sticky" + "text": "Ajouter un pense-bête" } }, "search": { - "title": "Aucun sticky ne correspond à votre recherche.", - "description": "Essayez un autre terme ou faites-le nous savoir si vous êtes sûr que votre recherche est correcte.", + "title": "Cela ne correspond à aucun de vos pense-bêtes.", + "description": "Essayez un terme différent ou informez-nous\nsi vous êtes sûr que votre recherche est correcte.", "primary_button": { - "text": "Ajouter un sticky" + "text": "Ajouter un pense-bête" } } - } - }, - - "home_widgets": { - "empty_state": { - "general": { - "title": "C'est calme sans widgets, activez-les", - "description": "Il semble que tous vos widgets soient désactivés. Activez-les maintenant pour améliorer votre expérience !", - "primary_button": { - "text": "Gérer les widgets" - } + }, + "toasts": { + "errors": { + "wrong_name": "Le nom du pense-bête ne peut pas dépasser 100 caractères.", + "already_exists": "Il existe déjà un pense-bête sans description" + }, + "created": { + "title": "Pense-bête créé", + "message": "Le pense-bête a été créé avec succès" + }, + "not_created": { + "title": "Pense-bête non créé", + "message": "Le pense-bête n'a pas pu être créé" + }, + "updated": { + "title": "Pense-bête mis à jour", + "message": "Le pense-bête a été mis à jour avec succès" + }, + "not_updated": { + "title": "Pense-bête non mis à jour", + "message": "Le pense-bête n'a pas pu être mis à jour" + }, + "removed": { + "title": "Pense-bête supprimé", + "message": "Le pense-bête a été supprimé avec succès" + }, + "not_removed": { + "title": "Pense-bête non supprimé", + "message": "Le pense-bête n'a pas pu être supprimé" } } } diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json index 4f53184200a..6bea30140c2 100644 --- a/packages/i18n/src/locales/ja/translations.json +++ b/packages/i18n/src/locales/ja/translations.json @@ -156,7 +156,6 @@ "enter_god_mode": "ゴッドモードに入る", "workspace_logo": "ワークスペースロゴ", "new_issue": "新しい問題", - "home": "ホーム", "your_work": "あなたの作業", "drafts": "下書き", "projects": "プロジェクト", @@ -313,9 +312,140 @@ "remove_parent_issue": "親問題を削除", "add_parent": "親問題を追加", "loading_members": "メンバーを読み込んでいます...", + + "view_link_copied_to_clipboard": "表示リンクがクリップボードにコピーされました。", + "required": "必須", + "optional": "任意", + "Cancel": "キャンセル", + "Update": "更新", + "Add": "追加", + "Updating": "更新中", + "Adding": "追加中", + "edit": "編集", + "open_in_new_tab": "新しいタブで開く", + "delete": "削除", + "good": "お", + "morning": "はよう", + "afternoon": "昼", + "evening": "晩は", + "show_all": "すべて表示", + "show_less": "表示を減らす", "no_data_yet": "まだデータがありません", "connections": "接続", + "toast": { + "success": "成功!", + "error": "エラー!" + }, + + "home": { + "empty": { + "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": "すべてのウィジェットがオフになっているようです。 今すぐ有効にして、体験を向上させましょう!", + "primary_button": { + "text": "ウィジェットを管理" + } + } + }, + "quick_links": { + "empty": "すぐに使いたい作業用リンクを保存します。", + "add": "クイックリンクを追加", + "title": "クイックリンク", + "title_plural": "クイックリンク", + "toasts": { + "created": { + "title": "リンクが作成されました", + "message": "リンクの作成に成功しました" + }, + "not_created": { + "title": "リンクが作成されませんでした", + "message": "リンクを作成できませんでした" + }, + "updated": { + "title": "リンクが更新されました", + "message": "リンクの更新に成功しました" + }, + "not_updated": { + "title": "リンクが更新されませんでした", + "message": "リンクを更新できませんでした" + }, + "removed": { + "title": "リンクが削除されました", + "message": "リンクの削除に成功しました" + }, + "not_removed": { + "title": "リンクが削除されませんでした", + "message": "リンクを削除できませんでした" + } + } + }, + "recents": { + "title": "最近", + "empty": { + "project": "プロジェクトを訪問すると、最近のプロジェクトがここに表示されます。", + "page": "ページを訪問すると、最近のページがここに表示されます。", + "issue": "課題を訪問すると、最近の課題がここに表示されます。", + "default": "最近のアイテムはまだありません。" + }, + "filters": { + "all": "すべてのアイテム", + "projects": "プロジェクト", + "pages": "ページ", + "issues": "課題" + } + }, + "my_stickies": { + "title": "付箋" + }, + "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": "ステータス", @@ -932,32 +1062,59 @@ }, "stickies": { + "title": "付箋メモ", + "placeholder": "ここをクリックして入力", + "all": "すべての付箋", + "no-data": "アイデアをメモしたり、ひらめきやアイデアを記録しましょう。付箋を追加して始めましょう。", + "add": "付箋を追加", + "search_placeholder": "タイトルで検索", + "delete": "付箋を削除", + "delete_confirmation": "この付箋を削除してもよろしいですか?", "empty_state": { + "simple": "アイデアをメモしたり、ひらめきやアイデアを記録しましょう。付箋を追加して始めましょう。", "general": { - "title": "スティッキーは、その場で素早く取るメモやタスクです。", - "description": "いつでもどこでもアクセスできるスティッキーを作成して、思いついたアイデアを簡単にキャプチャしましょう。", + "title": "付箋は、その場で書き留める簡単なメモやToDoリストです。", + "description": "いつでもどこからでもアクセスできる付箋を作成して、思いやアイデアを簡単に記録できます。", "primary_button": { - "text": "スティッキーを追加" + "text": "付箋を追加" } }, "search": { - "title": "一致するスティッキーはありません。", - "description": "別の用語を試すか、検索が正しい場合はお知らせください。", + "title": "該当する付箋が見つかりません。", + "description": "別の検索語を試すか、\n検索が正しい場合はお知らせください。", "primary_button": { - "text": "スティッキーを追加" + "text": "付箋を追加" } } - } - }, - - "home_widgets": { - "empty_state": { - "general": { - "title": "ウィジェットがないと静かですね、オンにしましょう", - "description": "すべてのウィジェットがオフになっているようです。 今すぐ有効にして、体験を向上させましょう!", - "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": "付箋を削除できませんでした" } } } diff --git a/packages/i18n/src/locales/zh-CN/translations.json b/packages/i18n/src/locales/zh-CN/translations.json index 12c85b57f7b..e00cd5bc75f 100644 --- a/packages/i18n/src/locales/zh-CN/translations.json +++ b/packages/i18n/src/locales/zh-CN/translations.json @@ -158,7 +158,6 @@ "enter_god_mode": "进入上帝模式", "workspace_logo": "工作区徽标", "new_issue": "新建问题", - "home": "首页", "your_work": "您的工作", "drafts": "草稿", "projects": "项目", @@ -315,6 +314,118 @@ "add_parent": "添加父问题", "loading_members": "正在加载成员...", "inbox": "收件箱", + "show_all": "显示全部", + "show_less": "显示较少", + "no_data_yet": "暂无数据", + + "toast": { + "success": "成功!", + "error": "错误!" + }, + + "home": { + "empty": { + "create_project": { + "title": "创建项目", + "description": "在Plane中,大多数事情都从项目开始。", + "cta": "开始使用" + }, + "invite_team": { + "title": "邀请团队", + "description": "与同事一起构建、部署和管理。", + "cta": "邀请成员" + }, + "configure_workspace": { + "title": "设置工作空间", + "description": "开启或关闭功能,或进行更多设置。", + "cta": "配置此工作空间" + }, + "personalize_account": { + "title": "让Plane更个性化", + "description": "选择您的头像、颜色等更多设置。", + "cta": "立即个性化" + } + }, + "quick_links": { + "empty": "保存您想随手可得的工作相关链接。", + "add": "添加快捷链接", + "title": "快捷链接", + "title_plural": "快捷链接", + "toasts": { + "created": { + "title": "链接已创建", + "message": "链接已成功创建" + }, + "not_created": { + "title": "链接未创建", + "message": "无法创建链接" + }, + "updated": { + "title": "链接已更新", + "message": "链接已成功更新" + }, + "not_updated": { + "title": "链接未更新", + "message": "无法更新链接" + }, + "removed": { + "title": "链接已删除", + "message": "链接已成功删除" + }, + "not_removed": { + "title": "链接未删除", + "message": "无法删除链接" + } + } + }, + "recents": { + "title": "最近", + "empty": { + "project": "访问项目后,您的最近项目将显示在这里。", + "page": "访问页面后,您的最近页面将显示在这里。", + "issue": "访问问题后,您的最近问题将显示在这里。", + "default": "您还没有任何最近项目。" + }, + "filters": { + "all": "所有项目", + "projects": "项目", + "pages": "页面", + "issues": "问题" + } + }, + "my_stickies": { + "title": "您的便签" + }, + "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": "网址", + "required": "网址无效", + "placeholder": "输入或粘贴网址" + }, + "title": { + "text": "显示标题", + "placeholder": "您希望将此链接显示为什么" + } + } + }, "common": { "all": "全部", "states": "状态", @@ -495,5 +606,62 @@ "subscribed": "已订阅", "activity": "活动" } + }, + "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": "无法删除便签" + } + } } } diff --git a/web/app/[workspaceSlug]/(projects)/header.tsx b/web/app/[workspaceSlug]/(projects)/header.tsx index a7a19afe6b0..d85f90507a7 100644 --- a/web/app/[workspaceSlug]/(projects)/header.tsx +++ b/web/app/[workspaceSlug]/(projects)/header.tsx @@ -7,6 +7,7 @@ import { Home } from "lucide-react"; import githubBlackImage from "/public/logos/github-black.png"; import githubWhiteImage from "/public/logos/github-white.png"; // ui +import { useTranslation } from "@plane/i18n"; import { Breadcrumbs, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common"; @@ -19,6 +20,7 @@ export const WorkspaceDashboardHeader = () => { // hooks const { captureEvent } = useEventTracker(); const { resolvedTheme } = useTheme(); + const { t } = useTranslation(); return ( <> @@ -28,7 +30,9 @@ export const WorkspaceDashboardHeader = () => { } />} + link={ + } /> + } /> @@ -51,7 +55,7 @@ export const WorkspaceDashboardHeader = () => { width={16} alt="GitHub Logo" /> - Star us on GitHub + {t("home.star_us_on_github")} diff --git a/web/app/[workspaceSlug]/(projects)/page.tsx b/web/app/[workspaceSlug]/(projects)/page.tsx index 52f0faffefb..7a808decd2f 100644 --- a/web/app/[workspaceSlug]/(projects)/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/page.tsx @@ -2,6 +2,7 @@ import { observer } from "mobx-react"; // components +import { useTranslation } from "@plane/i18n"; import { PageHead, AppHeader, ContentWrapper } from "@/components/core"; import { WorkspaceHomeView } from "@/components/home"; // hooks @@ -11,8 +12,9 @@ import { WorkspaceDashboardHeader } from "./header"; const WorkspaceDashboardPage = observer(() => { const { currentWorkspace } = useWorkspace(); + const { t } = useTranslation(); // derived values - const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Home` : undefined; + const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - ${t("home.title")}` : undefined; return ( <> diff --git a/web/core/components/command-palette/command-modal.tsx b/web/core/components/command-palette/command-modal.tsx index 8422288cf51..39543062649 100644 --- a/web/core/components/command-palette/command-modal.tsx +++ b/web/core/components/command-palette/command-modal.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { Command } from "cmdk"; -import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import useSWR from "swr"; @@ -41,6 +40,7 @@ import { IssueIdentifier } from "@/plane-web/components/issues"; import { WorkspaceService } from "@/plane-web/services"; // services import { IssueService } from "@/services/issue"; +import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; const workspaceService = new WorkspaceService(); const issueService = new IssueService(); diff --git a/web/core/components/core/content-overflow-HOC.tsx b/web/core/components/core/content-overflow-HOC.tsx index 1b37b0429af..f8089c34bc0 100644 --- a/web/core/components/core/content-overflow-HOC.tsx +++ b/web/core/components/core/content-overflow-HOC.tsx @@ -1,5 +1,6 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { cn } from "@plane/utils"; interface IContentOverflowWrapper { @@ -31,6 +32,9 @@ export const ContentOverflowWrapper = observer((props: IContentOverflowWrapper) const contentRef = useRef(null); const containerRef = useRef(null); + // hooks + const { t } = useTranslation(); + useEffect(() => { if (!contentRef?.current) return; @@ -142,7 +146,7 @@ export const ContentOverflowWrapper = observer((props: IContentOverflowWrapper) onClick={handleToggle} disabled={isTransitioning} > - {showAll ? "Show less" : "Show all"} + {showAll ? t("show_less") : t("show_all")} )} diff --git a/web/core/components/home/home-dashboard-widgets.tsx b/web/core/components/home/home-dashboard-widgets.tsx index 1d0be0c5b03..4b8ed0a29bc 100644 --- a/web/core/components/home/home-dashboard-widgets.tsx +++ b/web/core/components/home/home-dashboard-widgets.tsx @@ -25,27 +25,27 @@ export const HOME_WIDGETS_LIST: { quick_links: { component: DashboardQuickLinks, fullWidth: false, - title: "Quicklinks", + title: "home.quick_links.title_plural", }, recents: { component: RecentActivityWidget, fullWidth: false, - title: "Recents", + title: "home.recents.title", }, my_stickies: { component: StickiesWidget, fullWidth: false, - title: "Your stickies", + title: "home.my_stickies.title", }, new_at_plane: { component: null, fullWidth: false, - title: "New at Plane", + title: "home.new_at_plane.title", }, quick_tutorial: { component: null, fullWidth: false, - title: "Quick tutorial", + title: "home.quick_tutorial.title", }, }; @@ -85,8 +85,8 @@ export const DashboardWidgets = observer(() => { ) : (
diff --git a/web/core/components/home/index.ts b/web/core/components/home/index.ts index d783e089a0e..09005cb24a2 100644 --- a/web/core/components/home/index.ts +++ b/web/core/components/home/index.ts @@ -1,4 +1,3 @@ export * from "./widgets"; export * from "./home-dashboard-widgets"; -export * from "./project-empty-state"; export * from "./root"; diff --git a/web/core/components/home/project-empty-state.tsx b/web/core/components/home/project-empty-state.tsx deleted file mode 100644 index c8cba817a37..00000000000 --- a/web/core/components/home/project-empty-state.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { observer } from "mobx-react"; -import Image from "next/image"; -// ui -import { Button } from "@plane/ui"; -// hooks -import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store"; -import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; -// assets -import ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp"; - -export const DashboardProjectEmptyState = observer(() => { - // store hooks - const { toggleCreateProjectModal } = useCommandPalette(); - const { setTrackElement } = useEventTracker(); - const { allowPermissions } = useUserPermissions(); - - // derived values - const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE); - - return ( -
-

Overview of your projects, activity, and metrics

-

- Welcome to Plane, we are excited to have you here. Create your first project and track your issues, and this - page will transform into a space that helps you progress. Admins will also see items which help their team - progress. -

- Project empty state - {canCreateProject && ( -
- -
- )} -
- ); -}); diff --git a/web/core/components/home/user-greetings.tsx b/web/core/components/home/user-greetings.tsx index 82a94cc8062..31fbab185ea 100644 --- a/web/core/components/home/user-greetings.tsx +++ b/web/core/components/home/user-greetings.tsx @@ -1,6 +1,7 @@ import { FC } from "react"; import { Shapes } from "lucide-react"; // plane types +import { useTranslation } from "@plane/i18n"; import { IUser } from "@plane/types"; // plane ui import { Button } from "@plane/ui"; @@ -16,6 +17,8 @@ export const UserGreetingsView: FC = (props) => { const { user, handleWidgetModal } = props; // current time hook const { currentTime } = useCurrentTime(); + // store hooks + const { t } = useTranslation(); const hour = new Intl.DateTimeFormat("en-US", { hour12: false, @@ -44,7 +47,7 @@ export const UserGreetingsView: FC = (props) => {

- Good {greeting}, {user?.first_name} {user?.last_name} + {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name}

{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙️"}
@@ -55,7 +58,7 @@ export const UserGreetingsView: FC = (props) => {
); diff --git a/web/core/components/home/widgets/empty-states/links.tsx b/web/core/components/home/widgets/empty-states/links.tsx index 40df6aedeee..37e04fea730 100644 --- a/web/core/components/home/widgets/empty-states/links.tsx +++ b/web/core/components/home/widgets/empty-states/links.tsx @@ -1,10 +1,14 @@ import { Link2 } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; -export const LinksEmptyState = () => ( -
-
- +export const LinksEmptyState = () => { + const { t } = useTranslation(); + return ( +
+
+ +
{t("home.quick_links.empty")}
+
-

Save links to work things that you{"'"}d like handy.

-
-); + ); +}; diff --git a/web/core/components/home/widgets/empty-states/no-projects.tsx b/web/core/components/home/widgets/empty-states/no-projects.tsx index c3b9a8807a7..748538616e3 100644 --- a/web/core/components/home/widgets/empty-states/no-projects.tsx +++ b/web/core/components/home/widgets/empty-states/no-projects.tsx @@ -3,7 +3,7 @@ import Link from "next/link"; import { useParams } from "next/navigation"; import { Briefcase, Hotel, Users } from "lucide-react"; // plane ui -import { Avatar } from "@plane/ui"; +import { useTranslation } from "@plane/i18n"; // helpers import { getFileURL } from "@/helpers/file.helper"; // hooks @@ -19,6 +19,7 @@ export const NoProjectsEmptyState = () => { const { toggleCreateProjectModal } = useCommandPalette(); const { setTrackElement } = useEventTracker(); const { data: currentUser } = useUser(); + const { t } = useTranslation(); // derived values const canCreateProject = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -28,11 +29,11 @@ export const NoProjectsEmptyState = () => { const EMPTY_STATE_DATA = [ { id: "create-project", - title: "Create a project.", - description: "Most things start with a project in Plane.", - icon: , + title: "home.empty.create_project.title", + description: "home.empty.create_project.description", + icon: , cta: { - text: "Get started", + text: "home.empty.create_project.cta", onClick: (e: React.MouseEvent) => { if (!canCreateProject) return; e.preventDefault(); @@ -44,39 +45,48 @@ export const NoProjectsEmptyState = () => { }, { id: "invite-team", - title: "Invite your team.", - description: "Build, ship, and manage with coworkers.", - icon: , + title: "home.empty.invite_team.title", + description: "home.empty.invite_team.description", + icon: , cta: { - text: "Get them in", + text: "home.empty.invite_team.cta", link: `/${workspaceSlug}/settings/members`, }, }, { id: "configure-workspace", - title: "Set up your workspace.", - description: "Turn features on or off or go beyond that.", - icon: , + title: "home.empty.configure_workspace.title", + description: "home.empty.configure_workspace.description", + icon: , cta: { - text: "Configure this workspace", + text: "home.empty.configure_workspace.cta", link: "settings", }, }, { id: "personalize-account", - title: "Make Plane yours.", - description: "Choose your picture, colors, and more.", - icon: ( - - ), + title: "home.empty.personalize_account.title", + description: "home.empty.personalize_account.description", + icon: + currentUser?.avatar_url && currentUser?.avatar_url.trim() !== "" ? ( + + + {currentUser?.display_name + + + ) : ( + + + {(currentUser?.email ?? currentUser?.display_name ?? "?")[0]} + + + ), cta: { - text: "Personalize now", + text: "home.empty.personalize_account.cta", link: "/profile", }, }, @@ -92,14 +102,15 @@ export const NoProjectsEmptyState = () => {
{item.icon}
-

{item.title}

-

{item.description}

+

{t(item.title)}

+

{t(item.description)}

+ {item.cta.link ? ( - {item.cta.text} + {t(item.cta.text)} ) : ( )}
diff --git a/web/core/components/home/widgets/empty-states/recents.tsx b/web/core/components/home/widgets/empty-states/recents.tsx index 1398cb467af..5d48d29565f 100644 --- a/web/core/components/home/widgets/empty-states/recents.tsx +++ b/web/core/components/home/widgets/empty-states/recents.tsx @@ -1,41 +1,41 @@ import { Briefcase, FileText, History } from "lucide-react"; -// plane ui +import { useTranslation } from "@plane/i18n"; import { LayersIcon } from "@plane/ui"; const getDisplayContent = (type: string) => { switch (type) { case "project": return { - icon: Briefcase, - text: "Projects you go into or have assigned work in will show up here.", + icon: , + text: "home.recents.empty.project", }; case "page": return { - icon: FileText, - text: "Create, see, or change something on pages you have access to and see them here.", + icon: , + text: "home.recents.empty.page", }; case "issue": return { - icon: LayersIcon, - text: "Let's see some issues to see them show up here.", + icon: , + text: "home.recents.empty.issue", }; default: return { - icon: History, - text: "Whatever you see and act on in Plane will show up here.", + icon: , + text: "home.recents.empty.default", }; } }; - export const RecentsEmptyState = ({ type }: { type: string }) => { - const displayContent = getDisplayContent(type); + const { t } = useTranslation(); + + const { icon, text } = getDisplayContent(type); return ( -
-
- +
+
+ {icon}
{t(text)}
-

{displayContent.text}

); }; diff --git a/web/core/components/home/widgets/empty-states/stickies.tsx b/web/core/components/home/widgets/empty-states/stickies.tsx index 88986ae0715..1210ea096ce 100644 --- a/web/core/components/home/widgets/empty-states/stickies.tsx +++ b/web/core/components/home/widgets/empty-states/stickies.tsx @@ -1,13 +1,15 @@ // plane ui +import { useTranslation } from "@plane/i18n"; import { RecentStickyIcon } from "@plane/ui"; -export const StickiesEmptyState = () => ( -
-
- +export const StickiesEmptyState = () => { + const { t } = useTranslation(); + return ( +
+
+ +
{t("stickies.empty_state.simple")}
+
-

- Jot down an idea, capture an aha, or record a brainwave. Add a sticky to get started. -

-
-); + ); +}; diff --git a/web/core/components/home/widgets/links/action.tsx b/web/core/components/home/widgets/links/action.tsx index 361eeaf9234..5199b7d4dc3 100644 --- a/web/core/components/home/widgets/links/action.tsx +++ b/web/core/components/home/widgets/links/action.tsx @@ -1,10 +1,12 @@ import { PlusIcon } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; type TProps = { onClick: () => void; }; export const AddLink = (props: TProps) => { const { onClick } = props; + const { t } = useTranslation(); return ( ); }; diff --git a/web/core/components/home/widgets/links/create-update-link-modal.tsx b/web/core/components/home/widgets/links/create-update-link-modal.tsx index 9e45415b33b..fb27c457a13 100644 --- a/web/core/components/home/widgets/links/create-update-link-modal.tsx +++ b/web/core/components/home/widgets/links/create-update-link-modal.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; // plane types // plane ui +import { useTranslation } from "@plane/i18n"; import { TLink, TLinkEditableFields } from "@plane/types"; import { Button, Input, ModalCore } from "@plane/ui"; import { TLinkOperations } from "./use-links"; @@ -40,6 +41,7 @@ export const LinkCreateUpdateModal: FC = observer((props) } = useForm({ defaultValues, }); + const { t } = useTranslation(); const onClose = () => { if (handleOnClose) handleOnClose(); @@ -66,17 +68,20 @@ export const LinkCreateUpdateModal: FC = observer((props)
-

{preloadedData?.id ? "Update" : "Add"} quicklink

+

+ {preloadedData?.id ? t("Update") : t("Add")} {t("home.quick_links.title")} +

( = observer((props) onChange={onChange} ref={ref} hasError={Boolean(errors.url)} - placeholder="Type or paste a URL" + placeholder={t("link.modal.url.placeholder")} className="w-full" /> )} /> - {errors.url && URL is invalid} + {errors.url && {t("link.modal.url.required")}}
= observer((props) onChange={onChange} ref={ref} hasError={Boolean(errors.title)} - placeholder="What you'd like to see this link as" + placeholder={t("link.modal.title.placeholder")} className="w-full" /> )} @@ -119,10 +124,11 @@ export const LinkCreateUpdateModal: FC = observer((props)
diff --git a/web/core/components/home/widgets/links/link-detail.tsx b/web/core/components/home/widgets/links/link-detail.tsx index 17c96717305..e632aaa2f77 100644 --- a/web/core/components/home/widgets/links/link-detail.tsx +++ b/web/core/components/home/widgets/links/link-detail.tsx @@ -2,8 +2,8 @@ import { FC } from "react"; import { observer } from "mobx-react"; -import { Pencil, Trash2, ExternalLink, Link2, Link } from "lucide-react"; -// plane ui +import { Pencil, Trash2, ExternalLink, EllipsisVertical, Link2, Link } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { TOAST_TYPE, setToast, CustomMenu, TContextMenuItem } from "@plane/ui"; // plane utils import { cn, copyTextToClipboard } from "@plane/utils"; @@ -26,6 +26,7 @@ export const ProjectLinkDetail: FC = observer((props) => { const { quickLinks: { getLinkById, toggleLinkModal, setLinkData }, } = useHome(); + const { t } = useTranslation(); const linkDetail = getLinkById(linkId); if (!linkDetail) return <>; @@ -41,8 +42,8 @@ export const ProjectLinkDetail: FC = observer((props) => { copyTextToClipboard(viewLink).then(() => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Link Copied!", - message: "View link copied to clipboard.", + title: t("link_copied"), + message: t("view_link_copied_to_clipboard"), }); }); const handleOpenInNewTab = () => window.open(`${viewLink}`, "_blank"); @@ -51,25 +52,25 @@ export const ProjectLinkDetail: FC = observer((props) => { { key: "edit", action: () => handleEdit(true), - title: "Edit", + title: t("edit"), icon: Pencil, }, { key: "open-new-tab", action: handleOpenInNewTab, - title: "Open in new tab", + title: t("open_in_new_tab"), icon: ExternalLink, }, { key: "copy-link", action: handleCopyText, - title: "Copy link", + title: t("copy_link"), icon: Link, }, { key: "delete", action: () => linkOperations.remove(linkId), - title: "Delete", + title: t("delete"), icon: Trash2, }, ]; diff --git a/web/core/components/home/widgets/links/root.tsx b/web/core/components/home/widgets/links/root.tsx index f25f035d165..0cd9d30e6e4 100644 --- a/web/core/components/home/widgets/links/root.tsx +++ b/web/core/components/home/widgets/links/root.tsx @@ -1,6 +1,7 @@ import { observer } from "mobx-react"; import useSWR from "swr"; import { Plus } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { THomeWidgetProps } from "@plane/types"; import { useHome } from "@/hooks/store/use-home"; import { LinkCreateUpdateModal } from "./create-update-link-modal"; @@ -13,6 +14,7 @@ export const DashboardQuickLinks = observer((props: THomeWidgetProps) => { const { quickLinks: { isLinkModalOpen, toggleLinkModal, linkData, setLinkData, fetchLinks }, } = useHome(); + const { t } = useTranslation(); useSWR( workspaceSlug ? `HOME_LINKS_${workspaceSlug}` : null, @@ -34,14 +36,14 @@ export const DashboardQuickLinks = observer((props: THomeWidgetProps) => { />
-
Quicklinks
+
{t("home.quick_links.title_plural")}
diff --git a/web/core/components/home/widgets/links/use-links.tsx b/web/core/components/home/widgets/links/use-links.tsx index a6668d92efc..3607f5b6abf 100644 --- a/web/core/components/home/widgets/links/use-links.tsx +++ b/web/core/components/home/widgets/links/use-links.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react"; +import { useTranslation } from "@plane/i18n"; import { TProjectLink } from "@plane/types"; import { setToast, TOAST_TYPE } from "@plane/ui"; import { useHome } from "@/hooks/store/use-home"; @@ -26,6 +27,7 @@ export const useLinks = (workspaceSlug: string) => { fetchLinks, }, } = useHome(); + const { t } = useTranslation(); const linkOperations: TLinkOperations = useMemo( () => ({ @@ -34,17 +36,17 @@ export const useLinks = (workspaceSlug: string) => { if (!workspaceSlug) throw new Error("Missing required fields"); await createLink(workspaceSlug, data); setToast({ - message: "The link has been successfully created", + message: t("home.quick_links.toasts.created.message"), type: TOAST_TYPE.SUCCESS, - title: "Link created", + title: t("home.quick_links.toasts.created.title"), }); toggleLinkModal(false); } catch (error: any) { console.error("error", error); setToast({ - message: error?.data?.error ?? "The link could not be created", + message: error?.data?.error ?? t("home.quick_links.toasts.not_created.message"), type: TOAST_TYPE.ERROR, - title: "Link not created", + title: t("home.quick_links.toasts.not_created.title"), }); throw error; } @@ -54,16 +56,16 @@ export const useLinks = (workspaceSlug: string) => { if (!workspaceSlug) throw new Error("Missing required fields"); await updateLink(workspaceSlug, linkId, data); setToast({ - message: "The link has been successfully updated", + message: t("home.quick_links.toasts.updated.message"), type: TOAST_TYPE.SUCCESS, - title: "Link updated", + title: t("home.quick_links.toasts.updated.title"), }); toggleLinkModal(false); - } catch (error) { + } catch (error: any) { setToast({ - message: "The link could not be updated", + message: error?.data?.error ?? t("home.quick_links.toasts.not_updated.message"), type: TOAST_TYPE.ERROR, - title: "Link not updated", + title: t("home.quick_links.toasts.not_updated.title"), }); throw error; } @@ -73,15 +75,15 @@ export const useLinks = (workspaceSlug: string) => { if (!workspaceSlug) throw new Error("Missing required fields"); await removeLink(workspaceSlug, linkId); setToast({ - message: "The link has been successfully removed", + message: t("home.quick_links.toasts.removed.message"), type: TOAST_TYPE.SUCCESS, - title: "Link removed", + title: t("home.quick_links.toasts.removed.message"), }); - } catch (error) { + } catch (error: any) { setToast({ - message: "The link could not be removed", + message: error?.data?.error ?? t("home.quick_links.toasts.not_removed.message"), type: TOAST_TYPE.ERROR, - title: "Link not removed", + title: t("home.quick_links.toasts.not_removed.title"), }); } }, diff --git a/web/core/components/home/widgets/manage/index.tsx b/web/core/components/home/widgets/manage/index.tsx index 9a2239e2ee9..8834ca69dcb 100644 --- a/web/core/components/home/widgets/manage/index.tsx +++ b/web/core/components/home/widgets/manage/index.tsx @@ -4,6 +4,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; // plane types // plane ui +import { useTranslation } from "@plane/i18n"; import { EModalWidth, ModalCore } from "@plane/ui"; import { WidgetList } from "./widget-list"; @@ -16,11 +17,12 @@ export type TProps = { export const ManageWidgetsModal: FC = observer((props) => { // props const { workspaceSlug, isModalOpen, handleOnClose } = props; + const { t } = useTranslation(); return (
-
Manage widgets
+
{t("home.manage_widgets")}
diff --git a/web/core/components/home/widgets/manage/widget-item.tsx b/web/core/components/home/widgets/manage/widget-item.tsx index bfd9ca38812..42cb7f5c0d4 100644 --- a/web/core/components/home/widgets/manage/widget-item.tsx +++ b/web/core/components/home/widgets/manage/widget-item.tsx @@ -15,6 +15,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { createRoot } from "react-dom/client"; // plane types +import { useTranslation } from "@plane/i18n"; import { InstructionType, TWidgetEntityData } from "@plane/types"; // plane ui import { DropIndicator, ToggleSwitch } from "@plane/ui"; @@ -44,6 +45,7 @@ export const WidgetItem: FC = observer((props) => { const elementRef = useRef(null); // hooks const { widgetsMap } = useHome(); + const { t } = useTranslation(); // derived values const widget = widgetsMap[widgetId] as TWidgetEntityData; const widgetTitle = HOME_WIDGETS_LIST[widget.key]?.title; @@ -128,7 +130,7 @@ export const WidgetItem: FC = observer((props) => { >
-
{widgetTitle}
+
{t(widgetTitle, { count: 1 })}
{ const { orderedWidgets, reorderWidget, toggleWidget } = useHome(); + const { t } = useTranslation(); const handleDrop = (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => { const dropTargets = location?.current?.dropTargets ?? []; @@ -31,14 +33,14 @@ export const WidgetList = observer(({ workspaceSlug }: { workspaceSlug: string } reorderWidget(workspaceSlug, sourceData.id, droppedId, instruction); /** sequence */ setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success!", - message: "Widget reordered successfully.", + title: t("toast.success"), + message: t("home.widget.reordered_successfully"), }); } catch { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Error occurred while reordering widget.", + title: t("toast.error"), + message: t("home.widget.reordering_failed"), }); } } diff --git a/web/core/components/home/widgets/recents/filters.tsx b/web/core/components/home/widgets/recents/filters.tsx index da7e9e39ab3..255249dd14e 100644 --- a/web/core/components/home/widgets/recents/filters.tsx +++ b/web/core/components/home/widgets/recents/filters.tsx @@ -3,6 +3,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; import { ChevronDown } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { TRecentActivityFilterKeys } from "@plane/types"; import { CustomMenu } from "@plane/ui"; import { cn } from "@plane/utils"; @@ -11,11 +12,12 @@ export type TFiltersDropdown = { className?: string; activeFilter: TRecentActivityFilterKeys; setActiveFilter: (filter: TRecentActivityFilterKeys) => void; - filters: { name: TRecentActivityFilterKeys; icon?: React.ReactNode }[]; + filters: { name: TRecentActivityFilterKeys; icon?: React.ReactNode; i18n_key: string }[]; }; export const FiltersDropdown: FC = observer((props) => { const { className, activeFilter, setActiveFilter, filters } = props; + const { t } = useTranslation(); const DropdownOptions = () => filters?.map((filter) => ( @@ -27,10 +29,11 @@ export const FiltersDropdown: FC = observer((props) => { }} > {filter.icon &&
{filter.icon}
} -
{`${filter.name}s`}
+
{t(filter.i18n_key)}
)); + const title = activeFilter ? filters?.find((filter) => filter.name === activeFilter)?.i18n_key : ""; return ( = observer((props) => { placement="bottom-start" customButton={ } diff --git a/web/core/components/home/widgets/recents/index.tsx b/web/core/components/home/widgets/recents/index.tsx index 8530563de34..e23c11c54e2 100644 --- a/web/core/components/home/widgets/recents/index.tsx +++ b/web/core/components/home/widgets/recents/index.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; // types import useSWR from "swr"; import { Briefcase, FileText } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { TActivityEntityData, THomeWidgetProps, TRecentActivityFilterKeys } from "@plane/types"; // components import { LayersIcon } from "@plane/ui"; @@ -20,11 +21,11 @@ import { RecentProject } from "./project"; const WIDGET_KEY = EWidgetKeys.RECENT_ACTIVITY; const workspaceService = new WorkspaceService(); -const filters: { name: TRecentActivityFilterKeys; icon?: React.ReactNode }[] = [ - { name: "all item" }, - { name: "issue", icon: }, - { name: "page", icon: }, - { name: "project", icon: }, +const filters: { name: TRecentActivityFilterKeys; icon?: React.ReactNode; i18n_key: string }[] = [ + { name: "all item", i18n_key: "home.recents.filters.all" }, + { name: "issue", icon: , i18n_key: "home.recents.filters.issues" }, + { name: "page", icon: , i18n_key: "home.recents.filters.pages" }, + { name: "project", icon: , i18n_key: "home.recents.filters.projects" }, ]; type TRecentWidgetProps = THomeWidgetProps & { @@ -40,6 +41,7 @@ export const RecentActivityWidget: React.FC = observer((prop const ref = useRef(null); // store hooks const { joinedProjectIds, loader } = useProject(); + const { t } = useTranslation(); const { data: recents, isLoading } = useSWR( workspaceSlug ? `WORKSPACE_RECENT_ACTIVITY_${workspaceSlug}_${filter}` : null, @@ -77,7 +79,7 @@ export const RecentActivityWidget: React.FC = observer((prop return (
-
Recents
+
{t("home.recents.title")}
{showFilterSelect && }
@@ -94,7 +96,7 @@ export const RecentActivityWidget: React.FC = observer((prop buttonClassName="bg-custom-background-90/20" >
-
Recents
+
{t("home.recents.title")}
{showFilterSelect && }
diff --git a/web/core/components/stickies/delete-modal.tsx b/web/core/components/stickies/delete-modal.tsx index c810f426841..ecbceae93f3 100644 --- a/web/core/components/stickies/delete-modal.tsx +++ b/web/core/components/stickies/delete-modal.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { observer } from "mobx-react"; // ui +import { useTranslation } from "@plane/i18n"; import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui"; interface IStickyDelete { @@ -15,6 +16,8 @@ export const StickyDeleteModal: React.FC = observer((props) => { const { isOpen, handleClose, handleSubmit } = props; // states const [loader, setLoader] = useState(false); + // hooks + const { t } = useTranslation(); const formSubmit = async () => { try { @@ -23,8 +26,8 @@ export const StickyDeleteModal: React.FC = observer((props) => { } catch { setToast({ type: TOAST_TYPE.ERROR, - title: "Warning!", - message: "Something went wrong. Please try again later.", + title: t("stickies.toasts.not_removed.title"), + message: t("stickies.toasts.not_removed.message"), }); } finally { setLoader(false); @@ -37,8 +40,8 @@ export const StickyDeleteModal: React.FC = observer((props) => { handleSubmit={formSubmit} isSubmitting={loader} isOpen={isOpen} - title="Delete sticky" - content="Are you sure you want to delete the sticky?" + title={t("stickies.delete")} + content={t("stickies.delete_confirmation")} /> ); }); diff --git a/web/core/components/stickies/layout/stickies-truncated.tsx b/web/core/components/stickies/layout/stickies-truncated.tsx index c7af1191b8f..c163d7d3223 100644 --- a/web/core/components/stickies/layout/stickies-truncated.tsx +++ b/web/core/components/stickies/layout/stickies-truncated.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import { useParams } from "next/navigation"; import useSWR from "swr"; // plane utils +import { useTranslation } from "@plane/i18n"; import { cn } from "@plane/utils"; // hooks import { useSticky } from "@/hooks/use-stickies"; @@ -20,6 +21,7 @@ export const StickiesTruncated = observer((props: StickiesTruncatedProps) => { const { workspaceSlug } = useParams(); // store hooks const { fetchWorkspaceStickies } = useSticky(); + const { t } = useTranslation(); useSWR( workspaceSlug ? `WORKSPACE_STICKIES_${workspaceSlug}` : null, @@ -40,7 +42,7 @@ export const StickiesTruncated = observer((props: StickiesTruncatedProps) => { )} onClick={handleClose} > - Show all + {t("show_all")} } > diff --git a/web/core/components/stickies/modal/search.tsx b/web/core/components/stickies/modal/search.tsx index 15e21c81126..22499c03a8b 100644 --- a/web/core/components/stickies/modal/search.tsx +++ b/web/core/components/stickies/modal/search.tsx @@ -8,6 +8,7 @@ import { Search, X } from "lucide-react"; // plane hooks import { useOutsideClickDetector } from "@plane/hooks"; // helpers +import { useTranslation } from "@plane/i18n"; import { cn } from "@/helpers/common.helper"; import { useSticky } from "@/hooks/use-stickies"; @@ -16,6 +17,7 @@ export const StickySearch: FC = observer(() => { const { workspaceSlug } = useParams(); // hooks const { searchQuery, updateSearchQuery, fetchWorkspaceStickies } = useSticky(); + const { t } = useTranslation(); // refs const inputRef = useRef(null); // states @@ -71,7 +73,7 @@ export const StickySearch: FC = observer(() => { { updateSearchQuery(e.target.value); diff --git a/web/core/components/stickies/sticky/use-operations.tsx b/web/core/components/stickies/sticky/use-operations.tsx index 73f21231857..b582fd5638c 100644 --- a/web/core/components/stickies/sticky/use-operations.tsx +++ b/web/core/components/stickies/sticky/use-operations.tsx @@ -1,5 +1,6 @@ import { useMemo } from "react"; // plane types +import { useTranslation } from "@plane/i18n"; import { InstructionType, TSticky } from "@plane/types"; // plane ui import { setToast, TOAST_TYPE } from "@plane/ui"; @@ -36,13 +37,14 @@ export const useStickyOperations = (props: TProps) => { // store hooks const { stickies, getWorkspaceStickyIds, createSticky, updateSticky, deleteSticky, updateStickyPosition } = useSticky(); + const { t } = useTranslation(); const isValid = (data: Partial) => { if (data.name && data.name.length > 100) { setToast({ type: TOAST_TYPE.ERROR, - title: "Sticky not updated", - message: "The sticky name cannot be longer than 100 characters.", + title: t("stickies.toasts.not_updated.title"), + message: t("stickies.toasts.errors.wrong_name"), }); return false; } @@ -63,9 +65,9 @@ export const useStickyOperations = (props: TProps) => { const latestSticky = stickies[workspaceStickIds[0]]; if (latestSticky && (!latestSticky.description_html || isCommentEmpty(latestSticky.description_html))) { setToast({ - message: "There already exists a sticky with no description", + message: t("stickies.toasts.errors.already_exists"), type: TOAST_TYPE.WARNING, - title: "Sticky already created", + title: t("stickies.toasts.not_created.title"), }); return; } @@ -75,15 +77,15 @@ export const useStickyOperations = (props: TProps) => { await createSticky(workspaceSlug, payload); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Sticky created", - message: "The sticky has been successfully created.", + title: t("stickies.toasts.created.title"), + message: t("stickies.toasts.created.message"), }); } catch (error: any) { console.error("Error in creating sticky:", error); setToast({ type: TOAST_TYPE.ERROR, - title: "Sticky not created", - message: error?.data?.error ?? "The sticky could not be created.", + title: t("stickies.toasts.not_created.title"), + message: error?.data?.error ?? t("stickies.toasts.not_created.message"), }); } }, @@ -96,8 +98,8 @@ export const useStickyOperations = (props: TProps) => { console.error("Error in updating sticky:", error); setToast({ type: TOAST_TYPE.ERROR, - title: "Sticky not updated", - message: "The sticky could not be updated.", + title: t("stickies.toasts.not_updated.title"), + message: t("stickies.toasts.not_updated.message"), }); } }, @@ -107,15 +109,15 @@ export const useStickyOperations = (props: TProps) => { await deleteSticky(workspaceSlug, stickyId); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Sticky removed", - message: "The sticky has been removed successfully.", + title: t("stickies.toasts.removed.title"), + message: t("stickies.toasts.removed.message"), }); } catch (error) { console.error("Error in removing sticky:", error); setToast({ type: TOAST_TYPE.ERROR, - title: "Sticky not removed", - message: "The sticky could not be removed.", + title: t("stickies.toasts.not_removed.title"), + message: t("stickies.toasts.not_removed.message"), }); } }, @@ -132,8 +134,8 @@ export const useStickyOperations = (props: TProps) => { console.error("Error in updating sticky position:", error); setToast({ type: TOAST_TYPE.ERROR, - title: "Sticky not updated", - message: "The sticky could not be updated.", + title: t("stickies.toasts.not_updated.title"), + message: t("stickies.toasts.not_updated.message"), }); } }, diff --git a/web/core/components/stickies/widget.tsx b/web/core/components/stickies/widget.tsx index cafec4013ab..e94741cc096 100644 --- a/web/core/components/stickies/widget.tsx +++ b/web/core/components/stickies/widget.tsx @@ -2,6 +2,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { Plus } from "lucide-react"; // hooks +import { useTranslation } from "@plane/i18n"; import { useSticky } from "@/hooks/use-stickies"; import { StickiesTruncated } from "./layout"; import { StickySearch } from "./modal/search"; @@ -12,6 +13,7 @@ export const StickiesWidget: React.FC = observer(() => { const { workspaceSlug } = useParams(); // store hooks const { creatingSticky, toggleShowNewSticky } = useSticky(); + const { t } = useTranslation(); // sticky operations const { stickyOperations } = useStickyOperations({ workspaceSlug: workspaceSlug?.toString() ?? "", @@ -20,7 +22,7 @@ export const StickiesWidget: React.FC = observer(() => { return (
-
Your stickies
+
{t("stickies.title")}
{/* actions */}
@@ -33,7 +35,7 @@ export const StickiesWidget: React.FC = observer(() => { disabled={creatingSticky} > - Add sticky + {t("stickies.add")} {creatingSticky && (
{ const SIDEBAR_USER_MENU_ITEMS = [ { key: "home", - labelTranslationKey: "home", + labelTranslationKey: "home.title", href: `/${workspaceSlug.toString()}/`, access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], Icon: Home,