diff --git a/.github/crowdin-docs.yml b/.github/crowdin-docs.yml index df1570f88a..6ce8b3203b 100644 --- a/.github/crowdin-docs.yml +++ b/.github/crowdin-docs.yml @@ -9,6 +9,10 @@ "base_path": ".." files: [ + { + "source": "packages/twenty-docs/getting-started/**/*.mdx", + "translation": "packages/twenty-docs/l/%two_letters_code%/getting-started/**/%original_file_name%", + }, { # # MDX documentation files - user-guide diff --git a/packages/twenty-docs/docs.json b/packages/twenty-docs/docs.json index e512d59155..22f79f2849 100644 --- a/packages/twenty-docs/docs.json +++ b/packages/twenty-docs/docs.json @@ -468,32 +468,6 @@ { "language": "fr", "tabs": [ - { - "tab": "Prise en main", - "groups": [ - { - "group": "Bienvenue", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Concepts clés", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Guide de l'utilisateur", "groups": [ @@ -901,32 +875,6 @@ { "language": "ar", "tabs": [ - { - "tab": "البدء", - "groups": [ - { - "group": "مرحبًا", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "المفاهيم الأساسية", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "دليل المستخدم", "groups": [ @@ -1334,32 +1282,6 @@ { "language": "cs", "tabs": [ - { - "tab": "Začínáme", - "groups": [ - { - "group": "Vítejte", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Základní pojmy", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Uživatelská příručka", "groups": [ @@ -1767,32 +1689,6 @@ { "language": "de", "tabs": [ - { - "tab": "Erste Schritte", - "groups": [ - { - "group": "Willkommen", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Kernkonzepte", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Benutzerhandbuch", "groups": [ @@ -2200,32 +2096,6 @@ { "language": "es", "tabs": [ - { - "tab": "Primeros pasos", - "groups": [ - { - "group": "Bienvenido", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Conceptos clave", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Guía de usuario", "groups": [ @@ -2633,32 +2503,6 @@ { "language": "it", "tabs": [ - { - "tab": "Per iniziare", - "groups": [ - { - "group": "Benvenuto", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Concetti chiave", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Guida utente", "groups": [ @@ -3066,32 +2910,6 @@ { "language": "ja", "tabs": [ - { - "tab": "Getting Started", - "groups": [ - { - "group": "Welcome", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Core Concepts", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "ユーザーガイド", "groups": [ @@ -3215,8 +3033,7 @@ "l/ja/user-guide/workflows/how-tos/crm-automations/formula-fields", "l/ja/user-guide/workflows/how-tos/crm-automations/display-related-record-data", "l/ja/user-guide/workflows/how-tos/crm-automations/closed-won-automations", - "l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities", - "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" + "l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities" ] }, { @@ -3272,11 +3089,9 @@ "group": "Layout", "icon": "table-columns", "pages": [ - "user-guide/layout/overview", { "group": "Reference", "pages": [ - "user-guide/layout/capabilities/navigation", { "group": "Views", "pages": [ @@ -3287,8 +3102,7 @@ "l/ja/user-guide/views-pipelines/capabilities/fields-and-columns", "l/ja/user-guide/views-pipelines/capabilities/view-settings" ] - }, - "user-guide/layout/capabilities/record-pages" + } ] }, { @@ -3402,79 +3216,6 @@ "l/ja/developers/introduction" ] }, - { - "group": "Apps", - "pages": [ - { - "group": "Getting Started", - "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" - ] - }, - { - "group": "Config", - "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" - ] - }, - { - "group": "Data", - "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" - ] - }, - { - "group": "Logic", - "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" - ] - }, - { - "group": "Layout", - "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" - ] - }, - { - "group": "Operations", - "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" - ] - } - ] - }, - { - "group": "API", - "pages": [ - "developers/extend/api", - "developers/extend/webhooks", - "developers/extend/oauth" - ] - }, { "group": "セルフホスト", "pages": [ @@ -3487,9 +3228,7 @@ { "group": "貢献", "pages": [ - "l/ja/developers/contribute/capabilities/local-setup", - "developers/contribute/commands", - "developers/contribute/style-guide" + "l/ja/developers/contribute/capabilities/local-setup" ] } ] @@ -3499,32 +3238,6 @@ { "language": "ko", "tabs": [ - { - "tab": "시작하기", - "groups": [ - { - "group": "환영합니다", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "핵심 개념", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "사용자 안내서", "groups": [ @@ -3932,32 +3645,6 @@ { "language": "pt", "tabs": [ - { - "tab": "Primeiros passos", - "groups": [ - { - "group": "Boas-vindas", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Conceitos essenciais", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "User Guide", "groups": [ @@ -4365,32 +4052,6 @@ { "language": "ro", "tabs": [ - { - "tab": "Începeți", - "groups": [ - { - "group": "Bun venit", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Concepte cheie", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "User Guide", "groups": [ @@ -4798,32 +4459,6 @@ { "language": "ru", "tabs": [ - { - "tab": "Начало работы", - "groups": [ - { - "group": "Добро пожаловать", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Основные понятия", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Руководство пользователя", "groups": [ @@ -5231,32 +4866,6 @@ { "language": "tr", "tabs": [ - { - "tab": "Başlarken", - "groups": [ - { - "group": "Hoş geldiniz", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "Temel Kavramlar", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "Kullanıcı Rehberi", "groups": [ @@ -5664,32 +5273,6 @@ { "language": "zh", "tabs": [ - { - "tab": "开始使用", - "groups": [ - { - "group": "欢迎", - "pages": [ - "getting-started/introduction", - "getting-started/key-features", - "getting-started/quickstart" - ] - }, - { - "group": "核心概念", - "pages": [ - "getting-started/core-concepts/data-model", - "getting-started/core-concepts/layout", - "getting-started/core-concepts/workflows", - "getting-started/core-concepts/calendar-and-email", - "getting-started/core-concepts/ai", - "getting-started/core-concepts/apps", - "getting-started/core-concepts/dashboards", - "getting-started/core-concepts/glossary" - ] - } - ] - }, { "tab": "用户指南", "groups": [ diff --git a/packages/twenty-docs/scripts/fix-translated-links.sh b/packages/twenty-docs/scripts/fix-translated-links.sh index 8b0cda33d8..87bd2927a1 100755 --- a/packages/twenty-docs/scripts/fix-translated-links.sh +++ b/packages/twenty-docs/scripts/fix-translated-links.sh @@ -26,6 +26,8 @@ for lang_dir in "$DOCS_DIR"/*/ ; do echo "📝 Processing $lang_code documentation..." + find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ + "s|href=\"/getting-started/|href=\"/l/$lang_code/getting-started/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|href=\"/user-guide/|href=\"/l/$lang_code/user-guide/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ @@ -33,6 +35,8 @@ for lang_dir in "$DOCS_DIR"/*/ ; do find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|href=\"/twenty-ui/|href=\"/l/$lang_code/twenty-ui/|g" {} \; + find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ + "s|](/getting-started/|](/l/$lang_code/getting-started/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|](/user-guide/|](/l/$lang_code/user-guide/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ @@ -40,6 +44,8 @@ for lang_dir in "$DOCS_DIR"/*/ ; do find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|](/twenty-ui/|](/l/$lang_code/twenty-ui/|g" {} \; + find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ + "s|https://docs\.twenty\.com/getting-started/|https://docs.twenty.com/l/$lang_code/getting-started/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|https://docs\.twenty\.com/user-guide/|https://docs.twenty.com/l/$lang_code/user-guide/|g" {} \; find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ @@ -47,13 +53,6 @@ for lang_dir in "$DOCS_DIR"/*/ ; do find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ "s|https://docs\.twenty\.com/twenty-ui/|https://docs.twenty.com/l/$lang_code/twenty-ui/|g" {} \; - find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ - "s|https://docs\.twenty\.com/user-guide/|https://docs.twenty.com/$lang_code/user-guide/|g" {} \; - find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ - "s|https://docs\.twenty\.com/developers/|https://docs.twenty.com/$lang_code/developers/|g" {} \; - find "$lang_dir" -name "*.mdx" -type f -exec sed -i.bak \ - "s|https://docs\.twenty\.com/twenty-ui/|https://docs.twenty.com/$lang_code/twenty-ui/|g" {} \; - find "$lang_dir" -name "*.bak" -type f -delete echo "✅ $lang_code documentation links fixed" diff --git a/packages/twenty-docs/scripts/generate-docs-json.ts b/packages/twenty-docs/scripts/generate-docs-json.ts index 60be8acda2..a239538cbc 100644 --- a/packages/twenty-docs/scripts/generate-docs-json.ts +++ b/packages/twenty-docs/scripts/generate-docs-json.ts @@ -113,37 +113,54 @@ const buildLanguageEntry = (language: string): GeneratedLanguage => { return { language, - tabs: baseStructure.tabs.map((tab) => ({ - tab: translationMaps.tabLabels.get(tab.key) ?? tab.label, - groups: tab.groups.map((group) => - buildGroup(group, translationMaps, language), - ), - })), + tabs: baseStructure.tabs + .map((tab) => ({ + tab: translationMaps.tabLabels.get(tab.key) ?? tab.label, + groups: tab.groups + .map((group) => buildGroup(group, translationMaps, language)) + .filter((group): group is GeneratedGroup => group !== null), + })) + .filter((tab) => tab.groups.length > 0), }; }; +// Mintlify requires each page path to appear in only one language's navigation. +// Duplicating a path across languages breaks the language switcher (it can no +// longer resolve the equivalent page and falls back to the first page). So a +// page is only included in a non-default language when its translation exists, +// and empty groups/tabs are dropped entirely. const buildGroup = ( group: BaseGroup, translations: TranslationMaps, language: string, -): GeneratedGroup => ({ - group: translations.groupLabels.get(group.key) ?? group.label, - ...(group.icon ? { icon: group.icon } : {}), - pages: group.pages.map((page) => - typeof page === 'string' - ? formatPageSlug(page, language) - : buildGroup(page, translations, language), - ), -}); +): GeneratedGroup | null => { + const pages = group.pages + .map((page) => + typeof page === 'string' + ? formatPageSlug(page, language) + : buildGroup(page, translations, language), + ) + .filter((page): page is string | GeneratedGroup => page !== null); -const formatPageSlug = (slug: string, language: string): string => { + if (pages.length === 0) { + return null; + } + + return { + group: translations.groupLabels.get(group.key) ?? group.label, + ...(group.icon ? { icon: group.icon } : {}), + pages, + }; +}; + +const formatPageSlug = (slug: string, language: string): string | null => { if (language === DEFAULT_LANGUAGE) { return slug; } const localizedPagePath = path.join(localesRoot, language, `${slug}.mdx`); - return fs.existsSync(localizedPagePath) ? `l/${language}/${slug}` : slug; + return fs.existsSync(localizedPagePath) ? `l/${language}/${slug}` : null; }; const hasLocaleContent = (language: string): boolean => {