diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 9eda364..e3f1070 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -1330,6 +1330,335 @@ const productManagerSidebar = [ } ] +const localizeSidebarLinks = (sidebar, locale) => + sidebar.map((group) => ({ + ...group, + items: group.items.map((item) => ({ + ...item, + link: item.link.replace(/^\/(?:zh-cn|en|ko-kr)\//, `/${locale}/`) + })) + })) + +const stage1SidebarLabels = { + 'ja-jp': [ + { + text: 'はじめに', + items: ['学習ロードマップ', 'AI時代、話せればプログラミングできる'] + }, + { + text: 'プロダクトプロトタイプ実践', + items: [ + 'AIプログラミングツールを学ぶ', + '良いアイデアを見つける', + 'プロトタイプを作る', + 'AI能力を統合する', + '完全プロジェクト実践' + ] + }, + { + text: '付録:ビジネス思考', + items: [ + 'プロダクト思考とソリューション設計', + 'AI業界応用シナリオ参考(B向け)', + 'AI消費者シナリオの着想参考(C向け)' + ] + }, + { + text: '付録:ユーザー調査と需要検証', + items: [ + 'アイデアの見つけ方:初心者向けの3つの参考ソース', + 'Double Diamond:正しいことを先に、次に正しく行う', + 'Jobs to Be Doneでユーザーの本当の目的を見つける', + 'The Mom Test:需要を検証するユーザーインタビュー法' + ] + }, + { + text: '付録:技術的な解決策', + items: [ + 'コードでエラーが出たらどうするか', + '7つのAIプログラミングツール比較', + 'デザインAgentとコーディングAgentでWebサイトを作る' + ] + } + ], + 'zh-tw': [ + { + text: '新手入門', + items: ['學習地圖', 'AI 時代,會說話就會程式設計'] + }, + { + text: '產品原型實戰', + items: [ + '學會 AI 程式設計工具', + '找到好點子', + '搭建產品原型', + '接入 AI 能力', + '完整專案實戰' + ] + }, + { + text: '附錄:業務思維', + items: [ + '產品思維與方案設計', + 'AI 產業應用場景參考(B 端)', + 'AI 消費場景靈感參考(C 端)' + ] + }, + { + text: '附錄:使用者研究與需求驗證', + items: [ + '從哪裡找點子:3 種最適合新手的參考來源', + '雙鑽模型:先做對的事,再把事做對', + '用 Jobs to Be Done 找到使用者真正想完成的事', + 'The Mom Test 使用者訪談法' + ] + }, + { + text: '附錄:技術方案', + items: [ + '寫程式碼時遇到錯誤怎麼辦', + '七款 AI 程式設計工具對比', + '用設計和程式設計 Agent 設計網站' + ] + } + ], + 'es-es': [ + { + text: 'Primeros pasos', + items: ['Ruta de aprendizaje', 'En la era de la IA, hablar es programar'] + }, + { + text: 'Práctica de prototipos de producto', + items: [ + 'Aprender herramientas de programación con IA', + 'Encontrar buenas ideas', + 'Construir un prototipo', + 'Integrar capacidades de IA', + 'Práctica de proyecto completo' + ] + }, + { + text: 'Apéndice: pensamiento de negocio', + items: [ + 'Pensamiento de producto y diseño de soluciones', + 'Escenarios de aplicación industrial de IA (B2B)', + 'Inspiración para escenarios de consumo con IA (B2C)' + ] + }, + { + text: 'Apéndice: investigación de usuarios y validación', + items: [ + 'Dónde encontrar ideas: 3 fuentes para principiantes', + 'Double Diamond: primero lo correcto, luego hacerlo bien', + 'Jobs to Be Done para descubrir lo que el usuario quiere lograr', + 'The Mom Test: entrevistas para validar demanda' + ] + }, + { + text: 'Apéndice: soluciones técnicas', + items: [ + 'Qué hacer si aparecen errores al programar', + 'Comparación de siete herramientas de programación con IA', + 'Diseñar sitios web con agentes de diseño y programación' + ] + } + ], + 'fr-fr': [ + { + text: 'Bien démarrer', + items: [ + "Parcours d'apprentissage", + "À l'ère de l'IA, savoir parler, c'est savoir programmer" + ] + }, + { + text: 'Pratique de prototype produit', + items: [ + 'Apprendre les outils de programmation IA', + 'Trouver une bonne idée', + 'Créer un prototype produit', + 'Intégrer des capacités IA', + 'Projet complet en conditions réelles' + ] + }, + { + text: 'Annexe : pensée métier', + items: [ + 'Pensée produit et conception de solutions', + "Scénarios d'application IA en entreprise (B2B)", + "Inspirations de scénarios consommateurs avec l'IA (B2C)" + ] + }, + { + text: 'Annexe : recherche utilisateur et validation', + items: [ + 'Où trouver des idées : 3 sources pour débutants', + "Double Diamond : faire d'abord la bonne chose, puis bien la faire", + 'Jobs to Be Done pour trouver ce que les utilisateurs veulent accomplir', + 'The Mom Test : entretiens utilisateur pour valider la demande' + ] + }, + { + text: 'Annexe : solutions techniques', + items: [ + "Que faire en cas d'erreur de code", + 'Comparaison de sept outils de programmation IA', + 'Concevoir un site avec des agents de design et de programmation' + ] + } + ], + 'de-de': [ + { + text: 'Einstieg', + items: ['Lernpfad', 'Im KI-Zeitalter reicht Reden zum Programmieren'] + }, + { + text: 'Produktprototyp-Praxis', + items: [ + 'KI-Programmierwerkzeuge lernen', + 'Gute Ideen finden', + 'Produktprototyp erstellen', + 'KI-Fähigkeiten integrieren', + 'Vollständiges Projektpraktikum' + ] + }, + { + text: 'Anhang: Geschäftsdenken', + items: [ + 'Produktdenken und Lösungsentwurf', + 'KI-Anwendungsszenarien in Branchen (B2B)', + 'Inspiration für KI-Konsumszenarien (B2C)' + ] + }, + { + text: 'Anhang: Nutzerforschung und Validierung', + items: [ + 'Wo findet man Ideen: 3 Quellen für Einsteiger', + 'Double Diamond: Erst das Richtige tun, dann richtig tun', + 'Mit Jobs to Be Done erkennen, was Nutzer wirklich erreichen wollen', + 'The Mom Test: Nutzerinterviews zur Nachfragevalidierung' + ] + }, + { + text: 'Anhang: technische Lösungen', + items: [ + 'Was tun bei Fehlern im Code', + 'Vergleich von sieben KI-Programmierwerkzeugen', + 'Websites mit Design- und Programmier-Agenten entwerfen' + ] + } + ], + 'ar-sa': [ + { + text: 'البدء', + items: [ + 'خريطة التعلم', + 'في عصر الذكاء الاصطناعي، من يستطيع التحدث يستطيع البرمجة' + ] + }, + { + text: 'تطبيق نموذج المنتج الأولي', + items: [ + 'تعلم أدوات البرمجة بالذكاء الاصطناعي', + 'إيجاد فكرة جيدة', + 'بناء نموذج أولي للمنتج', + 'دمج قدرات الذكاء الاصطناعي', + 'مشروع عملي كامل' + ] + }, + { + text: 'ملحق: التفكير التجاري', + items: [ + 'التفكير المنتجي وتصميم الحلول', + 'سيناريوهات تطبيق الذكاء الاصطناعي في الصناعة (B2B)', + 'إلهام سيناريوهات المستهلك بالذكاء الاصطناعي (B2C)' + ] + }, + { + text: 'ملحق: بحث المستخدم والتحقق من الطلب', + items: [ + 'من أين تجد الأفكار: 3 مصادر مناسبة للمبتدئين', + 'نموذج الماس المزدوج: افعل الشيء الصحيح أولاً ثم افعله بشكل صحيح', + 'استخدم Jobs to Be Done لمعرفة ما يريد المستخدم إنجازه', + 'The Mom Test: مقابلات المستخدم للتحقق من الطلب' + ] + }, + { + text: 'ملحق: حلول تقنية', + items: [ + 'ماذا تفعل عند ظهور أخطاء في الكود', + 'مقارنة سبع أدوات برمجة بالذكاء الاصطناعي', + 'تصميم المواقع باستخدام وكلاء التصميم والبرمجة' + ] + } + ], + 'vi-vn': [ + { + text: 'Nhập môn', + items: ['Lộ trình học tập', 'Thời đại AI, biết nói là biết lập trình'] + }, + { + text: 'Thực hành nguyên mẫu sản phẩm', + items: [ + 'Học công cụ lập trình AI', + 'Tìm ý tưởng tốt', + 'Xây dựng nguyên mẫu sản phẩm', + 'Tích hợp năng lực AI', + 'Thực chiến dự án hoàn chỉnh' + ] + }, + { + text: 'Phụ lục: tư duy kinh doanh', + items: [ + 'Tư duy sản phẩm và thiết kế giải pháp', + 'Tham khảo kịch bản ứng dụng AI trong ngành (B2B)', + 'Gợi ý kịch bản tiêu dùng với AI (B2C)' + ] + }, + { + text: 'Phụ lục: nghiên cứu người dùng và xác thực nhu cầu', + items: [ + 'Tìm ý tưởng ở đâu: 3 nguồn phù hợp cho người mới', + 'Double Diamond: làm đúng việc trước, rồi làm đúng cách', + 'Dùng Jobs to Be Done để tìm điều người dùng thật sự muốn hoàn thành', + 'The Mom Test: phỏng vấn người dùng để xác thực nhu cầu' + ] + }, + { + text: 'Phụ lục: giải pháp kỹ thuật', + items: [ + 'Làm gì khi gặp lỗi trong code', + 'So sánh bảy công cụ lập trình AI', + 'Thiết kế website bằng agent thiết kế và agent lập trình' + ] + } + ] +} + +const applySidebarLabels = (sidebar, locale) => { + const labels = stage1SidebarLabels[locale] + if (!labels) return sidebar + + return sidebar.map((group, groupIndex) => ({ + ...group, + text: labels[groupIndex]?.text ?? group.text, + items: group.items.map((item, itemIndex) => ({ + ...item, + text: labels[groupIndex]?.items[itemIndex] ?? item.text + })) + })) +} + +const getStage1Sidebar = (locale) => { + if (locale === 'zh-cn') return productManagerSidebar + if (locale === 'en') return productManagerSidebarEn + if (locale === 'ko-kr') return productManagerSidebarKo + return applySidebarLabels( + localizeSidebarLinks(productManagerSidebarEn, locale), + locale + ) +} + export default defineConfig({ markdown: { config: (md) => { @@ -2427,8 +2756,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'ホーム', link: '/ja-jp/' }, { text: '初心者とPM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/ja-jp/stage-1/learning-map/', + activeMatch: '/ja-jp/stage-1/' }, { text: 'フルスタック開発', @@ -2446,8 +2775,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - // TODO: Add Japanese sidebar when content is ready - sidebar: {} + sidebar: { + '/ja-jp/stage-1/': getStage1Sidebar('ja-jp') + } } }, 'zh-tw': { @@ -2478,8 +2808,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: '首頁', link: '/zh-tw/' }, { text: '新手與產品原型', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/zh-tw/stage-1/learning-map/', + activeMatch: '/zh-tw/stage-1/' }, { text: '初中級開發', @@ -2497,7 +2827,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/zh-tw/stage-1/': getStage1Sidebar('zh-tw') + } } }, 'ko-kr': { @@ -2580,8 +2912,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'Inicio', link: '/es-es/' }, { text: 'Principiante y PM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/es-es/stage-1/learning-map/', + activeMatch: '/es-es/stage-1/' }, { text: 'Desarrollo Full Stack', @@ -2599,7 +2931,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/es-es/stage-1/': getStage1Sidebar('es-es') + } } }, 'fr-fr': { @@ -2630,8 +2964,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'Accueil', link: '/fr-fr/' }, { text: 'Débutant & PM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/fr-fr/stage-1/learning-map/', + activeMatch: '/fr-fr/stage-1/' }, { text: 'Développement Full Stack', @@ -2649,7 +2983,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/fr-fr/stage-1/': getStage1Sidebar('fr-fr') + } } }, 'de-de': { @@ -2680,8 +3016,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'Start', link: '/de-de/' }, { text: 'Anfänger & PM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/de-de/stage-1/learning-map/', + activeMatch: '/de-de/stage-1/' }, { text: 'Full Stack Entwicklung', @@ -2699,7 +3035,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/de-de/stage-1/': getStage1Sidebar('de-de') + } } }, 'ar-sa': { @@ -2730,8 +3068,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'الرئيسية', link: '/ar-sa/' }, { text: 'مبتدأ & PM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/ar-sa/stage-1/learning-map/', + activeMatch: '/ar-sa/stage-1/' }, { text: 'تطوير Full Stack', @@ -2749,7 +3087,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/ar-sa/stage-1/': getStage1Sidebar('ar-sa') + } } }, 'vi-vn': { @@ -2781,8 +3121,8 @@ Sitemap: ${siteUrl}/sitemap.xml { text: 'Trang chủ', link: '/vi-vn/' }, { text: 'Người mới & PM', - link: '/zh-cn/stage-1/learning-map/', - activeMatch: '/zh-cn/stage-1/' + link: '/vi-vn/stage-1/learning-map/', + activeMatch: '/vi-vn/stage-1/' }, { text: 'Phát triển Full Stack', @@ -2800,7 +3140,9 @@ Sitemap: ${siteUrl}/sitemap.xml activeMatch: '/zh-cn/appendix/' } ], - sidebar: {} + sidebar: { + '/vi-vn/stage-1/': getStage1Sidebar('vi-vn') + } } } } diff --git a/docs/.vitepress/theme/data/relatedArticles.js b/docs/.vitepress/theme/data/relatedArticles.js index a1050c0..074a116 100644 --- a/docs/.vitepress/theme/data/relatedArticles.js +++ b/docs/.vitepress/theme/data/relatedArticles.js @@ -4,7 +4,7 @@ * - value: 该文档底部相关文章卡片数组 * 页面只负责按 key 读取并渲染,不在页面内重复维护映射数据。 */ -export const relatedArticlesMap = { +const rawRelatedArticlesMap = { 'zh-cn/stage-1/learning-map': [ { href: '/zh-cn/stage-1/ai-capabilities-through-games/', @@ -178,3 +178,41 @@ export const relatedArticlesMap = { } ] } + +const supportedLocales = [ + 'zh-cn', + 'en', + 'zh-tw', + 'ja-jp', + 'ko-kr', + 'es-es', + 'fr-fr', + 'de-de', + 'ar-sa', + 'vi-vn' +] + +const getLocaleFromKey = (key) => + supportedLocales.find((locale) => key.startsWith(`${locale}/`)) + +const localizeArticleLinks = (items, locale) => + items.map((item) => ({ + ...item, + href: item.href.replace(/^\/zh-cn\/stage-1\//, `/${locale}/stage-1/`) + })) + +export const relatedArticlesMap = new Proxy(rawRelatedArticlesMap, { + get(target, prop) { + if (typeof prop !== 'string') return target[prop] + if (prop in target) return target[prop] + + const locale = getLocaleFromKey(prop) + if (!locale || locale === 'zh-cn') return undefined + + const fallbackKey = prop.replace(`${locale}/`, 'zh-cn/') + const fallbackItems = target[fallbackKey] + if (!fallbackItems) return undefined + + return localizeArticleLinks(fallbackItems, locale) + } +}) diff --git a/docs/de-de/stage-1/introduction-to-ai-ide/index.md b/docs/de-de/stage-1/introduction-to-ai-ide/index.md index 342b5a7..e614ae0 100644 --- a/docs/de-de/stage-1/introduction-to-ai-ide/index.md +++ b/docs/de-de/stage-1/introduction-to-ai-ide/index.md @@ -12,7 +12,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = 'ca. 1 Tag, in mehreren Sitzungen moeglich' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/introduction-to-ai-ide'] ?? [] + relatedArticlesMap['de-de/stage-1/introduction-to-ai-ide'] ?? [] diff --git a/docs/es-es/stage-1/integrating-ai-capabilities/index.md b/docs/es-es/stage-1/integrating-ai-capabilities/index.md index 477df38..b0e582c 100644 --- a/docs/es-es/stage-1/integrating-ai-capabilities/index.md +++ b/docs/es-es/stage-1/integrating-ai-capabilities/index.md @@ -8,7 +8,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = 'aprox. 1 dia' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/integrating-ai-capabilities'] ?? [] + relatedArticlesMap['es-es/stage-1/integrating-ai-capabilities'] ?? [] # Nivel inicial 4: Incorporar capacidades de IA al prototipo diff --git a/docs/fr-fr/stage-1/ai-capabilities-through-games/index.md b/docs/fr-fr/stage-1/ai-capabilities-through-games/index.md index 47a4088..80aa432 100644 --- a/docs/fr-fr/stage-1/ai-capabilities-through-games/index.md +++ b/docs/fr-fr/stage-1/ai-capabilities-through-games/index.md @@ -14,7 +14,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = 'Environ 4 heures, réalisables en plusieurs sessions' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/ai-capabilities-through-games'] ?? [] + relatedArticlesMap['fr-fr/stage-1/ai-capabilities-through-games'] ?? [] ## Introduction du chapitre diff --git a/docs/ja-jp/stage-1/ai-capabilities-through-games/index.md b/docs/ja-jp/stage-1/ai-capabilities-through-games/index.md index e28c036..c7cc729 100644 --- a/docs/ja-jp/stage-1/ai-capabilities-through-games/index.md +++ b/docs/ja-jp/stage-1/ai-capabilities-through-games/index.md @@ -14,7 +14,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = '約 4 時間、複数回に分けて完了可能' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/ai-capabilities-through-games'] ?? [] + relatedArticlesMap['ja-jp/stage-1/ai-capabilities-through-games'] ?? [] ## 本章のガイド diff --git a/docs/ja-jp/stage-1/integrating-ai-capabilities/index.md b/docs/ja-jp/stage-1/integrating-ai-capabilities/index.md index 64a89e6..e2e9457 100644 --- a/docs/ja-jp/stage-1/integrating-ai-capabilities/index.md +++ b/docs/ja-jp/stage-1/integrating-ai-capabilities/index.md @@ -8,7 +8,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = '約 1 日' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/integrating-ai-capabilities'] ?? [] + relatedArticlesMap['ja-jp/stage-1/integrating-ai-capabilities'] ?? [] # 初級四:プロトタイプに AI 能力を注入する diff --git a/docs/ja-jp/stage-1/introduction-to-ai-ide/index.md b/docs/ja-jp/stage-1/introduction-to-ai-ide/index.md index c4741a9..53f8bd8 100644 --- a/docs/ja-jp/stage-1/introduction-to-ai-ide/index.md +++ b/docs/ja-jp/stage-1/introduction-to-ai-ide/index.md @@ -12,7 +12,7 @@ import { relatedArticlesMap } from '@theme/data/relatedArticles' const duration = '約 1 日、複数回に分けても可' const relatedArticles = - relatedArticlesMap['zh-cn/stage-1/introduction-to-ai-ide'] ?? [] + relatedArticlesMap['ja-jp/stage-1/introduction-to-ai-ide'] ?? []