Browse Source

Merge pull request #390 from metaplex-foundation/localization/initial-jp-kr

Localization/initial jp kr
Tony Boyle 4 weeks ago
parent
commit
2cfbbd2d36
100 changed files with 8665 additions and 176 deletions
  1. 223 0
      SEO_EXAMPLE.md
  2. 181 0
      SEO_IMPLEMENTATION.md
  3. 359 0
      TRANSLATION_STATUS.md
  4. 3 1
      package.json
  5. 164 48
      pnpm-lock.yaml
  6. 112 0
      scripts/validate-translations.js
  7. 4 0
      src/components/Header.jsx
  8. 93 0
      src/components/LanguageSwitcher.jsx
  9. 19 5
      src/components/NavList.jsx
  10. 121 0
      src/components/SEOHead.jsx
  11. 4 3
      src/components/TableOfContent.jsx
  12. 225 43
      src/components/helperComponents/guideIndex.jsx
  13. 37 11
      src/components/products/Grid.jsx
  14. 42 26
      src/components/products/Sections.jsx
  15. 44 0
      src/components/products/amman/index.js
  16. 41 0
      src/components/products/aura/index.js
  17. 92 0
      src/components/products/bubblegum-v2/index.js
  18. 110 0
      src/components/products/bubblegum/index.js
  19. 71 0
      src/components/products/candyMachine/index.js
  20. 53 0
      src/components/products/cli/index.js
  21. 131 0
      src/components/products/core/index.js
  22. 95 0
      src/components/products/coreCandyMachine/index.js
  23. 50 0
      src/components/products/das-api/index.js
  24. 44 0
      src/components/products/fusion/index.js
  25. 77 0
      src/components/products/global/index.js
  26. 134 0
      src/components/products/guides/index.js
  27. 35 0
      src/components/products/hydra/index.js
  28. 65 0
      src/components/products/inscription/index.js
  29. 101 0
      src/components/products/legacyDocumentation/index.js
  30. 80 0
      src/components/products/mpl-hybrid/index.js
  31. 41 0
      src/components/products/shank/index.js
  32. 14 0
      src/components/products/sugar/index.js
  33. 86 0
      src/components/products/tokenAuthRules/index.js
  34. 104 0
      src/components/products/tokenMetadata/index.js
  35. 116 0
      src/components/products/umi/index.js
  36. 168 0
      src/config/TRANSLATIONS_REVIEW.md
  37. 340 0
      src/config/TRANSLATION_SYSTEM.md
  38. 113 0
      src/config/languages.js
  39. 523 0
      src/config/navigation-translations.js
  40. 83 0
      src/contexts/LocaleContext.js
  41. 12 0
      src/i18n.js
  42. 63 0
      src/locales/en.json
  43. 63 0
      src/locales/ja.json
  44. 63 0
      src/locales/ko.json
  45. 1 0
      src/middleware.js
  46. 28 0
      src/pages/404.js
  47. 0 11
      src/pages/404.md
  48. 16 24
      src/pages/_app.jsx
  49. 27 4
      src/pages/_document.jsx
  50. 48 0
      src/pages/ja/amman/cli-commands.md
  51. 126 0
      src/pages/ja/amman/configuration.md
  52. 37 0
      src/pages/ja/amman/getting-started.md
  53. 15 0
      src/pages/ja/amman/index.md
  54. 206 0
      src/pages/ja/amman/pre-made-configs.md
  55. 13 0
      src/pages/ja/aura/blockchains/eclipse.md
  56. 13 0
      src/pages/ja/aura/blockchains/solana.md
  57. 18 0
      src/pages/ja/aura/faq.md
  58. 17 0
      src/pages/ja/aura/index.md
  59. 17 0
      src/pages/ja/aura/reading-solana-and-svm-data.md
  60. 49 0
      src/pages/ja/bubblegum-v2/burn-cnfts.md
  61. 49 0
      src/pages/ja/bubblegum-v2/collections.md
  62. 60 0
      src/pages/ja/bubblegum-v2/concurrent-merkle-trees.md
  63. 50 0
      src/pages/ja/bubblegum-v2/create-trees.md
  64. 70 0
      src/pages/ja/bubblegum-v2/delegate-cnfts.md
  65. 57 0
      src/pages/ja/bubblegum-v2/delegate-trees.md
  66. 60 0
      src/pages/ja/bubblegum-v2/faq.md
  67. 40 0
      src/pages/ja/bubblegum-v2/fetch-cnfts.md
  68. 50 0
      src/pages/ja/bubblegum-v2/freeze-cnfts.md
  69. 51 0
      src/pages/ja/bubblegum-v2/hashed-nft-data.md
  70. 147 0
      src/pages/ja/bubblegum-v2/index.md
  71. 33 0
      src/pages/ja/bubblegum-v2/merkle-tree-canopy.md
  72. 40 0
      src/pages/ja/bubblegum-v2/mint-cnfts.md
  73. 15 0
      src/pages/ja/bubblegum-v2/sdk/index.md
  74. 46 0
      src/pages/ja/bubblegum-v2/sdk/javascript.md
  75. 116 0
      src/pages/ja/bubblegum-v2/sdk/rust.md
  76. 49 0
      src/pages/ja/bubblegum-v2/stored-nft-data.md
  77. 50 0
      src/pages/ja/bubblegum-v2/transfer-cnfts.md
  78. 74 0
      src/pages/ja/bubblegum-v2/update-cnfts.md
  79. 94 0
      src/pages/ja/bubblegum-v2/verify-creators.md
  80. 47 0
      src/pages/ja/bubblegum/burn-cnfts.md
  81. 50 0
      src/pages/ja/bubblegum/create-trees.md
  82. 157 0
      src/pages/ja/bubblegum/decompress-cnfts.md
  83. 70 0
      src/pages/ja/bubblegum/delegate-cnfts.md
  84. 57 0
      src/pages/ja/bubblegum/delegate-trees.md
  85. 15 0
      src/pages/ja/bubblegum/guides/index.md
  86. 661 0
      src/pages/ja/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana.md
  87. 150 0
      src/pages/ja/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms.md
  88. 145 0
      src/pages/ja/bubblegum/index.md
  89. 195 0
      src/pages/ja/bubblegum/mint-cnfts.md
  90. 15 0
      src/pages/ja/bubblegum/sdk/index.md
  91. 47 0
      src/pages/ja/bubblegum/sdk/javascript.md
  92. 119 0
      src/pages/ja/bubblegum/sdk/rust.md
  93. 50 0
      src/pages/ja/bubblegum/transfer-cnfts.md
  94. 72 0
      src/pages/ja/bubblegum/update-cnfts.md
  95. 99 0
      src/pages/ja/bubblegum/verify-collections.md
  96. 57 0
      src/pages/ja/bubblegum/verify-creators.md
  97. 191 0
      src/pages/ja/candy-machine/custom-guards/generating-client.md
  98. 17 0
      src/pages/ja/candy-machine/getting-started/index.md
  99. 50 0
      src/pages/ja/candy-machine/getting-started/js.md
  100. 20 0
      src/pages/ja/candy-machine/getting-started/rust.md

+ 223 - 0
SEO_EXAMPLE.md

@@ -0,0 +1,223 @@
+# SEO Implementation Example
+
+## What Gets Generated
+
+When a user visits any page on the Metaplex Developer Hub, the SEOHead component automatically generates comprehensive SEO tags.
+
+### Example Page: Core Documentation
+
+For a Core documentation page at `/core`, here's what gets generated for each language:
+
+---
+
+## English Version (`/core`)
+
+```html
+<head>
+  <!-- Primary Meta Tags -->
+  <title>Core | Metaplex Developer Hub</title>
+  <meta name="description" content="Learn about Metaplex Core NFT standard" />
+
+  <!-- Canonical URL - points to itself -->
+  <link rel="canonical" href="https://developers.metaplex.com/core" />
+
+  <!-- Language Alternates (hreflang) -->
+  <link rel="alternate" hreflang="en" href="https://developers.metaplex.com/core" />
+  <link rel="alternate" hreflang="ja" href="https://developers.metaplex.com/ja/core" />
+  <link rel="alternate" hreflang="ko" href="https://developers.metaplex.com/ko/core" />
+  <link rel="alternate" hreflang="x-default" href="https://developers.metaplex.com/core" />
+
+  <!-- Open Graph -->
+  <meta property="og:title" content="Core | Metaplex Developer Hub" />
+  <meta property="og:description" content="Learn about Metaplex Core NFT standard" />
+  <meta property="og:url" content="https://developers.metaplex.com/core" />
+  <meta property="og:type" content="website" />
+  <meta property="og:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="og:locale" content="en_US" />
+  <meta property="og:locale:alternate" content="ja_JP" />
+  <meta property="og:locale:alternate" content="ko_KR" />
+
+  <!-- Twitter Card -->
+  <meta name="twitter:card" content="summary_large_image" />
+  <meta name="twitter:title" content="Core | Metaplex Developer Hub" />
+  <meta name="twitter:description" content="Learn about Metaplex Core NFT standard" />
+  <meta name="twitter:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="twitter:domain" content="developers.metaplex.com" />
+</head>
+```
+
+---
+
+## Japanese Version (`/ja/core`)
+
+```html
+<head>
+  <!-- Primary Meta Tags -->
+  <title>Core | Metaplex 開発者向け</title>
+  <meta name="description" content="Metaplex Core NFT標準について学ぶ" />
+
+  <!-- Canonical URL - points to ITSELF (Japanese version) -->
+  <link rel="canonical" href="https://developers.metaplex.com/ja/core" />
+
+  <!-- Language Alternates (hreflang) - SAME for all versions -->
+  <link rel="alternate" hreflang="en" href="https://developers.metaplex.com/core" />
+  <link rel="alternate" hreflang="ja" href="https://developers.metaplex.com/ja/core" />
+  <link rel="alternate" hreflang="ko" href="https://developers.metaplex.com/ko/core" />
+  <link rel="alternate" hreflang="x-default" href="https://developers.metaplex.com/core" />
+
+  <!-- Open Graph -->
+  <meta property="og:title" content="Core | Metaplex 開発者向け" />
+  <meta property="og:description" content="Metaplex Core NFT標準について学ぶ" />
+  <meta property="og:url" content="https://developers.metaplex.com/ja/core" />
+  <meta property="og:type" content="website" />
+  <meta property="og:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="og:locale" content="ja_JP" />
+  <meta property="og:locale:alternate" content="en_US" />
+  <meta property="og:locale:alternate" content="ko_KR" />
+
+  <!-- Twitter Card -->
+  <meta name="twitter:card" content="summary_large_image" />
+  <meta name="twitter:title" content="Core | Metaplex 開発者向け" />
+  <meta name="twitter:description" content="Metaplex Core NFT標準について学ぶ" />
+  <meta name="twitter:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="twitter:domain" content="developers.metaplex.com" />
+</head>
+```
+
+---
+
+## Korean Version (`/ko/core`)
+
+```html
+<head>
+  <!-- Primary Meta Tags -->
+  <title>Core | Metaplex 개발자 허브</title>
+  <meta name="description" content="Metaplex Core NFT 표준에 대해 알아보기" />
+
+  <!-- Canonical URL - points to ITSELF (Korean version) -->
+  <link rel="canonical" href="https://developers.metaplex.com/ko/core" />
+
+  <!-- Language Alternates (hreflang) - SAME for all versions -->
+  <link rel="alternate" hreflang="en" href="https://developers.metaplex.com/core" />
+  <link rel="alternate" hreflang="ja" href="https://developers.metaplex.com/ja/core" />
+  <link rel="alternate" hreflang="ko" href="https://developers.metaplex.com/ko/core" />
+  <link rel="alternate" hreflang="x-default" href="https://developers.metaplex.com/core" />
+
+  <!-- Open Graph -->
+  <meta property="og:title" content="Core | Metaplex 개발자 허브" />
+  <meta property="og:description" content="Metaplex Core NFT 표준에 대해 알아보기" />
+  <meta property="og:url" content="https://developers.metaplex.com/ko/core" />
+  <meta property="og:type" content="website" />
+  <meta property="og:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="og:locale" content="ko_KR" />
+  <meta property="og:locale:alternate" content="en_US" />
+  <meta property="og:locale:alternate" content="ja_JP" />
+
+  <!-- Twitter Card -->
+  <meta name="twitter:card" content="summary_large_image" />
+  <meta name="twitter:title" content="Core | Metaplex 개발자 허브" />
+  <meta name="twitter:description" content="Metaplex Core NFT 표준에 대해 알아보기" />
+  <meta name="twitter:image" content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg" />
+  <meta property="twitter:domain" content="developers.metaplex.com" />
+</head>
+```
+
+---
+
+## Key SEO Principles
+
+### 1. Self-Referential Canonicals ✅
+Each language version points to **itself** as canonical:
+- English canonical → English URL
+- Japanese canonical → Japanese URL
+- Korean canonical → Korean URL
+
+**Why?** Tells Google these are all equally important, just in different languages.
+
+### 2. Identical hreflang Tags ✅
+**All three versions have the exact same hreflang tags** pointing to all alternates.
+
+**Why?** Creates a complete bidirectional relationship between language versions.
+
+### 3. Proper og:locale ✅
+Each version declares its own locale and lists others as alternates:
+- English: `og:locale="en_US"` + alternates for ja_JP, ko_KR
+- Japanese: `og:locale="ja_JP"` + alternates for en_US, ko_KR
+- Korean: `og:locale="ko_KR"` + alternates for en_US, ja_JP
+
+**Why?** Social media platforms serve the right version when shared.
+
+### 4. x-default for Fallback ✅
+All versions include `hreflang="x-default"` pointing to English.
+
+**Why?** If user's language isn't available, Google serves the default (English).
+
+---
+
+## How Search Engines Use This
+
+### Google Search
+1. **User in Japan searches in Japanese** → Serves `/ja/core`
+2. **User in Korea searches in Korean** → Serves `/ko/core`
+3. **User in US searches in English** → Serves `/core`
+4. **User in France searches** → Serves `/core` (x-default)
+
+### Social Media Sharing
+1. **Tweet link to `/core`** → Shows English title/description
+2. **Tweet link to `/ja/core`** → Shows Japanese title/description
+3. **Tweet link to `/ko/core`** → Shows Korean title/description
+
+### No Duplicate Content Penalty
+Google understands these are **translations**, not duplicates, because:
+- ✅ Self-referential canonicals (each points to itself)
+- ✅ Complete hreflang annotations
+- ✅ Proper og:locale declarations
+- ✅ Different content language in actual page
+
+---
+
+## Validation Tools
+
+After deployment, test with:
+
+1. **Google Rich Results Test**
+   - https://search.google.com/test/rich-results
+   - Check each language version
+
+2. **Facebook Sharing Debugger**
+   - https://developers.facebook.com/tools/debug/
+   - Verify og:locale tags work
+
+3. **Twitter Card Validator**
+   - https://cards-dev.twitter.com/validator
+   - Confirm Twitter cards appear correctly
+
+4. **Google Search Console**
+   - Submit all language versions
+   - Monitor for hreflang errors
+   - Check international targeting
+
+---
+
+## Component Usage
+
+All of this is handled automatically by the `SEOHead` component:
+
+```jsx
+// In _app.jsx
+<SEOHead
+  title={page.title}           // From markdown frontmatter
+  description={page.description}
+  metaTitle={page.metaTitle}
+  locale={page.locale}          // Auto-detected from URL
+/>
+```
+
+The component:
+1. Detects current language from URL path
+2. Generates all alternate URLs automatically
+3. Creates proper canonical URL
+4. Sets all Open Graph and Twitter tags
+5. Handles hreflang bidirectional links
+
+**Zero manual configuration needed per page!** 🎉

+ 181 - 0
SEO_IMPLEMENTATION.md

@@ -0,0 +1,181 @@
+# SEO Implementation for Multilingual Documentation
+
+**Date:** 2025-10-12
+**Status:** ✅ Complete
+
+## Overview
+
+This document outlines the SEO implementation for the Metaplex Developer Hub's multilingual documentation, following international best practices for language-specific content.
+
+## Changes Made
+
+### 1. Language Code Standardization
+
+Updated from custom codes to ISO 639-1 standard:
+
+| Old Code | New Code | Language | Reason |
+|----------|----------|----------|--------|
+| `/jp/` | `/ja/` | Japanese | ISO 639-1 standard for Japanese |
+| `/kr/` | `/ko/` | Korean | ISO 639-1 standard for Korean |
+
+**Why This Matters:**
+- hreflang tags require ISO 639-1 language codes
+- Improves international SEO
+- Aligns with web standards
+- Prevents confusion with search engines
+
+### 2. New SEO Components
+
+#### Language Configuration (`src/config/languages.js`)
+Central configuration for all language settings:
+- ISO 639-1 language codes
+- URL path mappings
+- Native language names
+- Helper functions for path generation
+
+#### SEOHead Component (`src/components/SEOHead.jsx`)
+Comprehensive SEO meta tags including:
+- **Title and Description**: Translated for each language
+- **Canonical URLs**: Self-referential (each language version points to itself)
+- **hreflang Tags**: Proper alternate language links
+- **Open Graph**: Language-aware OG tags with locale alternates
+- **Twitter Card**: Complete social media meta tags
+
+### 3. File Migrations
+
+#### Directories Renamed
+- `/src/pages/jp/` → `/src/pages/ja/` (437 files)
+- `/src/pages/kr/` → `/src/pages/ko/` (437 files)
+
+#### Locale Files Renamed
+- `/src/locales/jp.json` → `/src/locales/ja.json`
+- `/src/locales/kr.json` → `/src/locales/ko.json`
+
+### 4. Configuration Updates
+
+#### Updated Files
+1. **`src/contexts/LocaleContext.js`**
+   - Changed locale detection from `jp`/`kr` to `ja`/`ko`
+   - Updated import paths for locale JSON files
+
+2. **`src/components/LanguageSwitcher.jsx`**
+   - Updated language codes to `ja` and `ko`
+   - Updated path detection logic
+
+3. **`src/shared/localizedSections.js`**
+   - Changed translation keys from `jp`/`kr` to `ja`/`ko`
+
+4. **`src/shared/productTranslations.js`**
+   - Added Korean (`ko`) translations
+   - Changed Japanese key from `jp` to `ja`
+
+5. **`src/i18n.js`**
+   - Updated locale array: `['en', 'ja', 'ko']`
+
+6. **`src/pages/_app.jsx`**
+   - Replaced custom Head implementation with new SEOHead component
+   - Now automatically generates all SEO tags
+
+## SEO Features Implemented
+
+### ✅ Canonical URLs
+Each language version has a self-referential canonical URL:
+- English: `https://developers.metaplex.com/page`
+- Japanese: `https://developers.metaplex.com/ja/page`
+- Korean: `https://developers.metaplex.com/ko/page`
+
+### ✅ hreflang Tags
+Every page includes alternate language links:
+```html
+<link rel="alternate" hreflang="en" href="https://developers.metaplex.com/page" />
+<link rel="alternate" hreflang="ja" href="https://developers.metaplex.com/ja/page" />
+<link rel="alternate" hreflang="ko" href="https://developers.metaplex.com/ko/page" />
+<link rel="alternate" hreflang="x-default" href="https://developers.metaplex.com/page" />
+```
+
+### ✅ Open Graph Localization
+- `og:locale` set to proper format (e.g., `ja_JP`, `ko_KR`)
+- `og:locale:alternate` tags for other languages
+- Prevents duplicate content issues
+
+### ✅ Translated Meta Tags
+- Page titles translated in frontmatter
+- Meta descriptions translated
+- All social media cards properly localized
+
+## URL Structure
+
+### Final URL Pattern
+- **English (default):** `developers.metaplex.com/[page]`
+- **Japanese:** `developers.metaplex.com/ja/[page]`
+- **Korean:** `developers.metaplex.com/ko/[page]`
+
+### Example Pages
+| English | Japanese | Korean |
+|---------|----------|--------|
+| `/core` | `/ja/core` | `/ko/core` |
+| `/candy-machine` | `/ja/candy-machine` | `/ko/candy-machine` |
+| `/umi` | `/ja/umi` | `/ko/umi` |
+
+## Adding New Languages
+
+To add a new language in the future:
+
+1. **Update Language Config** (`src/config/languages.js`):
+   ```js
+   es: {
+     code: 'es',           // ISO 639-1 code
+     urlPath: '/es',       // URL prefix
+     name: 'Spanish',
+     nativeName: 'Español',
+     isDefault: false,
+   }
+   ```
+
+2. **Create Locale File**: `/src/locales/es.json`
+
+3. **Update i18n Config** (`src/i18n.js`):
+   ```js
+   const locales = ['en', 'ja', 'ko', 'es'];
+   ```
+
+4. **Add Translations** to:
+   - `src/shared/localizedSections.js`
+   - `src/shared/productTranslations.js`
+
+5. **Create Pages Directory**: `/src/pages/es/`
+
+The SEO system will automatically:
+- Generate hreflang tags
+- Create canonical URLs
+- Add Open Graph locale tags
+- Handle language switching
+
+## Testing SEO
+
+### Verify Implementation
+1. **Check hreflang tags**: View page source and look for `<link rel="alternate" hreflang="..."`
+2. **Test canonical URLs**: Ensure each language version has correct self-referential canonical
+3. **Validate Open Graph**: Use [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/)
+4. **Check Twitter Cards**: Use [Twitter Card Validator](https://cards-dev.twitter.com/validator)
+
+### Google Search Console
+After deployment:
+1. Submit all language versions to Search Console
+2. Monitor for any hreflang errors
+3. Verify language targeting is working correctly
+
+## Benefits
+
+✅ **No Duplicate Content Penalties**: Each language version properly identified
+✅ **Improved International SEO**: Search engines serve correct language to users
+✅ **Better User Experience**: Users see content in their preferred language
+✅ **Future-Proof**: Easy to add more languages
+✅ **Standards Compliant**: Follows W3C and Google best practices
+
+## References
+
+- [Google Search Central - Localized Versions](https://developers.google.com/search/docs/specialty/international/localized-versions)
+- [ISO 639-1 Language Codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
+- [hreflang Best Practices](https://developers.google.com/search/docs/specialty/international/localized-versions#html)
+- [Open Graph Protocol](https://ogp.me/)

+ 359 - 0
TRANSLATION_STATUS.md

@@ -0,0 +1,359 @@
+# Translation Status Report
+
+**Last Updated:** 2025-10-12
+
+## Overview
+
+This document tracks the translation status of the Metaplex Developer Hub documentation into Japanese (ja) and Korean (ko).
+
+**Important:** Language codes have been updated to ISO 639-1 standard:
+- Japanese: `jp` → `ja` (ISO 639-1)
+- Korean: `kr` → `ko` (ISO 639-1)
+
+This ensures proper SEO with hreflang tags and follows web standards.
+
+### Summary Statistics
+
+| Language | Files Translated | Total Files | Coverage | Missing |
+|----------|-----------------|-------------|----------|---------|
+| English  | 437 (baseline)  | 437         | 100%     | 0       |
+| Japanese (ja) | 437             | 437         | 🎉 **100%** | 0       |
+| Korean (ko)   | 437             | 437         | 🎉 **100%** | 0       |
+
+## Japanese Translation Status (437/437 - 100%) ✅
+
+### 🎉 COMPLETE - All Files Translated!
+
+#### High Priority - Active Products
+
+##### Core Candy Machine Guards (2 files) - 🎉 MOSTLY COMPLETE
+Remaining guard documentation pages:
+- `core-candy-machine/guards/freeze-sol-payment.md`
+- `core-candy-machine/guards/freeze-token-payment.md`
+
+**Recently Completed (27 files):**
+- ✅ address-gate.md
+- ✅ allocation.md
+- ✅ allow-list.md
+- ✅ asset-burn-multi.md
+- ✅ asset-burn.md
+- ✅ asset-gate.md
+- ✅ asset-mint-limit.md
+- ✅ asset-payment-multi.md
+- ✅ asset-payment.md
+- ✅ edition.md
+- ✅ end-date.md
+- ✅ gatekeeper.md
+- ✅ mint-limit.md
+- ✅ nft-burn.md
+- ✅ nft-gate.md
+- ✅ nft-mint-limit.md
+- ✅ nft-payment.md
+- ✅ program-gate.md
+- ✅ redeemed-amount.md
+- ✅ sol-fixed-fee.md
+- ✅ start-date.md
+- ✅ third-party-signer.md
+- ✅ token-burn.md
+- ✅ token-gate.md
+- ✅ token-payment.md
+- ✅ token2022-payment.md
+- ✅ vanity-mint.md
+
+##### DAS API - ✅ COMPLETE
+**Recently Completed (11 files):**
+- ✅ All core-extension methods (6 files)
+- ✅ das-api/core-extension/plugin-derivation.md
+- ✅ das-api/guides/get-fungible-assets.md
+- ✅ das-api/guides/get-nfts-by-owner.md
+- ✅ das-api/guides/owner-and-collection.md
+- ✅ All other guide files (pagination, search-by-criteria, etc.)
+
+##### Core Candy Machine Documentation - ✅ COMPLETE
+**Recently Completed (4 files):**
+- ✅ core-candy-machine/sdk/javascript.md
+- ✅ core-candy-machine/sdk/rust.md
+- ✅ core-candy-machine/guides/create-a-core-candy-machine-ui.md
+- ✅ core-candy-machine/guides/create-a-core-candy-machine-with-hidden-settings.md
+
+##### MPL Hybrid - ✅ COMPLETE
+**Recently Completed (2 files):**
+- ✅ mpl-hybrid/sdk/index.md
+- ✅ mpl-hybrid/sdk/javascript.md
+(Note: rust.md doesn't exist in source)
+
+#### Medium Priority
+
+##### Guides - ✅ COMPLETE
+**Recently Completed (4 files):**
+- ✅ guides/candy-machine/index.md
+- ✅ guides/candy-machine/airdrop-mint-to-another-wallet.md
+- ✅ guides/candy-machine/mint-to-another-wallet.md
+- ✅ guides/candy-machine/create-an-nft-collection-on-solana-with-candy-machine.md
+(Note: how-to-use-pinata-with-umi.md doesn't exist in source)
+
+#### Low Priority - Legacy Content
+
+##### Legacy Documentation (22 files)
+**Auction House** (11 files):
+- `legacy-documentation/auction-house/auctioneer/index.md`
+- `legacy-documentation/auction-house/auctioneer/endpoints.md`
+- `legacy-documentation/auction-house/auctioneer/installation.md`
+- `legacy-documentation/auction-house/index.md`
+- `legacy-documentation/auction-house/create.md`
+- `legacy-documentation/auction-house/delegate.md`
+- `legacy-documentation/auction-house/execute-sale.md`
+- `legacy-documentation/auction-house/getting-started.md`
+- `legacy-documentation/auction-house/list.md`
+- `legacy-documentation/auction-house/make-offer.md`
+- `legacy-documentation/auction-house/settings.md`
+
+**Developer Tools** (4 files):
+- `legacy-documentation/developer-tools/cli/index.md`
+- `legacy-documentation/developer-tools/cli/auctioneer.md`
+- `legacy-documentation/developer-tools/cli/candy-machine-v2.md`
+- `legacy-documentation/developer-tools/cli/token-metadata.md`
+
+**Fixed Price Sale** (2 files):
+- `legacy-documentation/fixed-price-sale/index.md`
+- `legacy-documentation/fixed-price-sale/creating-a-market.md`
+
+**Mobile SDKs** (2 files):
+- `legacy-documentation/mobile-sdks/android.md`
+- `legacy-documentation/mobile-sdks/ios.md`
+
+**Other** (3 files):
+- `legacy-documentation/index.md`
+- `legacy-documentation/gumdrop.md`
+- `legacy-documentation/nft-standard.md`
+
+---
+
+## Korean Translation Status (437/437 - 100%) ✅
+
+### 🎉 COMPLETE - All Files Translated!
+
+#### High Priority - Active Products
+
+##### DAS API Core Extension - ✅ COMPLETE
+**Recently Completed (10 files):**
+- ✅ das-api/core-extension/index.md
+- ✅ das-api/core-extension/convert-das-asset-to-core.md
+- ✅ das-api/core-extension/plugin-derivation.md
+- ✅ das-api/core-extension/methods/get-asset.md
+- ✅ das-api/core-extension/methods/get-assets-by-authority.md
+- ✅ das-api/core-extension/methods/get-assets-by-collection.md
+- ✅ das-api/core-extension/methods/get-assets-by-owner.md
+- ✅ das-api/core-extension/methods/get-collection.md
+- ✅ das-api/core-extension/methods/search-assets.md
+- ✅ das-api/core-extension/methods/search-collections.md
+
+##### Bubblegum v1 - ✅ COMPLETE
+**Recently Completed (17 files):**
+- ✅ bubblegum/index.md
+- ✅ bubblegum/create-trees.md
+- ✅ bubblegum/mint-cnfts.md
+- ✅ bubblegum/transfer-cnfts.md
+- ✅ bubblegum/update-cnfts.md
+- ✅ bubblegum/burn-cnfts.md
+- ✅ bubblegum/decompress-cnfts.md
+- ✅ bubblegum/delegate-cnfts.md
+- ✅ bubblegum/delegate-trees.md
+- ✅ bubblegum/verify-collections.md
+- ✅ bubblegum/verify-creators.md
+- ✅ bubblegum/guides/index.md
+- ✅ bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana.md
+- ✅ bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms.md
+- ✅ bubblegum/sdk/index.md
+- ✅ bubblegum/sdk/javascript.md
+- ✅ bubblegum/sdk/rust.md
+
+##### Candy Machine Sugar - ✅ COMPLETE
+**Recently Completed (23 files):**
+- ✅ All 19 command files (airdrop, bundlr, collection, config, deploy, freeze, guard, hash, launch, mint, reveal, show, sign, update, upload, validate, verify, withdraw, thaw/unfreeze)
+- ✅ Core files: bring-your-own-uploader.md, cache.md, getting-started.md, installation.md
+
+##### DAS API - ✅ COMPLETE
+**Recently Completed (24 files):**
+- ✅ All 13 methods files (get-asset, get-assets, search-assets, etc.)
+- ✅ All 11 guide files:
+  - collection-statistics.md, find-compressed-nfts.md, find-token-holders.md
+  - get-collection-nfts.md, get-fungible-assets.md, get-nfts-by-owner.md
+  - get-wallet-tokens.md, owner-and-collection.md, search-by-criteria.md
+  - index.md, pagination.md
+
+##### Candy Machine Guards (2 files) - 🎉 MOSTLY COMPLETE
+Remaining files:
+- `candy-machine/guards/freeze-sol-payment.md`
+- `candy-machine/guards/freeze-token-payment.md`
+
+**Recently Completed (15 files):**
+- ✅ address-gate.md, allocation.md, end-date.md, gatekeeper.md, mint-limit.md
+- ✅ nft-burn.md, nft-gate.md, nft-payment.md, program-gate.md, redeemed-amount.md
+- ✅ third-party-signer.md, token-burn.md, token-gate.md, token-payment.md, token2022-payment.md
+
+##### MPL Hybrid - ✅ COMPLETE
+**Recently Completed (2 files):**
+- ✅ mpl-hybrid/sdk/index.md
+- ✅ mpl-hybrid/sdk/javascript.md
+(Note: rust.md doesn't exist in source)
+
+#### Medium Priority
+
+##### Candy Machine Guides - ✅ COMPLETE
+**Recently Completed (4 files):**
+- ✅ guides/candy-machine/index.md
+- ✅ guides/candy-machine/airdrop-mint-to-another-wallet.md
+- ✅ guides/candy-machine/mint-to-another-wallet.md
+- ✅ guides/candy-machine/create-an-nft-collection-on-solana-with-candy-machine.md
+
+#### Low Priority - Legacy Content
+
+##### Legacy Documentation (22 files)
+Same structure as Japanese - entire section missing:
+- Auction House (11 files)
+- Developer Tools (4 files)
+- Fixed Price Sale (2 files)
+- Mobile SDKs (2 files)
+- Other (3 files)
+
+---
+
+## Products with Complete Coverage ✓
+
+The following products have 100% translation coverage in both Japanese and Korean:
+
+- **Core** - Complete NFT standard documentation
+- **Bubblegum v2** - Compressed NFTs (latest version)
+- **UMI** - JavaScript framework
+- **Token Metadata** - Legacy NFT standard
+- **Inscription** - On-chain data storage
+- **Fusion** - NFT combination system
+- **Token Auth Rules** - Authorization framework
+- **Shank** - IDL generation
+- **Hydra** - Fanout wallet
+- **Amman** - Local validator setup
+- **Aura** - Indexing network
+- **CLI** - Command-line interface
+
+---
+
+## Translation Priority Recommendations
+
+### Critical (Start Here)
+
+1. **Core Candy Machine Guards** (JP: 29 files, KR: 17 files)
+   - Actively used features for NFT drops
+   - Essential for developers launching collections
+
+2. **DAS API** (JP: 10 files, KR: 34 files)
+   - Critical API documentation
+   - Required for reading digital asset data
+
+3. **Bubblegum v1** (KR: 17 files only)
+   - Entire product section missing in Korean
+   - Still used by some projects
+
+### High Priority
+
+4. **Candy Machine Sugar CLI** (KR: 23 files only)
+   - Important CLI tool for NFT launches
+   - Missing command documentation
+
+5. **MPL Hybrid SDK** (JP: 3 files, KR: 3 files)
+   - SDK documentation for token-NFT swaps
+
+6. **Core Candy Machine SDK & Guides** (JP: 4 files)
+   - Implementation guides and SDK docs
+
+### Medium Priority
+
+7. **Candy Machine Guides** (KR: 3 files)
+8. **General Guides** (JP: 1 file, KR: 1 file)
+
+### Low Priority
+
+9. **Legacy Documentation** (JP: 22 files, KR: 22 files)
+   - Deprecated products
+   - Lower usage/relevance
+
+---
+
+## Progress Tracking
+
+### Recent Completions
+
+**2025-10-12 (Session 1):**
+- ✅ Completed all 11 root-level Korean pages
+  - community-guides.md, contact.md, developer-tools.md, official-links.md
+  - programs-and-tools.md, protocol-fees.md, rpc-providers.md
+  - security.md, stability-index.md, storage-providers.md, understanding-programs.md
+
+**2025-10-12 (Session 2):**
+- ✅ Completed Core Candy Machine Guards (Japanese) - 27 of 29 files
+  - All guards except freeze-sol-payment and freeze-token-payment
+- ✅ Completed DAS API Core Extension (Korean) - 10 files
+  - Full core-extension section including all methods
+- ✅ Completed Bubblegum v1 (Korean) - 17 files
+  - Entire product section: core docs, guides, and SDK
+
+**2025-10-12 (Session 3):**
+- ✅ Completed DAS API (Japanese) - 8 files
+  - Core extension methods and search guide
+- ✅ Completed Candy Machine Sugar CLI (Korean) - 23 files
+  - All commands and core documentation
+- ✅ Completed Candy Machine Guards (Korean) - 15 of 17 files
+  - All guards except freeze-sol-payment and freeze-token-payment
+
+**2025-10-12 (Session 4):**
+- ✅ Completed Core Candy Machine SDK & Guides (Japanese) - 4 files
+- ✅ Completed MPL Hybrid SDK (Japanese) - 2 files
+- ✅ Completed MPL Hybrid SDK (Korean) - 2 files
+- ✅ Completed Candy Machine Guides (Japanese) - 4 files
+- ✅ Completed Candy Machine Guides (Korean) - 4 files
+- ✅ Completed DAS API Methods & Guides (Korean) - 15 files (all methods + 2 guides)
+
+**2025-10-12 (Session 5):**
+- ✅ Completed DAS API Guides (Korean) - 9 files
+- ✅ Completed DAS API Guides (Japanese) - 3 files
+
+**2025-10-12 (Session 6 - FINAL):**
+- ✅ Completed Freeze Payment Guards (Japanese) - 2 files
+- ✅ Completed Freeze Payment Guards (Korean) - 2 files
+- ✅ Completed Legacy Documentation (Japanese) - 22 files
+- ✅ Completed Legacy Documentation (Korean) - 22 files
+- ✅ Completed remaining DAS API methods (Japanese) - 7 files
+- ✅ Completed remaining guides (Korean) - 1 file
+
+**🎉 FINAL TOTAL PROGRESS:**
+- Japanese: +73 files (364 → 437) - Coverage increased from 83.3% to **100%** ✅
+- Korean: +118 files (319 → 437) - Coverage increased from 73.0% to **100%** ✅
+- **Combined: +191 files translated to 100% completion**
+
+## 🎉 Translation Complete!
+
+**Status: 100% coverage achieved for both Japanese (ja) and Korean (ko)**
+
+All 437 documentation files have been successfully translated into both languages. The Metaplex Developer Hub now offers complete multilingual support.
+
+**URLs:**
+- English: `developers.metaplex.com/`
+- Japanese: `developers.metaplex.com/ja/`
+- Korean: `developers.metaplex.com/ko/`
+
+---
+
+## Notes
+
+- All file paths are relative to `src/pages/`
+- Translation coverage percentages are based on total markdown files
+- Legacy documentation has lower priority due to deprecated status
+- Focus on high-traffic, actively maintained products first
+
+## How to Update This Document
+
+When translations are completed:
+1. Move files from "Missing" lists to a "Recently Completed" section
+2. Update the summary statistics
+3. Update the "Last Updated" date
+4. Commit changes to track progress over time

+ 3 - 1
package.json

@@ -7,7 +7,8 @@
     "build": "next build",
     "start": "next start",
     "lint": "next lint",
-    "sitemap": "node generate-sitemap.mjs"
+    "sitemap": "node generate-sitemap.mjs",
+    "validate-translations": "node scripts/validate-translations.js"
   },
   "browserslist": "defaults, not ie <= 11",
   "dependencies": {
@@ -25,6 +26,7 @@
     "html-to-image": "^1.11.11",
     "lightense-images": "^1.0.17",
     "next": "13.4.7",
+    "next-intl": "^4.3.12",
     "postcss-focus-visible": "^6.0.4",
     "postcss-import": "^14.1.0",
     "prism-react-renderer": "^1.3.5",

+ 164 - 48
pnpm-lock.yaml

@@ -10,10 +10,10 @@ importers:
     dependencies:
       '@docsearch/react':
         specifier: ^3.8.0
-        version: 3.8.0(@algolia/client-search@5.19.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.17.3)
+        version: 3.8.0(@algolia/client-search@5.19.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.17.3)
       '@headlessui/react':
         specifier: 2.2.0
-        version: 2.2.0(react-dom@18.2.0)(react@18.2.0)
+        version: 2.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       '@heroicons/react':
         specifier: ^2.2.0
         version: 2.2.0(react@18.2.0)
@@ -25,7 +25,7 @@ importers:
         version: 0.3.0(react@18.2.0)
       '@markdoc/next.js':
         specifier: 0.2.3
-        version: 0.2.3(@markdoc/markdoc@0.3.0)(next@13.4.7)(react@18.2.0)
+        version: 0.2.3(@markdoc/markdoc@0.3.0(react@18.2.0))(next@13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
       '@sindresorhus/slugify':
         specifier: ^2.2.1
         version: 2.2.1
@@ -49,7 +49,10 @@ importers:
         version: 1.0.17
       next:
         specifier: 13.4.7
-        version: 13.4.7(react-dom@18.2.0)(react@18.2.0)
+        version: 13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      next-intl:
+        specifier: ^4.3.12
+        version: 4.4.0(next@13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)(typescript@5.7.3)
       postcss-focus-visible:
         specifier: ^6.0.4
         version: 6.0.4(postcss@8.4.49)
@@ -70,7 +73,7 @@ importers:
         version: 18.2.0(react@18.2.0)
       reactflow:
         specifier: ^11.11.4
-        version: 11.11.4(react-dom@18.2.0)(react@18.2.0)
+        version: 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       tailwindcss:
         specifier: ^3.4.16
         version: 3.4.16
@@ -234,6 +237,24 @@ packages:
   '@floating-ui/utils@0.2.8':
     resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
 
+  '@formatjs/ecma402-abstract@2.3.4':
+    resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==}
+
+  '@formatjs/fast-memoize@2.2.7':
+    resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==}
+
+  '@formatjs/icu-messageformat-parser@2.11.2':
+    resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==}
+
+  '@formatjs/icu-skeleton-parser@1.8.14':
+    resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==}
+
+  '@formatjs/intl-localematcher@0.5.10':
+    resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==}
+
+  '@formatjs/intl-localematcher@0.6.1':
+    resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==}
+
   '@headlessui/react@2.2.0':
     resolution: {integrity: sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==}
     engines: {node: '>=10'}
@@ -452,6 +473,9 @@ packages:
   '@rushstack/eslint-patch@1.10.4':
     resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
 
+  '@schummar/icu-type-parser@1.21.5':
+    resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
+
   '@sindresorhus/slugify@2.2.1':
     resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==}
     engines: {node: '>=12'}
@@ -931,6 +955,9 @@ packages:
       supports-color:
         optional: true
 
+  decimal.js@10.6.0:
+    resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
   decompress-response@6.0.0:
     resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
     engines: {node: '>=10'}
@@ -1336,6 +1363,9 @@ packages:
     resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
     engines: {node: '>= 0.4'}
 
+  intl-messageformat@10.7.16:
+    resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==}
+
   is-array-buffer@3.0.4:
     resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
     engines: {node: '>= 0.4'}
@@ -1589,6 +1619,20 @@ packages:
   natural-compare@1.4.0:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
 
+  negotiator@1.0.0:
+    resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+    engines: {node: '>= 0.6'}
+
+  next-intl@4.4.0:
+    resolution: {integrity: sha512-QHqnP9V9Pe7Tn0PdVQ7u1Z8k9yCkW5SJKeRy2g5gxzhSt/C01y3B9qNxuj3Fsmup/yreIHe6osxU6sFa+9WIkQ==}
+    peerDependencies:
+      next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+      typescript: ^5.0.0
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
   next@13.4.7:
     resolution: {integrity: sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==}
     engines: {node: '>=16.8.0'}
@@ -2213,6 +2257,11 @@ packages:
   uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
 
+  use-intl@4.4.0:
+    resolution: {integrity: sha512-smFekJWtokDRBLC5/ZumlBREzdXOkw06+56Ifj2uRe9266Mk+yWQm2PcJO+EwlOE5sHIXHixOTzN6V8E0RGUbw==}
+    peerDependencies:
+      react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+
   use-sync-external-store@1.2.2:
     resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==}
     peerDependencies:
@@ -2420,12 +2469,13 @@ snapshots:
 
   '@docsearch/css@3.8.0': {}
 
-  '@docsearch/react@3.8.0(@algolia/client-search@5.19.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.17.3)':
+  '@docsearch/react@3.8.0(@algolia/client-search@5.19.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(search-insights@2.17.3)':
     dependencies:
       '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.17.1)(search-insights@2.17.3)
       '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.17.1)
       '@docsearch/css': 3.8.0
       algoliasearch: 5.17.1
+    optionalDependencies:
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       search-insights: 2.17.3
@@ -2455,15 +2505,15 @@ snapshots:
       '@floating-ui/core': 1.6.8
       '@floating-ui/utils': 0.2.8
 
-  '@floating-ui/react-dom@2.1.2(react-dom@18.2.0)(react@18.2.0)':
+  '@floating-ui/react-dom@2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
       '@floating-ui/dom': 1.6.12
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
 
-  '@floating-ui/react@0.26.28(react-dom@18.2.0)(react@18.2.0)':
+  '@floating-ui/react@0.26.28(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@floating-ui/react-dom': 2.1.2(react-dom@18.2.0)(react@18.2.0)
+      '@floating-ui/react-dom': 2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       '@floating-ui/utils': 0.2.8
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -2471,12 +2521,42 @@ snapshots:
 
   '@floating-ui/utils@0.2.8': {}
 
-  '@headlessui/react@2.2.0(react-dom@18.2.0)(react@18.2.0)':
+  '@formatjs/ecma402-abstract@2.3.4':
+    dependencies:
+      '@formatjs/fast-memoize': 2.2.7
+      '@formatjs/intl-localematcher': 0.6.1
+      decimal.js: 10.6.0
+      tslib: 2.8.1
+
+  '@formatjs/fast-memoize@2.2.7':
+    dependencies:
+      tslib: 2.8.1
+
+  '@formatjs/icu-messageformat-parser@2.11.2':
+    dependencies:
+      '@formatjs/ecma402-abstract': 2.3.4
+      '@formatjs/icu-skeleton-parser': 1.8.14
+      tslib: 2.8.1
+
+  '@formatjs/icu-skeleton-parser@1.8.14':
+    dependencies:
+      '@formatjs/ecma402-abstract': 2.3.4
+      tslib: 2.8.1
+
+  '@formatjs/intl-localematcher@0.5.10':
+    dependencies:
+      tslib: 2.8.1
+
+  '@formatjs/intl-localematcher@0.6.1':
+    dependencies:
+      tslib: 2.8.1
+
+  '@headlessui/react@2.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@floating-ui/react': 0.26.28(react-dom@18.2.0)(react@18.2.0)
+      '@floating-ui/react': 0.26.28(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       '@react-aria/focus': 3.19.0(react@18.2.0)
       '@react-aria/interactions': 3.22.5(react@18.2.0)
-      '@tanstack/react-virtual': 3.11.1(react-dom@18.2.0)(react@18.2.0)
+      '@tanstack/react-virtual': 3.11.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
 
@@ -2525,16 +2605,15 @@ snapshots:
       '@jridgewell/sourcemap-codec': 1.5.0
 
   '@markdoc/markdoc@0.3.0(react@18.2.0)':
-    dependencies:
-      react: 18.2.0
     optionalDependencies:
       '@types/markdown-it': 12.2.3
+      react: 18.2.0
 
-  '@markdoc/next.js@0.2.3(@markdoc/markdoc@0.3.0)(next@13.4.7)(react@18.2.0)':
+  '@markdoc/next.js@0.2.3(@markdoc/markdoc@0.3.0(react@18.2.0))(next@13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)':
     dependencies:
       '@markdoc/markdoc': 0.3.0(react@18.2.0)
       js-yaml: 4.1.0
-      next: 13.4.7(react-dom@18.2.0)(react@18.2.0)
+      next: 13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       react: 18.2.0
 
   '@next/env@13.4.7': {}
@@ -2625,9 +2704,9 @@ snapshots:
     dependencies:
       react: 18.2.0
 
-  '@reactflow/background@11.3.14(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/background@11.3.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       classcat: 5.0.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -2636,9 +2715,9 @@ snapshots:
       - '@types/react'
       - immer
 
-  '@reactflow/controls@11.2.14(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/controls@11.2.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       classcat: 5.0.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -2647,7 +2726,7 @@ snapshots:
       - '@types/react'
       - immer
 
-  '@reactflow/core@11.11.4(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/core@11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
       '@types/d3': 7.4.3
       '@types/d3-drag': 3.0.7
@@ -2664,9 +2743,9 @@ snapshots:
       - '@types/react'
       - immer
 
-  '@reactflow/minimap@11.7.14(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/minimap@11.7.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       '@types/d3-selection': 3.0.11
       '@types/d3-zoom': 3.0.8
       classcat: 5.0.5
@@ -2679,9 +2758,9 @@ snapshots:
       - '@types/react'
       - immer
 
-  '@reactflow/node-resizer@2.2.14(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/node-resizer@2.2.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       classcat: 5.0.5
       d3-drag: 3.0.0
       d3-selection: 3.0.0
@@ -2692,9 +2771,9 @@ snapshots:
       - '@types/react'
       - immer
 
-  '@reactflow/node-toolbar@1.3.14(react-dom@18.2.0)(react@18.2.0)':
+  '@reactflow/node-toolbar@1.3.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       classcat: 5.0.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -2707,6 +2786,8 @@ snapshots:
 
   '@rushstack/eslint-patch@1.10.4': {}
 
+  '@schummar/icu-type-parser@1.21.5': {}
+
   '@sindresorhus/slugify@2.2.1':
     dependencies:
       '@sindresorhus/transliterate': 1.6.0
@@ -2732,7 +2813,7 @@ snapshots:
       postcss-selector-parser: 6.0.10
       tailwindcss: 3.4.16
 
-  '@tanstack/react-virtual@3.11.1(react-dom@18.2.0)(react@18.2.0)':
+  '@tanstack/react-virtual@3.11.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
       '@tanstack/virtual-core': 3.10.9
       react: 18.2.0
@@ -2880,6 +2961,7 @@ snapshots:
       '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.7.3)
       debug: 4.4.0
       eslint: 8.26.0
+    optionalDependencies:
       typescript: 5.7.3
     transitivePeerDependencies:
       - supports-color
@@ -2900,6 +2982,7 @@ snapshots:
       is-glob: 4.0.3
       semver: 7.6.3
       tsutils: 3.21.0(typescript@5.7.3)
+    optionalDependencies:
       typescript: 5.7.3
     transitivePeerDependencies:
       - supports-color
@@ -3259,6 +3342,8 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  decimal.js@10.6.0: {}
+
   decompress-response@6.0.0:
     dependencies:
       mimic-response: 3.1.0
@@ -3419,11 +3504,12 @@ snapshots:
       '@typescript-eslint/parser': 5.62.0(eslint@8.26.0)(typescript@5.7.3)
       eslint: 8.26.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.31.0)(eslint@8.26.0)
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0)
+      eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.7.3))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0)
       eslint-plugin-jsx-a11y: 6.10.2(eslint@8.26.0)
       eslint-plugin-react: 7.37.2(eslint@8.26.0)
       eslint-plugin-react-hooks: 4.6.2(eslint@8.26.0)
+    optionalDependencies:
       typescript: 5.7.3
     transitivePeerDependencies:
       - eslint-import-resolver-webpack
@@ -3437,11 +3523,11 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0)(eslint@8.26.0):
+  eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0):
     dependencies:
       debug: 4.4.0
       eslint: 8.26.0
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.7.3))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0)
       glob: 7.2.3
       is-glob: 4.0.3
       resolve: 1.22.8
@@ -3449,20 +3535,20 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0):
     dependencies:
-      '@typescript-eslint/parser': 5.62.0(eslint@8.26.0)(typescript@5.7.3)
       debug: 3.2.7
+    optionalDependencies:
+      '@typescript-eslint/parser': 5.62.0(eslint@8.26.0)(typescript@5.7.3)
       eslint: 8.26.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.31.0)(eslint@8.26.0)
+      eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.7.3))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0):
     dependencies:
       '@rtsao/scc': 1.1.0
-      '@typescript-eslint/parser': 5.62.0(eslint@8.26.0)(typescript@5.7.3)
       array-includes: 3.1.8
       array.prototype.findlastindex: 1.2.5
       array.prototype.flat: 1.3.2
@@ -3471,7 +3557,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.26.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.31.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -3482,6 +3568,8 @@ snapshots:
       semver: 6.3.1
       string.prototype.trimend: 1.0.9
       tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 5.62.0(eslint@8.26.0)(typescript@5.7.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
@@ -3814,6 +3902,13 @@ snapshots:
       hasown: 2.0.2
       side-channel: 1.1.0
 
+  intl-messageformat@10.7.16:
+    dependencies:
+      '@formatjs/ecma402-abstract': 2.3.4
+      '@formatjs/fast-memoize': 2.2.7
+      '@formatjs/icu-messageformat-parser': 2.11.2
+      tslib: 2.8.1
+
   is-array-buffer@3.0.4:
     dependencies:
       call-bind: 1.0.8
@@ -4045,7 +4140,19 @@ snapshots:
 
   natural-compare@1.4.0: {}
 
-  next@13.4.7(react-dom@18.2.0)(react@18.2.0):
+  negotiator@1.0.0: {}
+
+  next-intl@4.4.0(next@13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)(typescript@5.7.3):
+    dependencies:
+      '@formatjs/intl-localematcher': 0.5.10
+      negotiator: 1.0.0
+      next: 13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      react: 18.2.0
+      use-intl: 4.4.0(react@18.2.0)
+    optionalDependencies:
+      typescript: 5.7.3
+
+  next@13.4.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
     dependencies:
       '@next/env': 13.4.7
       '@swc/helpers': 0.5.1
@@ -4202,8 +4309,9 @@ snapshots:
   postcss-load-config@4.0.2(postcss@8.4.49):
     dependencies:
       lilconfig: 3.1.3
-      postcss: 8.4.49
       yaml: 2.6.1
+    optionalDependencies:
+      postcss: 8.4.49
 
   postcss-nested@6.2.0(postcss@8.4.49):
     dependencies:
@@ -4299,14 +4407,14 @@ snapshots:
     dependencies:
       loose-envify: 1.4.0
 
-  reactflow@11.11.4(react-dom@18.2.0)(react@18.2.0):
+  reactflow@11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
     dependencies:
-      '@reactflow/background': 11.3.14(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/controls': 11.2.14(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/core': 11.11.4(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/minimap': 11.7.14(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/node-resizer': 2.2.14(react-dom@18.2.0)(react@18.2.0)
-      '@reactflow/node-toolbar': 1.3.14(react-dom@18.2.0)(react@18.2.0)
+      '@reactflow/background': 11.3.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@reactflow/controls': 11.2.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@reactflow/core': 11.11.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@reactflow/minimap': 11.7.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@reactflow/node-resizer': 2.2.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@reactflow/node-toolbar': 1.3.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     transitivePeerDependencies:
@@ -4742,6 +4850,13 @@ snapshots:
     dependencies:
       punycode: 2.3.1
 
+  use-intl@4.4.0(react@18.2.0):
+    dependencies:
+      '@formatjs/fast-memoize': 2.2.7
+      '@schummar/icu-type-parser': 1.21.5
+      intl-messageformat: 10.7.16
+      react: 18.2.0
+
   use-sync-external-store@1.2.2(react@18.2.0):
     dependencies:
       react: 18.2.0
@@ -4820,5 +4935,6 @@ snapshots:
 
   zustand@4.5.5(react@18.2.0):
     dependencies:
-      react: 18.2.0
       use-sync-external-store: 1.2.2(react@18.2.0)
+    optionalDependencies:
+      react: 18.2.0

+ 112 - 0
scripts/validate-translations.js

@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+
+/**
+ * Build-time validation script for navigation translations
+ *
+ * This script validates that all products have complete translations
+ * for all required locales. It helps prevent maintenance drift by
+ * ensuring that when navigation structure changes, translations are
+ * kept in sync.
+ *
+ * Usage:
+ *   node scripts/validate-translations.js
+ *   node scripts/validate-translations.js --fail-on-warning
+ */
+
+const { products } = require('../src/components/products/index.js')
+const {
+  validateProductTranslations,
+  extractNavigationKeys,
+  generateTranslationTemplate
+} = require('../src/utils/navigation-localization.js')
+
+const REQUIRED_LOCALES = ['ja', 'ko']
+const FAIL_ON_WARNING = process.argv.includes('--fail-on-warning')
+const GENERATE_TEMPLATE = process.argv.includes('--generate-template')
+
+console.log('Validating navigation translations...\n')
+
+let hasErrors = false
+let hasWarnings = false
+const allResults = []
+
+products.forEach(product => {
+  // Skip products without navigation
+  if (!product.sections || product.sections.length === 0) {
+    return
+  }
+
+  const keys = extractNavigationKeys(product)
+
+  // Skip products with no navigable content
+  if (keys.sections.length === 0 && keys.links.length === 0) {
+    return
+  }
+
+  console.log(`Checking ${product.name}...`)
+
+  // Generate template if requested
+  if (GENERATE_TEMPLATE && (!product.localizedNavigation || Object.keys(product.localizedNavigation).length === 0)) {
+    console.log(`  Template for ${product.name}:`)
+    REQUIRED_LOCALES.forEach(locale => {
+      const template = generateTranslationTemplate(product, locale)
+      console.log(`  ${locale}:`, JSON.stringify(template, null, 2))
+    })
+    console.log()
+    return
+  }
+
+  const validation = validateProductTranslations(product, REQUIRED_LOCALES)
+  allResults.push({ product: product.name, validation })
+
+  if (!validation.isValid) {
+    hasErrors = true
+    console.log(`  ❌ Validation failed`)
+    validation.errors.forEach(error => {
+      console.log(`     - ${error}`)
+    })
+  } else {
+    console.log(`  ✅ All translations complete`)
+  }
+
+  // Show detailed results for each locale
+  Object.entries(validation.results).forEach(([locale, result]) => {
+    if (!result.isValid) {
+      hasWarnings = true
+      console.log(`  Locale ${locale}:`)
+      if (result.missing.sections.length > 0) {
+        console.log(`    Missing sections: ${result.missing.sections.join(', ')}`)
+      }
+      if (result.missing.links.length > 0) {
+        console.log(`    Missing links: ${result.missing.links.join(', ')}`)
+      }
+      if (result.missing.headline) {
+        console.log(`    Missing headline`)
+      }
+      if (result.missing.description) {
+        console.log(`    Missing description`)
+      }
+    }
+  })
+
+  console.log()
+})
+
+// Summary
+console.log('Summary:')
+console.log(`  Total products validated: ${allResults.length}`)
+console.log(`  Products with errors: ${allResults.filter(r => !r.validation.isValid).length}`)
+console.log(`  Products with warnings: ${allResults.filter(r => Object.values(r.validation.results).some(v => !v.isValid)).length}`)
+
+if (hasErrors || (FAIL_ON_WARNING && hasWarnings)) {
+  console.log('\n❌ Translation validation failed!')
+  console.log('Please ensure all navigation items have translations for all required locales.')
+  process.exit(1)
+} else if (hasWarnings) {
+  console.log('\n⚠️  Some warnings found, but validation passed.')
+  console.log('Consider adding missing translations for better localization coverage.')
+  process.exit(0)
+} else {
+  console.log('\n✅ All translations validated successfully!')
+  process.exit(0)
+}

+ 4 - 0
src/components/Header.jsx

@@ -2,6 +2,7 @@ import clsx from 'clsx'
 import Link from 'next/link'
 import React, { useEffect, useState } from 'react'
 
+import { LanguageSwitcher } from '@/components/LanguageSwitcher'
 import { MobileNavigation } from '@/components/MobileNavigation'
 import { Search } from '@/components/Search'
 import { ThemeSelector } from '@/components/ThemeSelector'
@@ -75,6 +76,9 @@ export function Header({ page }) {
           <div className="-my-5">
             <Search />
           </div>
+          <div className="hidden sm:block">
+            <LanguageSwitcher />
+          </div>
           <ThemeSelector className="relative z-10" />
           <Link
             href={page.product.github}

+ 93 - 0
src/components/LanguageSwitcher.jsx

@@ -0,0 +1,93 @@
+import { useState, useRef, useEffect } from 'react'
+import { useRouter } from 'next/router'
+import Link from 'next/link'
+import { useLocale } from '@/contexts/LocaleContext'
+
+const languages = [
+  { code: 'en', name: 'English', flag: '🇺🇸' },
+  { code: 'ja', name: '日本語', flag: '🇯🇵' },
+  { code: 'ko', name: '한국어', flag: '🇰🇷' },
+]
+
+export function LanguageSwitcher() {
+  const { locale } = useLocale()
+  const { pathname } = useRouter()
+  const [isOpen, setIsOpen] = useState(false)
+  const dropdownRef = useRef(null)
+
+  // Close dropdown when clicking outside
+  useEffect(() => {
+    const handleClickOutside = (event) => {
+      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+        setIsOpen(false)
+      }
+    }
+
+    document.addEventListener('mousedown', handleClickOutside)
+    return () => document.removeEventListener('mousedown', handleClickOutside)
+  }, [])
+
+  const getLocalizedPath = (targetLocale) => {
+    if (targetLocale === 'en') {
+      // For English, remove any locale prefix and return root path
+      if (pathname.startsWith('/ja')) {
+        return pathname.replace(/^\/ja/, '') || '/'
+      }
+      if (pathname.startsWith('/ko')) {
+        return pathname.replace(/^\/ko/, '') || '/'
+      }
+      return pathname
+    } else {
+      // For other locales, add the locale prefix
+      if (pathname.startsWith('/ja') || pathname.startsWith('/ko')) {
+        // Replace existing locale prefix
+        return pathname.replace(/^\/[a-z]{2}/, `/${targetLocale}`)
+      } else {
+        // Add locale prefix to English path
+        return `/${targetLocale}${pathname === '/' ? '' : pathname}`
+      }
+    }
+  }
+
+  const currentLanguage = languages.find(lang => lang.code === locale)
+
+  return (
+    <div className="relative" ref={dropdownRef}>
+      <button
+        onClick={() => setIsOpen(!isOpen)}
+        className="flex items-center space-x-1 px-2 py-1 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300 transition-colors"
+        aria-label="Select language"
+      >
+        <span className="text-base">{currentLanguage?.flag}</span>
+        <svg
+          className={`w-3 h-3 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
+          fill="none"
+          stroke="currentColor"
+          viewBox="0 0 24 24"
+        >
+          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
+        </svg>
+      </button>
+
+      {isOpen && (
+        <div className="absolute right-0 top-full mt-1 w-32 bg-white dark:bg-slate-800 rounded-md shadow-lg border border-slate-200 dark:border-slate-700 py-1 z-50">
+          {languages.map((lang) => (
+            <Link
+              key={lang.code}
+              href={getLocalizedPath(lang.code)}
+              onClick={() => setIsOpen(false)}
+              className={`flex items-center space-x-2 px-3 py-2 text-sm hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors ${
+                locale === lang.code
+                  ? 'text-blue-600 dark:text-blue-400 font-medium'
+                  : 'text-slate-700 dark:text-slate-300'
+              }`}
+            >
+              <span>{lang.flag}</span>
+              <span>{lang.name}</span>
+            </Link>
+          ))}
+        </div>
+      )}
+    </div>
+  )
+}

+ 19 - 5
src/components/NavList.jsx

@@ -4,16 +4,30 @@ import { Popover } from '@headlessui/react'
 import Link from 'next/link'
 import { SwitcherPopover } from './products/SwitcherPopover'
 import { productCategories } from './products/index'
+import { useTranslations, useLocale } from '@/contexts/LocaleContext'
+import { getLocalizedHref } from '@/config/languages'
 
 const NavList = () => {
+  const t = useTranslations('header')
+  const { locale } = useLocale()
+
+  const getTranslatedCategory = (category) => {
+    const categoryMap = {
+      'MPL': t('mpl', 'MPL'),
+      'Dev Tools': t('devTools', 'Dev Tools'),
+      'Aura': t('aura', 'Aura')
+    }
+    return categoryMap[category] || category
+  }
+
   return (
     <div className="hidden cursor-pointer  gap-8 lg:flex">
       {productCategories.map((item, index) => {
         if (item === 'Aura') {
           return (
-            <Link href="/aura" key={index}>
+            <Link href={getLocalizedHref("/aura", locale)} key={index}>
               <div className="-mx-4 -my-2 rounded-lg px-4 py-2 text-black dark:text-white">
-                Aura
+                {getTranslatedCategory(item)}
               </div>
             </Link>
           )
@@ -23,16 +37,16 @@ const NavList = () => {
           <div className="hidden flex-col lg:flex" key={index}>
             <SwitcherPopover menuItem={productCategories[index]}>
               <Popover.Button className="-mx-4 -my-2 rounded-lg px-4 py-2 text-black dark:text-white">
-                {item}
+                {getTranslatedCategory(item)}
               </Popover.Button>
             </SwitcherPopover>
           </div>
         )
       })}
       <div className="hidden flex-col lg:flex">
-        <Link href="/guides">
+        <Link href={getLocalizedHref("/guides", locale)}>
           <div className="-mx-4 -my-2 rounded-lg px-4 py-2 text-black dark:text-white">
-            Guides
+            {t('guides', 'Guides')}
           </div>
         </Link>
       </div>

+ 121 - 0
src/components/SEOHead.jsx

@@ -0,0 +1,121 @@
+import Head from 'next/head'
+import { useRouter } from 'next/router'
+import { LANGUAGES, generateAlternateUrls } from '@/config/languages'
+
+/**
+ * SEOHead Component
+ *
+ * Handles all SEO-related meta tags including:
+ * - Title and description
+ * - Open Graph tags
+ * - Twitter Card tags
+ * - Canonical URLs
+ * - hreflang tags for internationalization
+ *
+ * Usage:
+ * <SEOHead
+ *   title="Page Title"
+ *   description="Page description"
+ *   metaTitle="Custom Meta Title (optional)"
+ *   locale="en"
+ * />
+ */
+
+const SITE_URL = 'https://developers.metaplex.com'
+const DEFAULT_OG_IMAGE = `${SITE_URL}/assets/social/dev-hub-preview.jpg`
+
+export function SEOHead({ title, description, metaTitle, locale = 'en' }) {
+  const router = useRouter()
+  const { pathname } = router
+
+  // Use metaTitle if provided, otherwise use title
+  const finalMetaTitle = metaTitle || title
+
+  // Get current language config
+  const currentLang = LANGUAGES[locale] || LANGUAGES.en
+
+  // Normalize pathname (remove any existing language prefix to avoid duplication)
+  let normalizedPath = pathname
+  for (const lang of Object.values(LANGUAGES)) {
+    if (lang.urlPath && pathname.startsWith(lang.urlPath)) {
+      normalizedPath = pathname.slice(lang.urlPath.length) || '/'
+      break
+    }
+  }
+
+  // Generate canonical URL (always points to the current language version)
+  const canonicalUrl = `${SITE_URL}${currentLang.urlPath}${normalizedPath === '/' ? '' : normalizedPath}`
+
+  // Generate alternate URLs for hreflang
+  const alternateUrls = generateAlternateUrls(pathname)
+
+  return (
+    <Head>
+      {/* Primary Meta Tags */}
+      <title>{finalMetaTitle}</title>
+      {description && <meta name="description" content={description} />}
+
+      {/* Canonical URL - points to current language version */}
+      <link rel="canonical" href={canonicalUrl} />
+
+      {/* hreflang tags for language alternates */}
+      {alternateUrls.map(({ hreflang, url }) => (
+        <link
+          key={hreflang}
+          rel="alternate"
+          hrefLang={hreflang}
+          href={`${SITE_URL}${url}`}
+        />
+      ))}
+
+      {/* x-default hreflang (points to English version) */}
+      <link
+        rel="alternate"
+        hrefLang="x-default"
+        href={`${SITE_URL}${alternateUrls.find(alt => alt.hreflang === 'en')?.url || '/'}`}
+      />
+
+      {/* Open Graph */}
+      <meta property="og:title" content={finalMetaTitle} />
+      {description && <meta property="og:description" content={description} />}
+      <meta property="og:url" content={canonicalUrl} />
+      <meta property="og:type" content="website" />
+      <meta property="og:image" content={DEFAULT_OG_IMAGE} />
+      <meta property="og:locale" content={getOGLocale(locale)} />
+
+      {/* Add og:locale:alternate for other languages */}
+      {Object.keys(LANGUAGES)
+        .filter(lang => lang !== locale)
+        .map(lang => (
+          <meta
+            key={`og-locale-${lang}`}
+            property="og:locale:alternate"
+            content={getOGLocale(lang)}
+          />
+        ))}
+
+      {/* Twitter Card */}
+      <meta name="twitter:card" content="summary_large_image" />
+      <meta name="twitter:title" content={finalMetaTitle} />
+      {description && <meta name="twitter:description" content={description} />}
+      <meta name="twitter:image" content={DEFAULT_OG_IMAGE} />
+      <meta property="twitter:domain" content="developers.metaplex.com" />
+    </Head>
+  )
+}
+
+/**
+ * Convert locale code to Open Graph locale format
+ * en -> en_US
+ * ja -> ja_JP
+ * ko -> ko_KR
+ */
+function getOGLocale(localeCode) {
+  const ogLocaleMap = {
+    en: 'en_US',
+    ja: 'ja_JP',
+    ko: 'ko_KR',
+  }
+
+  return ogLocaleMap[localeCode] || 'en_US'
+}

+ 4 - 3
src/components/TableOfContent.jsx

@@ -10,7 +10,7 @@ function useTableOfContents(tableOfContents) {
       .flatMap((node) => [node.id, ...node.children.map((child) => child.id)])
       .map((id) => {
         let el = document.getElementById(id)
-        if (!el) return
+        if (!el) return null
 
         let style = window.getComputedStyle(el)
         let scrollMt = parseFloat(style.scrollMarginTop)
@@ -18,6 +18,7 @@ function useTableOfContents(tableOfContents) {
         let top = window.scrollY + el.getBoundingClientRect().top - scrollMt
         return { id, top }
       })
+      .filter(Boolean) // Remove null/undefined entries
   }, [])
 
   useEffect(() => {
@@ -25,9 +26,9 @@ function useTableOfContents(tableOfContents) {
     let headings = getHeadings(tableOfContents)
     function onScroll() {
       let top = window.scrollY
-      let current = headings[0].id
+      let current = headings[0]?.id
       for (let heading of headings) {
-        if (top >= heading.top) {
+        if (heading && top >= heading.top) {
           current = heading.id
         } else {
           break

+ 225 - 43
src/components/helperComponents/guideIndex.jsx

@@ -1,4 +1,5 @@
 import { useState } from 'react'
+import { useLocale } from '@/contexts/LocaleContext'
 
 const GuideTags = {
   js: 'javascript',
@@ -12,15 +13,27 @@ const GuideTags = {
 }
 
 const bubblegumV1Guides = {
-  name: 'Bubblegum V1',
+  name: {
+    en: 'Bubblegum V1',
+    jp: 'Bubblegum V1',
+    kr: 'Bubblegum V1'
+  },
   guides: [
     {
-      name: 'How to create 1000000 NFTs on Solana',
+      name: {
+        en: 'How to create 1000000 NFTs on Solana',
+        jp: 'Solanaで100万NFTを作成する方法',
+        kr: 'Solana에서 100만 NFT 만들기'
+      },
       path: '/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana',
       tags: [GuideTags.js, GuideTags.nfts],
     },
     {
-      name: 'How to interact with CNFTs on other SVMS',
+      name: {
+        en: 'How to interact with CNFTs on other SVMS',
+        jp: '他のSVMでcNFTと相互作用する方法',
+        kr: '다른 SVM에서 cNFT와 상호작용하는 방법'
+      },
       path: '/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms',
       tags: [GuideTags.js, GuideTags.nfts],
     },
@@ -28,20 +41,36 @@ const bubblegumV1Guides = {
 }
 
 const bubblegumV2Guides = {
-  name: 'Bubblegum V2',
+  name: {
+    en: 'Bubblegum V2',
+    jp: 'Bubblegum V2',
+    kr: 'Bubblegum V2'
+  },
   guides: [],
 }
 
 const candyMachineGuides = {
-  name: 'Candy Machine',
+  name: {
+    en: 'Candy Machine',
+    jp: 'キャンディマシン',
+    kr: '캔디 머신'
+  },
   guides: [
     {
-      name: 'Airdrop Mint to Another Wallet',
+      name: {
+        en: 'Airdrop Mint to Another Wallet',
+        jp: '他のウォレットへのエアドロップミント',
+        kr: '다른 지갑으로 에어드롭 민팅'
+      },
       path: '/candy-machine/guides/airdrop-mint-to-another-wallet',
       tags: [GuideTags.airdrop, GuideTags.nfts],
     },
     {
-      name: 'Create an NFT Collection on Solana with Candy Machine',
+      name: {
+        en: 'Create an NFT Collection on Solana with Candy Machine',
+        jp: 'キャンディマシンでSolanaにNFTコレクションを作成',
+        kr: '캔디 머신으로 Solana에서 NFT 컬렉션 만들기'
+      },
       path: '/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine',
       tags: [GuideTags.nfts],
     },
@@ -49,65 +78,117 @@ const candyMachineGuides = {
 }
 
 const coreGuides = {
-  name: 'Core',
+  name: {
+    en: 'Core',
+    jp: 'Core',
+    kr: 'Core'
+  },
   guides: [
     {
-      name: 'Immutable NFTs',
+      name: {
+        en: 'Immutable NFTs',
+        jp: '不変NFT',
+        kr: '불변 NFT'
+      },
       path: '/core/guides/immutability',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Create Soulbound NFT Asset',
+      name: {
+        en: 'Create Soulbound NFT Asset',
+        jp: 'ソウルバウンドNFTアセットの作成',
+        kr: '소울바운드 NFT 자산 생성'
+      },
       path: '/core/guides/create-soulbound-nft-asset',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Print Editions',
+      name: {
+        en: 'Print Editions',
+        jp: '印刷エディション',
+        kr: '인쇄 에디션'
+      },
       path: '/core/guides/print-editions',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Oracle Plugin Example',
+      name: {
+        en: 'Oracle Plugin Example',
+        jp: 'Oracleプラグインの例',
+        kr: 'Oracle 플러그인 예제'
+      },
       path: '/core/guides/oracle-plugin-example',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Onchain Ticketing with AppData',
+      name: {
+        en: 'Onchain Ticketing with AppData',
+        jp: 'AppDataを使用したオンチェーンチケット',
+        kr: 'AppData를 사용한 온체인 티켓팅'
+      },
       path: '/core/guides/onchain-ticketing-with-appdata',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'How to create a Core NFT Asset with JavaScript',
+      name: {
+        en: 'How to create a Core NFT Asset with JavaScript',
+        jp: 'JavaScriptでCore NFTアセットを作成する方法',
+        kr: 'JavaScript로 Core NFT 자산 생성하는 방법'
+      },
       path: '/core/guides/javascript/how-to-create-a-core-nft-asset-with-javascript',
       tags: [GuideTags.nfts, GuideTags.js],
     },
     {
-      name: 'How to create a Core Collection with Javascript',
+      name: {
+        en: 'How to create a Core Collection with Javascript',
+        jp: 'JavaScriptでCoreコレクションを作成する方法',
+        kr: 'JavaScript로 Core 컬렉션 생성하는 방법'
+      },
       path: '/core/guides/javascript/how-to-create-a-core-collection-with-javascript',
       tags: [GuideTags.nfts, GuideTags.js],
     },
     {
-      name: 'Web2 Typescript Staking Example',
+      name: {
+        en: 'Web2 Typescript Staking Example',
+        jp: 'Web2 TypeScriptステーキングの例',
+        kr: 'Web2 TypeScript 스테이킹 예제'
+      },
       path: '/core/guides/web2-typescript-staking-example',
       tags: [GuideTags.nfts, GuideTags.js],
     },
     {
-      name: 'Loyalty Card Concept Guide',
+      name: {
+        en: 'Loyalty Card Concept Guide',
+        jp: 'ロイヤリティカードコンセプトガイド',
+        kr: '로얄티 카드 컨셉트 가이드'
+      },
       path: '/core/guides/loyalty-card-concept-guide',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'How to create a Core NFT Asset with Anchor',
+      name: {
+        en: 'How to create a Core NFT Asset with Anchor',
+        jp: 'AnchorでCore NFTアセットを作成する方法',
+        kr: 'Anchor로 Core NFT 자산 생성하는 방법'
+      },
       path: '/core/guides/anchor/how-to-create-a-core-nft-asset-with-anchor',
       tags: [GuideTags.nfts, GuideTags.anchor, GuideTags.rust],
     },
     {
-      name: 'How to create a Core Collection with Anchor',
+      name: {
+        en: 'How to create a Core Collection with Anchor',
+        jp: 'AnchorでCoreコレクションを作成する方法',
+        kr: 'Anchor로 Core 컬렉션 생성하는 방법'
+      },
       path: '/core/guides/anchor/how-to-create-a-core-collection-with-anchor',
       tags: [GuideTags.nfts, GuideTags.anchor, GuideTags.rust],
     },
     {
-      name: 'Anchor Staking Example',
+      name: {
+        en: 'Anchor Staking Example',
+        jp: 'Anchorステーキングの例',
+        kr: 'Anchor 스테이킹 예제'
+      },
       path: '/core/guides/anchor/anchor-staking-example',
       tags: [GuideTags.nfts, GuideTags.anchor, GuideTags.rust],
     },
@@ -115,52 +196,123 @@ const coreGuides = {
 }
 
 const coreCandyMachineGuides = {
-  name: 'Core Candy Machine',
+  name: {
+    en: 'Core Candy Machine',
+    jp: 'Coreキャンディマシン',
+    kr: 'Core 캔디 머신'
+  },
   guides: [
     {
-      name: 'Create a Core Candy Machine UI',
+      name: {
+        en: 'Create a Core Candy Machine UI',
+        jp: 'CoreキャンディマシンUIの作成',
+        kr: 'Core 캔디 머신 UI 만들기'
+      },
       path: '/core-candy-machine/guides/create-a-core-candy-machine-ui',
       tags: [GuideTags.nfts, GuideTags.js],
     },
     {
-      name: 'Create a Core Candy Machine with Hidden Settings',
+      name: {
+        en: 'Create a Core Candy Machine with Hidden Settings',
+        jp: '非表示設定でCoreキャンディマシンを作成',
+        kr: '숨겨진 설정으로 Core 캔디 머신 만들기'
+      },
       path: '/core-candy-machine/guides/create-a-core-candy-machine-with-hidden-settings',
       tags: [GuideTags.nfts],
     },
   ],
 }
 
-const fusionGuides = { name: 'Fusion', guides: [] }
-const hydraGuides = { name: 'Hydra', guides: [] }
-const inscriptionGuides = { name: 'Inscription', guides: [] }
-const mpl404Guides = { name: 'MPL404', guides: [] }
-const tokenAuthGuides = { name: 'Token Auth', guides: [] }
+const fusionGuides = { 
+  name: {
+    en: 'Fusion',
+    jp: 'Fusion',
+    kr: 'Fusion'
+  }, 
+  guides: [] 
+}
+const hydraGuides = { 
+  name: {
+    en: 'Hydra',
+    jp: 'Hydra',
+    kr: 'Hydra'
+  }, 
+  guides: [] 
+}
+const inscriptionGuides = { 
+  name: {
+    en: 'Inscription',
+    jp: 'Inscription',
+    kr: 'Inscription'
+  }, 
+  guides: [] 
+}
+const mpl404Guides = { 
+  name: {
+    en: 'MPL404',
+    jp: 'MPL404',
+    kr: 'MPL404'
+  }, 
+  guides: [] 
+}
+const tokenAuthGuides = { 
+  name: {
+    en: 'Token Auth',
+    jp: 'トークン認証',
+    kr: '토큰 인증'
+  }, 
+  guides: [] 
+}
 
 const tokenMetadataGuides = {
-  name: 'Token Metadata',
+  name: {
+    en: 'Token Metadata',
+    jp: 'トークンメタデータ',
+    kr: '토큰 메타데이터'
+  },
   guides: [
     {
-      name: 'Get NFTs By Collection',
+      name: {
+        en: 'Get NFTs By Collection',
+        jp: 'コレクションでNFTを取得',
+        kr: '컬렉션별 NFT 가져오기'
+      },
       path: '/token-metadata/guides/get-by-collection',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Account Size Reduction',
+      name: {
+        en: 'Account Size Reduction',
+        jp: 'アカウントサイズ削減',
+        kr: '계정 크기 축소'
+      },
       path: '/token-metadata/guides/account-size-reduction',
       tags: [GuideTags.nfts],
     },
     {
-      name: 'Spl Token Claim Airdrop Using Gumdrop',
+      name: {
+        en: 'Spl Token Claim Airdrop Using Gumdrop',
+        jp: 'Gumdropを使用したSPLトークンクレームエアドロップ',
+        kr: 'Gumdrop을 사용한 SPL 토큰 클레임 에어드롭'
+      },
       path: '/guides/general/spl-token-claim-airdrop-using-gumdrop',
       tags: [GuideTags.nfts, GuideTags.airdrop, GuideTags.tokens],
     },
     {
-      name: 'Token Claimer Smart Contract',
+      name: {
+        en: 'Token Claimer Smart Contract',
+        jp: 'トークンクレーマースマートコントラクト',
+        kr: '토큰 클레이머 스마트 컨트렉트'
+      },
       path: '/token-metadata/guides/anchor/token-claimer-smart-contract',
       tags: [GuideTags.tokens, GuideTags.anchor, GuideTags.rust],
     },
     {
-      name: 'Create an NFT',
+      name: {
+        en: 'Create an NFT',
+        jp: 'NFTの作成',
+        kr: 'NFT 생성'
+      },
       path: '/token-metadata/guides/javascript/create-an-nft',
       tags: [GuideTags.tokens, GuideTags.js],
     },
@@ -168,22 +320,41 @@ const tokenMetadataGuides = {
 }
 
 const umiGuides = {
-  name: 'Umi',
+  name: {
+    en: 'Umi',
+    jp: 'Umi',
+    kr: 'Umi'
+  },
   guides: [
     {
-      name: 'Optimal Transactions with Compute Units and Priority Fees',
+      name: {
+        en: 'Optimal Transactions with Compute Units and Priority Fees',
+        jp: 'コンピュートユニットとプライオリティ手数料を使用した最適なトランザクション',
+        kr: '컴퓨트 유닛과 우선순위 수수료를 사용한 최적 트랜잭션'
+      },
       path: '/umi/guides/optimal-transactions-with-compute-units-and-priority-fees',
       tags: [GuideTags.js],
     },
     {
-      name: 'Serializing and Deserializing Transactions',
+      name: {
+        en: 'Serializing and Deserializing Transactions',
+        jp: 'トランザクションのシリアライズとデシリアライズ',
+        kr: '트랜잭션 직렬화 및 역직렬화'
+      },
       path: '/umi/guides/serializing-and-deserializing-transactions',
       tags: [GuideTags.js],
     },
   ],
 }
 
-const generalGuides = { name: 'General', guides: [] }
+const generalGuides = { 
+  name: {
+    en: 'General',
+    jp: '一般',
+    kr: '일반'
+  }, 
+  guides: [] 
+}
 
 const guideGroups = [
   bubblegumV1Guides,
@@ -203,6 +374,12 @@ const guideGroups = [
 
 const GuideIndexComponent = () => {
   const [selectedTag, setSelectedTag] = useState()
+  const { locale } = useLocale()
+
+  const getLocalizedText = (textObj, fallback = '') => {
+    if (typeof textObj === 'string') return textObj
+    return textObj?.[locale] || textObj?.en || fallback
+  }
 
   const TagPicker = () => (
     <div>
@@ -240,9 +417,14 @@ const GuideIndexComponent = () => {
     </div>
   )
 
+  const getLocalizedPath = (path) => {
+    if (locale === 'en') return path
+    return `/${locale}${path}`
+  }
+
   return (
     <div>
-      <h1>Program Guides Index</h1>
+      <h1>{locale === 'jp' ? 'プログラムガイドインデックス' : locale === 'kr' ? '프로그램 가이드 인덱스' : 'Program Guides Index'}</h1>
       <TagPicker />
       <ul>
         {guideGroups.map((guideGroup) => {
@@ -254,12 +436,12 @@ const GuideIndexComponent = () => {
           })
           if (filteredGuides.length > 0) {
             return (
-              <li key={guideGroup.name}>
-                {guideGroup.name}
+              <li key={getLocalizedText(guideGroup.name)}>
+                {getLocalizedText(guideGroup.name)}
                 <ul>
                   {filteredGuides.map((guide) => (
-                    <li key={guide.name}>
-                      <a href={guide.path}>{guide.name}</a>
+                    <li key={getLocalizedText(guide.name)}>
+                      <a href={getLocalizedPath(guide.path)}>{getLocalizedText(guide.name)}</a>
                     </li>
                   ))}
                 </ul>

+ 37 - 11
src/components/products/Grid.jsx

@@ -1,6 +1,8 @@
 import Link from 'next/link';
 import { IconWithName } from './IconWithName';
 import { products as allProducts } from './index';
+import { useLocale } from '@/contexts/LocaleContext';
+import { getLocalizedHref } from '@/config/languages';
 
 export function Grid({
   onClick,
@@ -9,25 +11,49 @@ export function Grid({
   numCols,
   ...props
 }) {
+  const { locale } = useLocale()
   const products = allProducts.filter(
     (product) => menuItem === product.navigationMenuCatergory
   )
 
+  // Localize product headlines and descriptions
+  const localizeProduct = (product) => {
+    if (locale === 'en' || !product.localizedNavigation || !product.localizedNavigation[locale]) {
+      return product
+    }
+
+    const localizedProduct = { ...product }
+    const productNav = product.localizedNavigation[locale]
+
+    if (productNav.headline) {
+      localizedProduct.headline = productNav.headline
+    }
+    if (productNav.description) {
+      localizedProduct.description = productNav.description
+    }
+
+    return localizedProduct
+  }
+
   let className = `relative grid sm:grid-cols-2 grid-cols-1`
 
   return (
     <ul className={className} {...props}>
-      {products.map((product) => (
-        <li key={product.path}>
-          <Link
-            href={`/${product.path}`}
-            className="block content-start rounded-lg p-3 hover:bg-slate-50 hover:dark:bg-slate-700"
-            onClick={onClick}
-          >
-            {IconWithName({ product, description: true })}
-          </Link>
-        </li>
-      ))}
+      {products.map((product) => {
+        const localizedProduct = localizeProduct(product)
+        return (
+          <li key={product.path || product.href}>
+            <Link
+              href={getLocalizedHref(product.href || product.path, locale)}
+              className="block content-start rounded-lg p-3 hover:bg-slate-50 hover:dark:bg-slate-700"
+              onClick={onClick}
+              {...(product.target && { target: product.target })}
+            >
+              {IconWithName({ product: localizedProduct, description: true })}
+            </Link>
+          </li>
+        )
+      })}
     </ul>
   )
 }

+ 42 - 26
src/components/products/Sections.jsx

@@ -5,32 +5,48 @@ import { Icon } from '@/components/icons'
 export function Sections({ sections, activeSectionId, className, props }) {
   return (
     <div className={clsx(className, 'flex')} {...props}>
-      {sections.map((section) => (
-        <Link
-          key={section.id}
-          href={section.href}
-          className={clsx(
-            'flex items-center gap-2 rounded-lg px-2 py-1',
-            'hover:bg-slate-50 hover:ring-1 hover:ring-inset hover:ring-slate-800/5',
-            'dark:hover:bg-slate-800/75 dark:hover:ring-1 dark:hover:ring-inset dark:hover:ring-white/5',
-            section.id === activeSectionId
-              ? 'font-medium text-slate-800 dark:text-white'
-              : 'text-slate-500 dark:text-slate-400'
-          )}
-          target={section.target}
-        >
-          <Icon
-            icon={section.icon}
-            className={clsx(
-              'h-5 w-5',
-              section.id === activeSectionId
-                ? 'text-slate-500 dark:text-slate-200'
-                : 'text-slate-400'
-            )}
-          />
-          <span className="leading-tight">{section.title}</span>
-        </Link>
-      ))}
+      {sections.map((section) => {
+        const isExternal = typeof section.href === 'string' && section.href.startsWith('http')
+        const content = (
+          <>
+            <Icon
+              icon={section.icon}
+              className={clsx(
+                'h-5 w-5',
+                section.id === activeSectionId
+                  ? 'text-slate-500 dark:text-slate-200'
+                  : 'text-slate-400'
+              )}
+            />
+            <span className="leading-tight">{section.title}</span>
+          </>
+        )
+
+        const classNames = clsx(
+          'flex items-center gap-2 rounded-lg px-2 py-1',
+          'hover:bg-slate-50 hover:ring-1 hover:ring-inset hover:ring-slate-800/5',
+          'dark:hover:bg-slate-800/75 dark:hover:ring-1 dark:hover:ring-inset dark:hover:ring-white/5',
+          section.id === activeSectionId
+            ? 'font-medium text-slate-800 dark:text-white'
+            : 'text-slate-500 dark:text-slate-400'
+        )
+
+        return isExternal ? (
+          <a
+            key={section.id}
+            href={section.href}
+            className={classNames}
+            target={section.target || '_blank'}
+            rel="noopener noreferrer"
+          >
+            {content}
+          </a>
+        ) : (
+          <Link key={section.id} href={section.href} className={classNames} target={section.target}>
+            {content}
+          </Link>
+        )
+      })}
     </div>
   )
 }

+ 44 - 0
src/components/products/amman/index.js

@@ -32,4 +32,48 @@ export const amman = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Local Validator Toolkit',
+      description: 'A local validator toolkit for testing Solana programs and applications.',
+      sections: {
+        'Introduction': 'Introduction'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'CLI Commands': 'CLI Commands',
+        'Configuration': 'Configuration',
+        'Pre-made Configs': 'Pre-made Configs'
+      }
+    },
+    ja: {
+      headline: 'ローカルバリデーターツールキット',
+      description: 'Solanaプログラムとアプリケーションをテストするためのローカルバリデーターツールキット。',
+      sections: {
+        'Introduction': '紹介'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'CLI Commands': 'CLIコマンド',
+        'Configuration': '設定',
+        'Pre-made Configs': '事前作成済み設定'
+      }
+    },
+    ko: {
+      headline: '로컬 밸리데이터 툴킷',
+      description: 'Solana 프로그램과 애플리케이션을 테스트하기 위한 로컬 밸리데이터 툴킷입니다.',
+      sections: {
+        'Introduction': '소개'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'CLI Commands': 'CLI 명령어',
+        'Configuration': '설정',
+        'Pre-made Configs': '미리 만들어진 설정'
+      }
+    }
+  }
 }

+ 41 - 0
src/components/products/aura/index.js

@@ -45,4 +45,45 @@ export const aura = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Indexing and Data Availability Network',
+      description: 'A data network that extends Solana and the Solana Virtual Machine (SVM)',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features'
+      },
+      links: {
+        'Overview': 'Overview',
+        'FAQ': 'FAQ',
+        'Reading Solana and SVM Data': 'Reading Solana and SVM Data'
+      }
+    },
+    ja: {
+      headline: 'インデックス作成・データ可用性ネットワーク',
+      description: 'SolanaとSolana Virtual Machine(SVM)を拡張するデータネットワーク',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能'
+      },
+      links: {
+        'Overview': '概要',
+        'FAQ': 'よくある質問',
+        'Reading Solana and SVM Data': 'SolanaとSVMデータの読み取り'
+      }
+    },
+    ko: {
+      headline: '인덱싱 및 데이터 가용성 네트워크',
+      description: 'Solana와 Solana Virtual Machine(SVM)을 확장하는 데이터 네트워크',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능'
+      },
+      links: {
+        'Overview': '개요',
+        'FAQ': '자주 묻는 질문',
+        'Reading Solana and SVM Data': 'Solana 및 SVM 데이터 읽기'
+      }
+    }
+  }
 }

+ 92 - 0
src/components/products/bubblegum-v2/index.js

@@ -5,6 +5,7 @@ import {
 } from '@/shared/sections';
 import { FolderIcon } from '@heroicons/react/24/solid';
 import { Hero } from './Hero';
+import { buildProductTranslations } from '@/config/navigation-translations';
 
 export const bubblegumv2 = {
   name: 'Bubblegum v2',
@@ -90,4 +91,95 @@ export const bubblegumv2 = {
       target: '_blank'
     },
   ],
+  // Hybrid approach: Keep product display (headline/desc) in product file for easy editing
+  // Use centralized keys for common navigation terms, inline for product-specific terms
+  localizedNavigation: buildProductTranslations({
+    // Product display translations - edit right here!
+    headlineTranslations: {
+      ja: '改良された圧縮NFT',
+      ko: '개선된 압축 NFT'
+    },
+    descriptionTranslations: {
+      ja: '新たな桁のスケールを実現するNFT。',
+      ko: '새로운 차원의 확장성을 제공하는 NFT입니다.'
+    },
+
+    sectionKeys: {
+      'Introduction': 'sections.introduction',  // Common - centralized
+      'SDK': 'sections.sdk',                     // Common - centralized
+      'Features': 'sections.features',           // Common - centralized
+      'Advanced': 'sections.advanced'            // Common - centralized
+    },
+    linkKeys: {
+      // Common terms - use centralized translations
+      'Overview': 'links.overview',
+      'FAQ': 'links.faq',
+      'Javascript': 'links.javascript',
+      'Rust': 'links.rust',
+      'Collections': 'links.collections',
+
+      // Bubblegum-specific terms - inline for easy editing
+      // To change these, edit right here in this file!
+      'Metaplex DAS API RPCs': {
+        ja: 'Metaplex DAS API RPC',
+        ko: 'Metaplex DAS API RPC'
+      },
+      'Creating Bubblegum Trees': {
+        ja: 'Bubblegumツリーの作成',
+        ko: 'Bubblegum 트리 생성'
+      },
+      'Minting Compressed NFTs (cNFTs)': {
+        ja: '圧縮NFT(cNFT)のミント',
+        ko: '압축 NFT(cNFT) 민팅'
+      },
+      'Fetching cNFTs': {
+        ja: 'cNFTの取得',
+        ko: 'cNFT 가져오기'
+      },
+      'Transferring cNFTs': {
+        ja: 'cNFTの転送',
+        ko: 'cNFT 전송'
+      },
+      'Freeze and Thaw cNFTs': {
+        ja: 'cNFTの凍結と解凍',
+        ko: 'cNFT 동결 및 해제'
+      },
+      'Updating cNFTs': {
+        ja: 'cNFTの更新',
+        ko: 'cNFT 업데이트'
+      },
+      'Burning cNFTs': {
+        ja: 'cNFTのバーン',
+        ko: 'cNFT 소각'
+      },
+      'Delegating cNFTs': {
+        ja: 'cNFTのデリゲート',
+        ko: 'cNFT 위임'
+      },
+      'Delegating Trees': {
+        ja: 'ツリーのデリゲート',
+        ko: '트리 위임'
+      },
+      'Verifying Creators': {
+        ja: '作成者の検証',
+        ko: '크리에이터 검증'
+      },
+      'Concurrent Merkle Trees': {
+        ja: '同時マークルツリー',
+        ko: '동시 머클 트리'
+      },
+      'Storing and Indexing NFT Data': {
+        ja: 'NFTデータの保存とインデックス化',
+        ko: 'NFT 데이터 저장 및 인덱싱'
+      },
+      'Hashing NFT Data': {
+        ja: 'NFTデータのハッシュ化',
+        ko: 'NFT 데이터 해싱'
+      },
+      'Merkle Tree Canopy': {
+        ja: 'マークルツリーキャノピー',
+        ko: '머클 트리 캐노피'
+      }
+    }
+  })
 }

+ 110 - 0
src/components/products/bubblegum/index.js

@@ -112,4 +112,114 @@ export const bubblegum = {
       target: '_blank'
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Compressed NFTs',
+      description: 'NFTs that scale.',
+      sections: {
+        'Introduction': 'Introduction',
+        'SDK': 'SDK',
+        'General Features': 'General Features',
+        'Bubblegum': 'Bubblegum',
+        'Advanced': 'Advanced',
+        'Javascript': 'Javascript'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Metaplex DAS API RPCs': 'Metaplex DAS API RPCs',
+        'FAQ': 'FAQ',
+        'Javascript': 'Javascript',
+        'Rust': 'Rust',
+        'Creating Bubblegum Trees': 'Creating Bubblegum Trees',
+        'Fetching cNFTs': 'Fetching cNFTs',
+        'Delegating Trees': 'Delegating Trees',
+        'Minting Compressed NFTs (cNFTs)': 'Minting Compressed NFTs (cNFTs)',
+        'Transferring cNFTs': 'Transferring cNFTs',
+        'Updating cNFTs': 'Updating cNFTs',
+        'Burning cNFTs': 'Burning cNFTs',
+        'Decompressing cNFTs': 'Decompressing cNFTs',
+        'Delegating cNFTs': 'Delegating cNFTs',
+        'Verifying Collections': 'Verifying Collections',
+        'Verifying Creators': 'Verifying Creators',
+        'Concurrent Merkle Trees': 'Concurrent Merkle Trees',
+        'Storing and Indexing NFT Data': 'Storing and Indexing NFT Data',
+        'Hashing NFT Data': 'Hashing NFT Data',
+        'Merkle Tree Canopy': 'Merkle Tree Canopy',
+        'How to Create a 1,000,000 NFT Collection on Solana': 'How to Create a 1,000,000 NFT Collection on Solana',
+        'How to Interact with cNFTs on Other SVMs': 'How to Interact with cNFTs on Other SVMs'
+      }
+    },
+    ja: {
+      headline: '圧縮NFT',
+      description: 'スケールするNFT。',
+      sections: {
+        'Introduction': '紹介',
+        'SDK': 'SDK',
+        'General Features': '一般機能',
+        'Bubblegum': 'Bubblegum',
+        'Advanced': '高度',
+        'Javascript': 'JavaScript'
+      },
+      links: {
+        'Overview': '概要',
+        'Metaplex DAS API RPCs': 'Metaplex DAS API RPC',
+        'FAQ': 'よくある質問',
+        'Javascript': 'JavaScript',
+        'Rust': 'Rust',
+        'Creating Bubblegum Trees': 'Bubblegumツリーの作成',
+        'Fetching cNFTs': 'cNFTの取得',
+        'Delegating Trees': 'ツリーのデリゲート',
+        'Minting Compressed NFTs (cNFTs)': '圧縮NFT(cNFT)のミント',
+        'Transferring cNFTs': 'cNFTの転送',
+        'Updating cNFTs': 'cNFTの更新',
+        'Burning cNFTs': 'cNFTのバーン',
+        'Decompressing cNFTs': 'cNFTの解凍',
+        'Delegating cNFTs': 'cNFTのデリゲート',
+        'Verifying Collections': 'コレクションの検証',
+        'Verifying Creators': '作成者の検証',
+        'Concurrent Merkle Trees': '同時マークルツリー',
+        'Storing and Indexing NFT Data': 'NFTデータの保存とインデックス化',
+        'Hashing NFT Data': 'NFTデータのハッシュ化',
+        'Merkle Tree Canopy': 'マークルツリーキャノピー',
+        'How to Create a 1,000,000 NFT Collection on Solana': 'Solanaで100万NFTコレクションを作成する方法',
+        'How to Interact with cNFTs on Other SVMs': '他のSVMでcNFTと相互作用する方法'
+      }
+    },
+    ko: {
+      headline: '압축 NFT',
+      description: '확장 가능한 NFT입니다.',
+      sections: {
+        'Introduction': '소개',
+        'SDK': 'SDK',
+        'General Features': '일반 기능',
+        'Bubblegum': 'Bubblegum',
+        'Advanced': '고급',
+        'Javascript': 'JavaScript'
+      },
+      links: {
+        'Overview': '개요',
+        'Metaplex DAS API RPCs': 'Metaplex DAS API RPC',
+        'FAQ': '자주 묻는 질문',
+        'Javascript': 'JavaScript',
+        'Rust': 'Rust',
+        'Creating Bubblegum Trees': 'Bubblegum 트리 생성',
+        'Fetching cNFTs': 'cNFT 가져오기',
+        'Delegating Trees': '트리 위임',
+        'Minting Compressed NFTs (cNFTs)': '압축 NFT(cNFT) 민팅',
+        'Transferring cNFTs': 'cNFT 전송',
+        'Updating cNFTs': 'cNFT 업데이트',
+        'Burning cNFTs': 'cNFT 소각',
+        'Decompressing cNFTs': 'cNFT 압축 해제',
+        'Delegating cNFTs': 'cNFT 위임',
+        'Verifying Collections': '컬렉션 검증',
+        'Verifying Creators': '크리에이터 검증',
+        'Concurrent Merkle Trees': '동시 머클 트리',
+        'Storing and Indexing NFT Data': 'NFT 데이터 저장 및 인덱싱',
+        'Hashing NFT Data': 'NFT 데이터 해싱',
+        'Merkle Tree Canopy': '머클 트리 캐노피',
+        'How to Create a 1,000,000 NFT Collection on Solana': 'Solana에서 100만 NFT 컬렉션 만들기',
+        'How to Interact with cNFTs on Other SVMs': '다른 SVM에서 cNFT와 상호작용하기'
+      }
+    }
+  }
 }

+ 71 - 0
src/components/products/candyMachine/index.js

@@ -205,4 +205,75 @@ export const candyMachine = {
       target: '_blank',
     }
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'TM NFT launchpad',
+      description: 'Launch your next NFT collection on Solana.',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features', 
+        'Available Guards': 'Available Guards',
+        'Custom Guards': 'Custom Guards',
+        'Candy Machine Guides': 'Candy Machine Guides'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'Candy Machine Settings': 'Candy Machine Settings',
+        'Managing Candy Machines': 'Managing Candy Machines',
+        'Inserting Items': 'Inserting Items',
+        'Candy Guards': 'Candy Guards',
+        'Guard Groups': 'Guard Groups',
+        'Special Guard Instructions': 'Special Guard Instructions',
+        'Minting': 'Minting',
+        'Programmable NFTs': 'Programmable NFTs'
+      }
+    },
+    ja: {
+      headline: 'TM NFTローンチパッド',
+      description: 'Solana上で次のNFTコレクションを立ち上げましょう。',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能',
+        'Available Guards': '利用可能なガード',
+        'Custom Guards': 'カスタムガード',
+        'Candy Machine Guides': 'Candy Machineガイド'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'Candy Machine Settings': 'Candy Machine設定',
+        'Managing Candy Machines': 'Candy Machineの管理',
+        'Inserting Items': 'アイテムの挿入',
+        'Candy Guards': 'Candy Guards',
+        'Guard Groups': 'ガードグループ',
+        'Special Guard Instructions': '特別ガード指示',
+        'Minting': 'ミンティング',
+        'Programmable NFTs': 'プログラマブルNFT'
+      }
+    },
+    ko: {
+      headline: 'TM NFT 런치패드',
+      description: '솔라나에서 다음 NFT 컬렉션을 출시하세요.',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능',
+        'Available Guards': '사용 가능한 가드',
+        'Custom Guards': '커스텀 가드',
+        'Candy Machine Guides': 'Candy Machine 가이드'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'Candy Machine Settings': 'Candy Machine 설정',
+        'Managing Candy Machines': 'Candy Machine 관리',
+        'Inserting Items': '아이템 삽입',
+        'Candy Guards': 'Candy Guards',
+        'Guard Groups': '가드 그룹',
+        'Special Guard Instructions': '특별 가드 지침',
+        'Minting': '민팅',
+        'Programmable NFTs': '프로그래밍 가능한 NFT'
+      }
+    }
+  }
 }

+ 53 - 0
src/components/products/cli/index.js

@@ -105,4 +105,57 @@ export const cli = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'MPLX CLI',
+      description: 'A CLI for the MPLX ecosystem',
+      sections: {
+        'Introduction': 'Introduction',
+        'Configuration': 'Configuration',
+        'Core Commands': 'Core Commands',
+        'Toolbox': 'Toolbox'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Installation': 'Installation',
+        'Wallets': 'Wallets',
+        'RPCs': 'RPCs',
+        'Explorer': 'Explorer'
+      }
+    },
+    ja: {
+      headline: 'MPLX CLI',
+      description: 'MPLXエコシステム用のCLI',
+      sections: {
+        'Introduction': '紹介',
+        'Configuration': '設定',
+        'Core Commands': 'Coreコマンド',
+        'Toolbox': 'ツールボックス'
+      },
+      links: {
+        'Overview': '概要',
+        'Installation': 'インストール',
+        'Wallets': 'ウォレット',
+        'RPCs': 'RPC',
+        'Explorer': 'エクスプローラー'
+      }
+    },
+    ko: {
+      headline: 'MPLX CLI',
+      description: 'MPLX 에코시스템을 위한 CLI',
+      sections: {
+        'Introduction': '소개',
+        'Configuration': '구성',
+        'Core Commands': 'Core 명령어',
+        'Toolbox': '툴박스'
+      },
+      links: {
+        'Overview': '개요',
+        'Installation': '설치',
+        'Wallets': '지갑',
+        'RPCs': 'RPC',
+        'Explorer': '익스플로러'
+      }
+    }
+  }
 }

+ 131 - 0
src/components/products/core/index.js

@@ -321,4 +321,135 @@ export const core = {
       target: '_blank',
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Next gen NFT standard',
+      description: 'Next generation Solana NFT standard.',
+      sections: {
+        'Introduction': 'Introduction',
+        'SDK': 'SDK', 
+        'Features': 'Features',
+        'Plugins': 'Plugins',
+        'External Plugins': 'External Plugins',
+        'General': 'General',
+        'JavaScript': 'JavaScript',
+        'Program Concepts': 'Program Concepts',
+        'Anchor': 'Anchor',
+      },
+      links: {
+        'Overview': 'Overview',
+        'What is an Asset?': 'What is an Asset?',
+        'JSON Schema': 'JSON Schema', 
+        'Token Metadata Differences': 'Token Metadata Differences',
+        'Ecosystem Support': 'Ecosystem Support',
+        'Anchor': 'Anchor',
+        'FAQ': 'FAQ',
+        'Javascript SDK': 'Javascript SDK',
+        'Rust SDK': 'Rust SDK',
+        'Creating Assets': 'Creating Assets',
+        'Fetching Assets': 'Fetching Assets',
+        'Updating Assets': 'Updating Assets',
+        'Transferring Assets': 'Transferring Assets',
+        'Burning Assets': 'Burning Assets',
+        'Collection Management': 'Collection Management',
+        'Execute Asset Signing': 'Execute Asset Signing',
+        'Helpers': 'Helpers',
+        'Deserializing Assets': 'Deserializing Assets',
+        'Adding Plugins': 'Adding Plugins',
+        'Removing Plugins': 'Removing Plugins',
+        'Delegating and Revoking Plugins': 'Delegating and Revoking Plugins',
+        'Immutability': 'Immutability',
+        'Soulbound Assets': 'Soulbound Assets',
+        'Print Editions': 'Print Editions',
+        'Oracle Plugin Example': 'Oracle Plugin Example',
+        'Appdata Plugin Example': 'Appdata Plugin Example',
+      }
+    },
+    ja: {
+      headline: '次世代NFT標準',
+      description: '次世代Solana NFT標準。',
+      sections: {
+        'Introduction': '紹介',
+        'SDK': 'SDK',
+        'Features': '機能', 
+        'Plugins': 'プラグイン',
+        'External Plugins': '外部プラグイン',
+        'General': '一般',
+        'JavaScript': 'JavaScript',
+        'Program Concepts': 'プログラムコンセプト',
+        'Anchor': 'Anchor',
+      },
+      links: {
+        'Overview': '概要',
+        'What is an Asset?': 'アセットとは?',
+        'JSON Schema': 'JSONスキーマ',
+        'Token Metadata Differences': 'Token Metadataとの違い',
+        'Ecosystem Support': 'エコシステムサポート',
+        'Anchor': 'Anchor',
+        'FAQ': 'よくある質問',
+        'Javascript SDK': 'JavaScript SDK',
+        'Rust SDK': 'Rust SDK', 
+        'Creating Assets': 'アセットの作成',
+        'Fetching Assets': 'アセットの取得',
+        'Updating Assets': 'アセットの更新',
+        'Transferring Assets': 'アセットの転送',
+        'Burning Assets': 'アセットのバーン',
+        'Collection Management': 'コレクション管理',
+        'Execute Asset Signing': 'アセット署名の実行',
+        'Helpers': 'ヘルパー',
+        'Deserializing Assets': 'アセットのデシリアライズ',
+        'Adding Plugins': 'プラグインの追加',
+        'Removing Plugins': 'プラグインの削除',
+        'Delegating and Revoking Plugins': 'プラグインの委任と取り消し',
+        'Immutability': '不変性',
+        'Soulbound Assets': 'ソウルバウンドアセット',
+        'Print Editions': 'プリントエディション',
+        'Oracle Plugin Example': 'Oracleプラグインの例',
+        'Appdata Plugin Example': 'Appdataプラグインの例',
+      }
+    },
+    ko: {
+      headline: '차세대 NFT 표준',
+      description: '차세대 Solana NFT 표준.',
+      sections: {
+        'Introduction': '소개',
+        'SDK': 'SDK',
+        'Features': '기능',
+        'Plugins': '플러그인',
+        'External Plugins': '외부 플러그인',
+        'General': '일반',
+        'JavaScript': 'JavaScript',
+        'Program Concepts': '프로그램 개념',
+        'Anchor': 'Anchor',
+      },
+      links: {
+        'Overview': '개요',
+        'What is an Asset?': '에셋이란?',
+        'JSON Schema': 'JSON 스키마',
+        'Token Metadata Differences': 'Token Metadata 차이점',
+        'Ecosystem Support': '생태계 지원',
+        'Anchor': 'Anchor',
+        'FAQ': '자주 묻는 질문',
+        'Javascript SDK': 'JavaScript SDK',
+        'Rust SDK': 'Rust SDK',
+        'Creating Assets': '에셋 생성',
+        'Fetching Assets': '에셋 가져오기',
+        'Updating Assets': '에셋 업데이트',
+        'Transferring Assets': '에셋 전송',
+        'Burning Assets': '에셋 소각',
+        'Collection Management': '컬렉션 관리',
+        'Execute Asset Signing': '에셋 서명 실행',
+        'Helpers': '헬퍼',
+        'Deserializing Assets': '에셋 역직렬화',
+        'Adding Plugins': '플러그인 추가',
+        'Removing Plugins': '플러그인 제거',
+        'Delegating and Revoking Plugins': '플러그인 위임 및 취소',
+        'Immutability': '불변성',
+        'Soulbound Assets': '소울바운드 에셋',
+        'Print Editions': '프린트 에디션',
+        'Oracle Plugin Example': 'Oracle 플러그인 예제',
+        'Appdata Plugin Example': 'Appdata 플러그인 예제',
+      }
+    }
+  }
 }

+ 95 - 0
src/components/products/coreCandyMachine/index.js

@@ -322,4 +322,99 @@ export const coreCandyMachine = {
       target: '_blank',
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Core Asset launchpad',
+      description: 'Launch your next MPL Core Asset collection on Solana.',
+      sections: {
+        'Introduction': 'Introduction',
+        'SDK': 'SDK',
+        'Core Candy Machine': 'Core Candy Machine',
+        'Available Guards': 'Available Guards',
+        'Custom Guards': 'Custom Guards',
+        'General': 'General'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Javascript SDK': 'Javascript SDK',
+        'Rust SDK': 'Rust SDK',
+        'Anti-Bot Protection Best Practices': 'Anti-Bot Protection Best Practices',
+        'Candy Guards': 'Candy Guards',
+        'Preparing Assets': 'Preparing Assets',
+        'Creating a Candy Machine': 'Creating a Candy Machine',
+        'Inserting Items': 'Inserting Items',
+        'Updating a Candy Machine and Guards': 'Updating a Candy Machine and Guards',
+        'Guard Groups and Phases': 'Guard Groups and Phases',
+        'Special Guard Instructions': 'Special Guard Instructions',
+        'Fetching a Candy Machine': 'Fetching a Candy Machine',
+        'Minting from a Candy Machine': 'Minting from a Candy Machine',
+        'Withdrawing a Candy Machine': 'Withdrawing a Candy Machine',
+        'Generating Client': 'Generating Client',
+        'Create a Website for minting Assets from your Core Candy Machine': 'Create a Website for minting Assets from your Core Candy Machine',
+        'Create a Core Candy Machine with Hidden Settings': 'Create a Core Candy Machine with Hidden Settings'
+      }
+    },
+    ja: {
+      headline: 'Core Assetローンチパッド',
+      description: 'Solana上で次のMPL Core Assetコレクションを立ち上げましょう。',
+      sections: {
+        'Introduction': '紹介',
+        'SDK': 'SDK',
+        'Core Candy Machine': 'Core Candy Machine',
+        'Available Guards': '利用可能なガード',
+        'Custom Guards': 'カスタムガード',
+        'General': '一般'
+      },
+      links: {
+        'Overview': '概要',
+        'Javascript SDK': 'JavaScript SDK',
+        'Rust SDK': 'Rust SDK',
+        'Anti-Bot Protection Best Practices': 'アンチボット保護のベストプラクティス',
+        'Candy Guards': 'キャンディガード',
+        'Preparing Assets': 'アセットの準備',
+        'Creating a Candy Machine': 'キャンディマシンの作成',
+        'Inserting Items': 'アイテムの挿入',
+        'Updating a Candy Machine and Guards': 'キャンディマシンとガードの更新',
+        'Guard Groups and Phases': 'ガードグループとフェーズ',
+        'Special Guard Instructions': '特別なガード命令',
+        'Fetching a Candy Machine': 'キャンディマシンの取得',
+        'Minting from a Candy Machine': 'キャンディマシンからのミント',
+        'Withdrawing a Candy Machine': 'キャンディマシンの引き出し',
+        'Generating Client': 'クライアントの生成',
+        'Create a Website for minting Assets from your Core Candy Machine': 'Core Candy MachineからアセットをミントするWebサイトの作成',
+        'Create a Core Candy Machine with Hidden Settings': '隠し設定でCore Candy Machineを作成'
+      }
+    },
+    ko: {
+      headline: 'Core Asset 런치패드',
+      description: '솔라나에서 다음 MPL Core Asset 컬렉션을 출시하세요.',
+      sections: {
+        'Introduction': '소개',
+        'SDK': 'SDK',
+        'Core Candy Machine': 'Core Candy Machine',
+        'Available Guards': '사용 가능한 가드',
+        'Custom Guards': '커스텀 가드',
+        'General': '일반'
+      },
+      links: {
+        'Overview': '개요',
+        'Javascript SDK': 'JavaScript SDK',
+        'Rust SDK': 'Rust SDK',
+        'Anti-Bot Protection Best Practices': '안티 봇 보호 베스트 프랙티스',
+        'Candy Guards': '캔디 가드',
+        'Preparing Assets': '자산 준비',
+        'Creating a Candy Machine': '캔디 머신 생성',
+        'Inserting Items': '아이템 삽입',
+        'Updating a Candy Machine and Guards': '캔디 머신 및 가드 업데이트',
+        'Guard Groups and Phases': '가드 그룹 및 단계',
+        'Special Guard Instructions': '특별 가드 명령',
+        'Fetching a Candy Machine': '캔디 머신 가져오기',
+        'Minting from a Candy Machine': '캔디 머신에서 민팅',
+        'Withdrawing a Candy Machine': '캔디 머신 인출',
+        'Generating Client': '클라이언트 생성',
+        'Create a Website for minting Assets from your Core Candy Machine': 'Core Candy Machine에서 자산을 민팅하는 웹사이트 만들기',
+        'Create a Core Candy Machine with Hidden Settings': '숨겨진 설정으로 Core Candy Machine 만들기'
+      }
+    }
+  }
 }

+ 50 - 0
src/components/products/das-api/index.js

@@ -3,6 +3,7 @@ import {
 } from '@/shared/sections';
 import { TableCellsIcon } from '@heroicons/react/24/solid';
 import { Hero } from './Hero';
+import { buildProductTranslations } from '@/config/navigation-translations';
 
 export const das = {
   name: 'DAS API',
@@ -80,4 +81,53 @@ export const das = {
       ],
     },
   ],
+  localizedNavigation: buildProductTranslations({
+    productKey: 'dasApi',
+    sectionKeys: {
+      'Introduction': 'sections.introduction',
+      'Methods & Playground': 'sections.methods',
+      'Guides': 'sections.guides',
+      'Core Extension SDK': 'sections.coreExtension'
+    },
+    linkKeys: {
+      'Overview': 'links.overview',
+      'Getting Started': 'links.gettingStarted',
+      'DAS API RPC Providers': 'links.dasApiProviders',
+      'Display Options': 'links.displayOptions',
+      'Method Overview': 'links.methodOverview',
+      'Get Asset': 'links.getAsset',
+      'Get Assets': 'links.getAssets',
+      'Get Asset Proof': 'links.getAssetProof',
+      'Get Asset Proofs': 'links.getAssetProofs',
+      'Get Asset Signatures': 'links.getAssetSignatures',
+      'Get Assets By Authority': 'links.getAssetsByAuthority',
+      'Get Assets By Creator': 'links.getAssetsByCreator',
+      'Get Assets By Group': 'links.getAssetsByGroup',
+      'Get Assets By Owner': 'links.getAssetsByOwner',
+      'Get NFT Editions': 'links.getNftEditions',
+      'Get Token Accounts': 'links.getTokenAccounts',
+      'Search Assets': 'links.searchAssets',
+      'Guides Overview': 'links.guidesOverview',
+      'Pagination': 'links.pagination',
+      'Get Collection NFTs': 'links.getCollectionNfts',
+      'Get NFTs by Owner': 'links.getNftsByOwner',
+      'Get Wallet Tokens': 'links.getWalletTokens',
+      'Get Fungible Assets': 'links.getFungibleAssets',
+      'Search by Criteria': 'links.searchByCriteria',
+      'Owner and Collection': 'links.ownerAndCollection',
+      'Find Compressed NFTs': 'links.findCompressedNfts',
+      'Collection Statistics': 'links.collectionStatistics',
+      'Find Token Holders': 'links.findTokenHolders',
+      'Extension Overview': 'links.extensionOverview',
+      'Get Core Asset': 'links.getCoreAsset',
+      'Get Core Collection': 'links.getCoreCollection',
+      'Get Core Assets By Authority': 'links.getCoreAssetsByAuthority',
+      'Get Core Assets By Collection': 'links.getCoreAssetsByCollection',
+      'Get Core Assets By Owner': 'links.getCoreAssetsByOwner',
+      'Search Core Assets': 'links.searchCoreAssets',
+      'Search Core Collections': 'links.searchCoreCollections',
+      'Plugin Derivation': 'links.pluginDerivation',
+      'Type Conversion': 'links.typeConversion'
+    }
+  })
 }

+ 44 - 0
src/components/products/fusion/index.js

@@ -35,4 +35,48 @@ export const fusion = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'NFTs inside NFTs',
+      description: 'Create composable NFTs.',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'Constraint Types': 'Constraint Types',
+        'Transfer Effects': 'Transfer Effects'
+      }
+    },
+    ja: {
+      headline: 'NFT内のNFT',
+      description: '合成可能なNFTを作成。',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'Constraint Types': '制約タイプ',
+        'Transfer Effects': '転送効果'
+      }
+    },
+    ko: {
+      headline: 'NFT 안의 NFT',
+      description: '구성 가능한 NFT를 만드세요.',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'Constraint Types': '제약 유형',
+        'Transfer Effects': '전송 효과'
+      }
+    }
+  }
 }

+ 77 - 0
src/components/products/global/index.js

@@ -54,4 +54,81 @@ export const global = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Developer Hub',
+      description: 'One place for all Metaplex developer resources.',
+      sections: {
+        'Overview': 'Overview',
+        'Resources': 'Resources',
+        'Community': 'Community',
+      },
+      links: {
+        'Introduction': 'Introduction',
+        'Programs and Tools': 'Programs and Tools',
+        'Official Links': 'Official Links',
+        'Developer Tools': 'Developer Tools',
+        'Understanding Programs': 'Understanding Programs',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDKs',
+        'RPC Providers': 'RPC Providers',
+        'Storage Providers': 'Storage Providers',
+        'Stability Index': 'Stability Index',
+        'Protocol Fees': 'Protocol Fees',
+        'Terms and Conditions': 'Terms and Conditions',
+        'Community Guides': 'Community Guides',
+        'Security': 'Security',
+        'Contact Us': 'Contact Us',
+      }
+    },
+    ja: {
+      headline: 'デベロッパーハブ',
+      description: 'Metaplexの全開発者リソースを一箇所にまとめたハブ。',
+      sections: {
+        'Overview': '概要',
+        'Resources': 'リソース',
+        'Community': 'コミュニティ',
+      },
+      links: {
+        'Introduction': '紹介',
+        'Programs and Tools': 'プログラムとツール',
+        'Official Links': '公式リンク',
+        'Developer Tools': '開発者ツール',
+        'Understanding Programs': 'プログラムの理解',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDK',
+        'RPC Providers': 'RPCプロバイダー',
+        'Storage Providers': 'ストレージプロバイダー',
+        'Stability Index': '安定性指標',
+        'Protocol Fees': 'プロトコル手数料',
+        'Terms and Conditions': '利用規約',
+        'Community Guides': 'コミュニティガイド',
+        'Security': 'セキュリティ',
+        'Contact Us': 'お問い合わせ',
+      }
+    },
+    ko: {
+      headline: '개발자 허브',
+      description: '모든 Metaplex 개발자 리소스를 한 곳에 모은 허브입니다.',
+      sections: {
+        'Overview': '개요',
+        'Resources': '리소스',
+        'Community': '커뮤니티',
+      },
+      links: {
+        'Introduction': '소개',
+        'Programs and Tools': '프로그램 및 도구',
+        'Official Links': '공식 링크',
+        'Developer Tools': '개발자 도구',
+        'Understanding Programs': '프로그램 이해',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDK',
+        'RPC Providers': 'RPC 제공업체',
+        'Storage Providers': '스토리지 제공업체',
+        'Stability Index': '안정성 지수',
+        'Protocol Fees': '프로토콜 수수료',
+        'Terms and Conditions': '이용 약관',
+        'Community Guides': '커뮤니티 가이드',
+        'Security': '보안',
+        'Contact Us': '문의하기',
+      }
+    }
+  }
 }

+ 134 - 0
src/components/products/guides/index.js

@@ -219,4 +219,138 @@ export const guides = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Guides for the Solana Blockchain',
+      description: 'Guides for the Solana Blockchain.',
+      sections: {
+        'Solana Basics': 'Solana Basics',
+        'General': 'General',
+        'Javascript': 'Javascript',
+        'Rust': 'Rust',
+        'Templates': 'Templates',
+        'Metaplex Program Guides': 'Metaplex Program Guides',
+        'Translated Guides': 'Translated Guides'
+      },
+      links: {
+        'What is Solana?': 'What is Solana?',
+        'Validators and Staking': 'Validators and Staking',
+        'RPCs and DAS': 'RPCs and DAS',
+        'Solana Programs': 'Solana Programs',
+        'Understanding PDAs': 'Understanding PDAs',
+        'Setup a Local Validator': 'Setup a Local Validator',
+        'How to Diagnose Transaction Errors on Solana': 'How to Diagnose Transaction Errors on Solana',
+        'Creating an NFT Collection With Candy Machine': 'Creating an NFT Collection With Candy Machine',
+        'Create deterministic Metadata with Turbo': 'Create deterministic Metadata with Turbo',
+        'The Payer-Authority Pattern': 'The Payer-Authority Pattern',
+        'Create a claim based airdrop using Gumdrop': 'Create a claim based airdrop using Gumdrop',
+        'Creating an NFT': 'Creating an NFT',
+        'How to Create a Solana Token': 'How to Create a Solana Token',
+        'How to Add Metadata to a Solana Token': 'How to Add Metadata to a Solana Token',
+        'Transferring Tokens': 'Transferring Tokens',
+        'Transferring SOL': 'Transferring SOL',
+        'Optimal transaction landing using umi': 'Optimal transaction landing using umi',
+        'Serializing and Deserializing Transactions': 'Serializing and Deserializing Transactions',
+        'Getting Started with Rust': 'Getting Started with Rust',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDKs',
+        'CPI into a Metaplex Program': 'CPI into a Metaplex Program',
+        'NextJs Template': 'NextJs Template',
+        'MPL-404 Hybrid UI Template': 'MPL-404 Hybrid UI Template',
+        'Bubblegum': 'Bubblegum',
+        'Core': 'Core',
+        'Candy Machine': 'Candy Machine',
+        'Core Candy Machine': 'Core Candy Machine',
+        'MPL-Hybrid / MPL-404': 'MPL-Hybrid / MPL-404',
+        'Japanese 日本語': 'Japanese 日本語'
+      }
+    },
+    ja: {
+      headline: 'Solanaブロックチェーンのガイド',
+      description: 'Solanaブロックチェーンのガイド。',
+      sections: {
+        'Solana Basics': 'Solanaの基本',
+        'General': '一般',
+        'Javascript': 'JavaScript',
+        'Rust': 'Rust',
+        'Templates': 'テンプレート',
+        'Metaplex Program Guides': 'Metaplexプログラムガイド',
+        'Translated Guides': '翻訳ガイド'
+      },
+      links: {
+        'What is Solana?': 'Solanaとは?',
+        'Validators and Staking': 'バリデーターとステーキング',
+        'RPCs and DAS': 'RPCとDAS',
+        'Solana Programs': 'Solanaプログラム',
+        'Understanding PDAs': 'PDAの理解',
+        'Setup a Local Validator': 'ローカルバリデーターのセットアップ',
+        'How to Diagnose Transaction Errors on Solana': 'Solanaでトランザクションエラーを診断する方法',
+        'Creating an NFT Collection With Candy Machine': 'Candy MachineでNFTコレクションを作成',
+        'Create deterministic Metadata with Turbo': 'Turboで決定論的メタデータを作成',
+        'The Payer-Authority Pattern': 'Payer-Authorityパターン',
+        'Create a claim based airdrop using Gumdrop': 'Gumdropを使用したクレームベースのエアドロップを作成',
+        'Creating an NFT': 'NFTの作成',
+        'How to Create a Solana Token': 'Solanaトークンの作成方法',
+        'How to Add Metadata to a Solana Token': 'Solanaトークンにメタデータを追加する方法',
+        'Transferring Tokens': 'トークンの転送',
+        'Transferring SOL': 'SOLの転送',
+        'Optimal transaction landing using umi': 'Umiを使用した最適なトランザクション着地',
+        'Serializing and Deserializing Transactions': 'トランザクションのシリアライズとデシリアライズ',
+        'Getting Started with Rust': 'Rustを始める',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDK',
+        'CPI into a Metaplex Program': 'MetaplexプログラムへのCPI',
+        'NextJs Template': 'NextJSテンプレート',
+        'MPL-404 Hybrid UI Template': 'MPL-404ハイブリッドUIテンプレート',
+        'Bubblegum': 'Bubblegum',
+        'Core': 'Core',
+        'Candy Machine': 'Candy Machine',
+        'Core Candy Machine': 'Core Candy Machine',
+        'MPL-Hybrid / MPL-404': 'MPL-Hybrid / MPL-404',
+        'Japanese 日本語': '日本語'
+      }
+    },
+    ko: {
+      headline: 'Solana 블록체인 가이드',
+      description: 'Solana 블록체인을 위한 가이드.',
+      sections: {
+        'Solana Basics': 'Solana 기초',
+        'General': '일반',
+        'Javascript': 'JavaScript',
+        'Rust': 'Rust',
+        'Templates': '템플릿',
+        'Metaplex Program Guides': 'Metaplex 프로그램 가이드',
+        'Translated Guides': '번역된 가이드'
+      },
+      links: {
+        'What is Solana?': 'Solana란 무엇인가?',
+        'Validators and Staking': '검증자와 스테이킹',
+        'RPCs and DAS': 'RPC와 DAS',
+        'Solana Programs': 'Solana 프로그램',
+        'Understanding PDAs': 'PDA 이해하기',
+        'Setup a Local Validator': '로컬 검증자 설정',
+        'How to Diagnose Transaction Errors on Solana': 'Solana에서 트랜잭션 오류 진단 방법',
+        'Creating an NFT Collection With Candy Machine': 'Candy Machine으로 NFT 컬렉션 생성',
+        'Create deterministic Metadata with Turbo': 'Turbo로 결정론적 메타데이터 생성',
+        'The Payer-Authority Pattern': 'Payer-Authority 패턴',
+        'Create a claim based airdrop using Gumdrop': 'Gumdrop을 사용한 클레임 기반 에어드롭 생성',
+        'Creating an NFT': 'NFT 생성',
+        'How to Create a Solana Token': 'Solana 토큰 생성 방법',
+        'How to Add Metadata to a Solana Token': 'Solana 토큰에 메타데이터 추가하는 방법',
+        'Transferring Tokens': '토큰 전송',
+        'Transferring SOL': 'SOL 전송',
+        'Optimal transaction landing using umi': 'Umi를 사용한 최적의 트랜잭션 착지',
+        'Serializing and Deserializing Transactions': '트랜잭션 직렬화 및 역직렬화',
+        'Getting Started with Rust': 'Rust 시작하기',
+        'Metaplex Rust SDKs': 'Metaplex Rust SDK',
+        'CPI into a Metaplex Program': 'Metaplex 프로그램으로 CPI',
+        'NextJs Template': 'NextJS 템플릿',
+        'MPL-404 Hybrid UI Template': 'MPL-404 하이브리드 UI 템플릿',
+        'Bubblegum': 'Bubblegum',
+        'Core': 'Core',
+        'Candy Machine': 'Candy Machine',
+        'Core Candy Machine': 'Core Candy Machine',
+        'MPL-Hybrid / MPL-404': 'MPL-Hybrid / MPL-404',
+        'Japanese 日本語': '일본어 日本語'
+      }
+    }
+  }
 }

+ 35 - 0
src/components/products/hydra/index.js

@@ -34,4 +34,39 @@ export const hydra = {
       target: '_blank',
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Fanout wallets',
+      description: 'Create shared wallets, split between multiple shareholders.',
+      sections: {
+        'Introduction': 'Introduction'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Quick Start': 'Quick Start'
+      }
+    },
+    ja: {
+      headline: 'ファンアウトウォレット',
+      description: '複数の株主間で分割された共有ウォレットを作成。',
+      sections: {
+        'Introduction': '紹介'
+      },
+      links: {
+        'Overview': '概要',
+        'Quick Start': 'クイックスタート'
+      }
+    },
+    ko: {
+      headline: '팬아웃 지갑',
+      description: '여러 주주 간에 분할된 공유 지갑을 만드세요.',
+      sections: {
+        'Introduction': '소개'
+      },
+      links: {
+        'Overview': '개요',
+        'Quick Start': '퀵 스타트'
+      }
+    }
+  }
 }

+ 65 - 0
src/components/products/inscription/index.js

@@ -66,4 +66,69 @@ export const inscription = {
       target: '_blank',
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'NFT inscribed on Solana',
+      description: 'Inscribe Data to Solana state.',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features',
+        'Advanced': 'Advanced'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'FAQ': 'FAQ',
+        'Initialize': 'Initialize',
+        'Write Data': 'Write Data',
+        'Fetch': 'Fetch',
+        'Clear Data': 'Clear Data',
+        'Close': 'Close',
+        'Authority': 'Authority',
+        'Inscription Sharding': 'Inscription Sharding'
+      }
+    },
+    ja: {
+      headline: 'Solanaに刻印されたNFT',
+      description: 'Solanaステートにデータを刻印。',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能',
+        'Advanced': '高度な機能'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'FAQ': 'よくある質問',
+        'Initialize': '初期化',
+        'Write Data': 'データの書き込み',
+        'Fetch': '取得',
+        'Clear Data': 'データのクリア',
+        'Close': 'クローズ',
+        'Authority': '権限',
+        'Inscription Sharding': 'Inscriptionシャーディング'
+      }
+    },
+    ko: {
+      headline: 'Solana에 새겨진 NFT',
+      description: 'Solana 상태에 데이터를 새기세요.',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능',
+        'Advanced': '고급 기능'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'FAQ': '자주 묻는 질문',
+        'Initialize': '초기화',
+        'Write Data': '데이터 쓰기',
+        'Fetch': '가져오기',
+        'Clear Data': '데이터 지우기',
+        'Close': '닫기',
+        'Authority': '권한',
+        'Inscription Sharding': 'Inscription 샤딩'
+      }
+    }
+  }
 }

+ 101 - 0
src/components/products/legacyDocumentation/index.js

@@ -108,4 +108,105 @@ export const legacyDocumentation = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Products from our old docs',
+      description: 'A collection of documentation of older Programs and Tools which might not be used anymore or deprecated. Migrated from our old docs for documentation for completeness.',
+      sections: {
+        'Auction House': 'Auction House',
+        'Developer Tools': 'Developer Tools',
+        'Fixed Price Sale': 'Fixed Price Sale',
+        'Gumdrop': 'Gumdrop',
+        'Mobile SDKs': 'Mobile SDKs',
+        'Token Entangler': 'Token Entangler',
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'Auction House Settings': 'Auction House Settings',
+        'Managing Auction Houses': 'Managing Auction Houses',
+        'Trading Assets': 'Trading Assets',
+        'Managing Buyer Escrow Account': 'Managing Buyer Escrow Account',
+        'Auction House Receipts': 'Auction House Receipts',
+        'Finding Bids, listings, sales': 'Finding Bids, listings, sales',
+        'How to manage Auction House using CLI': 'How to manage Auction House using CLI',
+        'Timed Auctions with Auctioneers': 'Timed Auctions with Auctioneers',
+        'FAQ': 'FAQ',
+        'Solita': 'Solita',
+        'Shank': 'Shank',
+        'Beet': 'Beet',
+        'Cusper': 'Cusper',
+        'Rust Bin': 'Rust Bin',
+        'Introduction': 'Introduction',
+        'Android SDK': 'Android SDK',
+        'iOS SDK': 'iOS SDK',
+      }
+    },
+    ja: {
+      headline: '旧ドキュメントの製品',
+      description: '現在使用されていないか非推奨の古いプログラムやツールのドキュメントコレクション。完全性のために旧ドキュメントから移行。',
+      sections: {
+        'Auction House': 'Auction House',
+        'Developer Tools': '開発者ツール',
+        'Fixed Price Sale': 'Fixed Price Sale',
+        'Gumdrop': 'Gumdrop',
+        'Mobile SDKs': 'モバイルSDK',
+        'Token Entangler': 'Token Entangler',
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'Auction House Settings': 'Auction House設定',
+        'Managing Auction Houses': 'Auction Houseの管理',
+        'Trading Assets': 'アセットの取引',
+        'Managing Buyer Escrow Account': '購入者エスクローアカウントの管理',
+        'Auction House Receipts': 'Auction Houseレシート',
+        'Finding Bids, listings, sales': '入札、リスティング、販売の検索',
+        'How to manage Auction House using CLI': 'CLIを使用したAuction Houseの管理方法',
+        'Timed Auctions with Auctioneers': 'Auctioneersを使用した時間制オークション',
+        'FAQ': 'よくある質問',
+        'Solita': 'Solita',
+        'Shank': 'Shank',
+        'Beet': 'Beet',
+        'Cusper': 'Cusper',
+        'Rust Bin': 'Rust Bin',
+        'Introduction': '紹介',
+        'Android SDK': 'Android SDK',
+        'iOS SDK': 'iOS SDK',
+      }
+    },
+    ko: {
+      headline: '기존 문서의 제품',
+      description: '더 이상 사용되지 않거나 사용되지 않을 수 있는 이전 프로그램 및 도구의 문서 모음입니다. 완전성을 위해 기존 문서에서 마이그레이션되었습니다.',
+      sections: {
+        'Auction House': 'Auction House',
+        'Developer Tools': '개발자 도구',
+        'Fixed Price Sale': 'Fixed Price Sale',
+        'Gumdrop': 'Gumdrop',
+        'Mobile SDKs': '모바일 SDK',
+        'Token Entangler': 'Token Entangler',
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'Auction House Settings': 'Auction House 설정',
+        'Managing Auction Houses': 'Auction House 관리',
+        'Trading Assets': '에셋 거래',
+        'Managing Buyer Escrow Account': '구매자 에스크로 계정 관리',
+        'Auction House Receipts': 'Auction House 영수증',
+        'Finding Bids, listings, sales': '입찰, 리스팅, 판매 찾기',
+        'How to manage Auction House using CLI': 'CLI를 사용한 Auction House 관리 방법',
+        'Timed Auctions with Auctioneers': 'Auctioneers를 사용한 시간제 경매',
+        'FAQ': '자주 묻는 질문',
+        'Solita': 'Solita',
+        'Shank': 'Shank',
+        'Beet': 'Beet',
+        'Cusper': 'Cusper',
+        'Rust Bin': 'Rust Bin',
+        'Introduction': '소개',
+        'Android SDK': 'Android SDK',
+        'iOS SDK': 'iOS SDK',
+      }
+    }
+  }
 }

+ 80 - 0
src/components/products/mpl-hybrid/index.js

@@ -97,4 +97,84 @@ export const mplHybrid = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Hybrid Assets',
+      description: 'Framework and on-chain protocol for hybrid assets.',
+      sections: {
+        'Introduction': 'Introduction',
+        'SDK': 'SDK',
+        'UI Templates': 'UI Templates',
+        'Features': 'Features',
+        'General': 'General'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Preparation': 'Preparation',
+        'FAQ': 'FAQ',
+        'Javascript SDK': 'Javascript SDK',
+        'MPL-404 Hybrid UI': 'MPL-404 Hybrid UI',
+        'Create Escrow Configuration': 'Create Escrow Configuration',
+        'Fetch Escrow Configuration': 'Fetch Escrow Configuration',
+        'Funding Escrow': 'Funding Escrow',
+        'Updating Escrow Configuration': 'Updating Escrow Configuration',
+        'Swapping NFTs to Tokens': 'Swapping NFTs to Tokens',
+        'Swapping Tokens to NFTs': 'Swapping Tokens to NFTs',
+        'Create your first Hybrid Collection': 'Create your first Hybrid Collection',
+        'MPL-404 Hybrid UI Template': 'MPL-404 Hybrid UI Template'
+      }
+    },
+    ja: {
+      headline: 'ハイブリッドアセット',
+      description: 'ハイブリッドアセット用のフレームワークとオンチェーンプロトコル。',
+      sections: {
+        'Introduction': '紹介',
+        'SDK': 'SDK',
+        'UI Templates': 'UIテンプレート',
+        'Features': '機能',
+        'General': '一般'
+      },
+      links: {
+        'Overview': '概要',
+        'Preparation': '準備',
+        'FAQ': 'よくある質問',
+        'Javascript SDK': 'JavaScript SDK',
+        'MPL-404 Hybrid UI': 'MPL-404ハイブリッドUI',
+        'Create Escrow Configuration': 'エスクロー設定の作成',
+        'Fetch Escrow Configuration': 'エスクロー設定の取得',
+        'Funding Escrow': 'エスクローの資金調達',
+        'Updating Escrow Configuration': 'エスクロー設定の更新',
+        'Swapping NFTs to Tokens': 'NFTからトークンへのスワップ',
+        'Swapping Tokens to NFTs': 'トークンからNFTへのスワップ',
+        'Create your first Hybrid Collection': '最初のハイブリッドコレクションを作成',
+        'MPL-404 Hybrid UI Template': 'MPL-404ハイブリッドUIテンプレート'
+      }
+    },
+    ko: {
+      headline: '하이브리드 에셋',
+      description: '하이브리드 에셋을 위한 프레임워크 및 온체인 프로토콜입니다.',
+      sections: {
+        'Introduction': '소개',
+        'SDK': 'SDK',
+        'UI Templates': 'UI 템플릿',
+        'Features': '기능',
+        'General': '일반'
+      },
+      links: {
+        'Overview': '개요',
+        'Preparation': '준비',
+        'FAQ': '자주 묻는 질문',
+        'Javascript SDK': 'JavaScript SDK',
+        'MPL-404 Hybrid UI': 'MPL-404 하이브리드 UI',
+        'Create Escrow Configuration': '에스크로 구성 생성',
+        'Fetch Escrow Configuration': '에스크로 구성 가져오기',
+        'Funding Escrow': '에스크로 자금 조달',
+        'Updating Escrow Configuration': '에스크로 구성 업데이트',
+        'Swapping NFTs to Tokens': 'NFT를 토큰으로 스와핑',
+        'Swapping Tokens to NFTs': '토큰을 NFT로 스와핑',
+        'Create your first Hybrid Collection': '첫 번째 하이브리드 컬렉션 만들기',
+        'MPL-404 Hybrid UI Template': 'MPL-404 하이브리드 UI 템플릿'
+      }
+    }
+  }
 }

+ 41 - 0
src/components/products/shank/index.js

@@ -40,4 +40,45 @@ export const shank = {
       ],
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'IDL Extraction for Solana Programs',
+      description: 'Extract IDLs from Rust Solana program code using attribute macros',
+      sections: {
+        'Introduction': 'Introduction',
+        'Reference': 'Reference'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'Macros Reference': 'Macros Reference'
+      }
+    },
+    ja: {
+      headline: 'SolanaプログラムのIDL抽出',
+      description: '属性マクロを使用してRust SolanaプログラムコードからIDLを抽出',
+      sections: {
+        'Introduction': '紹介',
+        'Reference': 'リファレンス'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'Macros Reference': 'マクロリファレンス'
+      }
+    },
+    ko: {
+      headline: 'Solana 프로그램용 IDL 추출',
+      description: '속성 매크로를 사용하여 Rust Solana 프로그램 코드에서 IDL을 추출',
+      sections: {
+        'Introduction': '소개',
+        'Reference': '참조'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'Macros Reference': '매크로 참조'
+      }
+    }
+  }
 }

+ 14 - 0
src/components/products/sugar/index.js

@@ -13,4 +13,18 @@ export const sugar = {
   heroes: [{ path: '/candy-machine/sugar', component: Hero }],
   sections: [
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Create Candy Machines easily',
+      description: 'Create Candy Machines easily'
+    },
+    ja: {
+      headline: 'Candy Machineを簡単に作成',
+      description: 'Candy Machineを簡単に作成'
+    },
+    ko: {
+      headline: 'Candy Machine 쉽게 만들기',
+      description: 'Candy Machine을 쉽게 만들 수 있습니다'
+    }
+  }
 }

+ 86 - 0
src/components/products/tokenAuthRules/index.js

@@ -106,4 +106,90 @@ export const tokenAuthRules = {
       target: '_blank'
     },
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'NFT permissions',
+      description: 'Design custom authorization rules for your NFTs.',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features',
+        'Composite Rules': 'Composite Rules',
+        'Primitive Rules': 'Primitive Rules',
+        'Advanced': 'Advanced'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Metaplex Rule Sets': 'Metaplex Rule Sets',
+        'Create or Update Rule Sets': 'Create or Update Rule Sets',
+        'Validating with a Rule Set': 'Validating with a Rule Set',
+        'All': 'All',
+        'Any': 'Any',
+        'Not': 'Not',
+        'Additional Signer': 'Additional Signer',
+        'Amount': 'Amount',
+        'Namespace': 'Namespace',
+        'Pass': 'Pass',
+        'PDA Match': 'PDA Match',
+        'Program Owned': 'Program Owned',
+        'Public Key Match': 'Public Key Match',
+        'Rule Set Buffers': 'Rule Set Buffers'
+      }
+    },
+    ja: {
+      headline: 'NFT権限',
+      description: 'NFTのカスタム認証ルールを設計。',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能',
+        'Composite Rules': 'コンポジットルール',
+        'Primitive Rules': 'プリミティブルール',
+        'Advanced': '高度な機能'
+      },
+      links: {
+        'Overview': '概要',
+        'Metaplex Rule Sets': 'Metaplexルールセット',
+        'Create or Update Rule Sets': 'ルールセットの作成・更新',
+        'Validating with a Rule Set': 'ルールセットでの検証',
+        'All': 'All(全て)',
+        'Any': 'Any(いずれか)',
+        'Not': 'Not(否定)',
+        'Additional Signer': '追加署名者',
+        'Amount': '数量',
+        'Namespace': 'ネームスペース',
+        'Pass': 'Pass(通過)',
+        'PDA Match': 'PDAマッチ',
+        'Program Owned': 'プログラム所有',
+        'Public Key Match': '公開鍵マッチ',
+        'Rule Set Buffers': 'ルールセットバッファ'
+      }
+    },
+    ko: {
+      headline: 'NFT 권한',
+      description: 'NFT에 대한 사용자 정의 인증 규칙을 설계하세요.',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능',
+        'Composite Rules': '복합 규칙',
+        'Primitive Rules': '원시 규칙',
+        'Advanced': '고급 기능'
+      },
+      links: {
+        'Overview': '개요',
+        'Metaplex Rule Sets': 'Metaplex 규칙 세트',
+        'Create or Update Rule Sets': '규칙 세트 생성 또는 업데이트',
+        'Validating with a Rule Set': '규칙 세트로 검증하기',
+        'All': 'All (모두)',
+        'Any': 'Any (임의)',
+        'Not': 'Not (부정)',
+        'Additional Signer': '추가 서명자',
+        'Amount': '수량',
+        'Namespace': '네임스페이스',
+        'Pass': 'Pass (통과)',
+        'PDA Match': 'PDA 매치',
+        'Program Owned': '프로그램 소유',
+        'Public Key Match': '공개 키 매치',
+        'Rule Set Buffers': '규칙 세트 버퍼'
+      }
+    }
+  }
 }

+ 104 - 0
src/components/products/tokenMetadata/index.js

@@ -108,4 +108,108 @@ export const tokenMetadata = {
     },
 
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Digital ownership standard',
+      description: 'Create tokens and NFTs with the SPL Token Program',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features',
+        'Guides': 'Guides',
+        'Javascript': 'Javascript',
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'FAQ': 'FAQ',
+        'Account Size Reduction': 'Account Size Reduction',
+        'Token Standards (Assets)': 'Token Standards (Assets)',
+        'Minting Assets': 'Minting Assets',
+        'Fetching Assets': 'Fetching Assets',
+        'Updating Assets': 'Updating Assets',
+        'Transferring Assets': 'Transferring Assets',
+        'Burning Assets': 'Burning Assets',
+        'Printed Editions': 'Printed Editions',
+        'Verified Collections': 'Verified Collections',
+        'Verified Creators': 'Verified Creators',
+        'Delegated Authorities': 'Delegated Authorities',
+        'Locking Assets': 'Locking Assets',
+        'Programmable NFTs (pNFTs)': 'Programmable NFTs (pNFTs)',
+        'NFT Escrow': 'NFT Escrow',
+        'SPL Token-2022': 'SPL Token-2022',
+        'Get Mints by Collection': 'Get Mints by Collection',
+        'Create a claim based airdrop using Gumdrop': 'Create a claim based airdrop using Gumdrop',
+        'Token Claimer (Airdrop) Smart Contract': 'Token Claimer (Airdrop) Smart Contract',
+        'Create an NFT': 'Create an NFT',
+      }
+    },
+    ja: {
+      headline: 'デジタル所有権標準',
+      description: 'SPL Token ProgramでトークンとNFTを作成',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能',
+        'Guides': 'ガイド',
+        'Javascript': 'JavaScript',
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'FAQ': 'よくある質問',
+        'Account Size Reduction': 'アカウントサイズ削減',
+        'Token Standards (Assets)': 'トークン標準(アセット)',
+        'Minting Assets': 'アセットのミント',
+        'Fetching Assets': 'アセットの取得',
+        'Updating Assets': 'アセットの更新',
+        'Transferring Assets': 'アセットの転送',
+        'Burning Assets': 'アセットのバーン',
+        'Printed Editions': '印刷エディション',
+        'Verified Collections': '検証済みコレクション',
+        'Verified Creators': '検証済み作成者',
+        'Delegated Authorities': '委任された権限',
+        'Locking Assets': 'アセットのロック',
+        'Programmable NFTs (pNFTs)': 'プログラマブルNFT(pNFT)',
+        'NFT Escrow': 'NFTエスクロー',
+        'SPL Token-2022': 'SPL Token-2022',
+        'Get Mints by Collection': 'コレクションでMintを取得',
+        'Create a claim based airdrop using Gumdrop': 'Gumdropを使用したクレームベースのエアドロップを作成',
+        'Token Claimer (Airdrop) Smart Contract': 'トークンクレーマー(エアドロップ)スマートコントラクト',
+        'Create an NFT': 'NFTの作成',
+      }
+    },
+    ko: {
+      headline: '디지털 소유권 표준',
+      description: 'SPL Token Program으로 토큰과 NFT를 생성',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능',
+        'Guides': '가이드',
+        'Javascript': 'JavaScript',
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'FAQ': '자주 묻는 질문',
+        'Account Size Reduction': '계정 크기 축소',
+        'Token Standards (Assets)': '토큰 표준 (에셋)',
+        'Minting Assets': '에셋 민팅',
+        'Fetching Assets': '에셋 가져오기',
+        'Updating Assets': '에셋 업데이트',
+        'Transferring Assets': '에셋 전송',
+        'Burning Assets': '에셋 소각',
+        'Printed Editions': '인쇄된 에디션',
+        'Verified Collections': '검증된 컬렉션',
+        'Verified Creators': '검증된 크리에이터',
+        'Delegated Authorities': '위임된 권한',
+        'Locking Assets': '에셋 잠금',
+        'Programmable NFTs (pNFTs)': '프로그래밍 가능한 NFT (pNFT)',
+        'NFT Escrow': 'NFT 에스크로',
+        'SPL Token-2022': 'SPL Token-2022',
+        'Get Mints by Collection': '컬렉션으로 민트 가져오기',
+        'Create a claim based airdrop using Gumdrop': 'Gumdrop을 사용한 클레임 기반 에어드롭 생성',
+        'Token Claimer (Airdrop) Smart Contract': '토큰 클레이머 (에어드롭) 스마트 컨트랙트',
+        'Create an NFT': 'NFT 생성',
+      }
+    }
+  }
 }

+ 116 - 0
src/components/products/umi/index.js

@@ -121,4 +121,120 @@ export const umi = {
     },
 
   ],
+  localizedNavigation: {
+    en: {
+      headline: 'Client wrapper',
+      description: 'A collection of core programs for your applications.',
+      sections: {
+        'Introduction': 'Introduction',
+        'Features': 'Features',
+        'Toolbox': 'Toolbox',
+        'Guides': 'Guides'
+      },
+      links: {
+        'Overview': 'Overview',
+        'Getting Started': 'Getting Started',
+        'Metaplex Umi Plugins': 'Metaplex Umi Plugins',
+        'Web3js Differences and Adapters': 'Web3js Differences and Adapters',
+        '@solana/kit Adapters': '@solana/kit Adapters',
+        'Accounts': 'Accounts',
+        'Helpers': 'Helpers',
+        'HTTP Requests': 'HTTP Requests',
+        'Interfaces': 'Interfaces',
+        'Implementations': 'Implementations',
+        'Kinobi': 'Kinobi',
+        'Plugins': 'Plugins',
+        'Programs': 'Programs',
+        'PublicKeys and Signers': 'PublicKeys and Signers',
+        'Create Account': 'Create Account',
+        'Transfer Sol': 'Transfer Sol',
+        'Token Managment': 'Token Managment',
+        'Priority Fees and Compute Managment': 'Priority Fees and Compute Managment',
+        'Address Lookup Table': 'Address Lookup Table',
+        'Transaction Memo': 'Transaction Memo',
+        'Optimal transaction landing': 'Optimal transaction landing',
+        'Serializing and Deserializing Transactions': 'Serializing and Deserializing Transactions',
+        'RPC': 'RPC',
+        'Serializers': 'Serializers',
+        'Storage': 'Storage',
+        'Transactions': 'Transactions'
+      }
+    },
+    ja: {
+      headline: 'クライアントラッパー',
+      description: 'アプリケーション用のコアプログラムのコレクション。',
+      sections: {
+        'Introduction': '紹介',
+        'Features': '機能',
+        'Toolbox': 'ツールボックス',
+        'Guides': 'ガイド'
+      },
+      links: {
+        'Overview': '概要',
+        'Getting Started': 'はじめに',
+        'Metaplex Umi Plugins': 'Metaplex Umiプラグイン',
+        'Web3js Differences and Adapters': 'Web3jsの違いとアダプター',
+        '@solana/kit Adapters': '@solana/kitアダプター',
+        'Accounts': 'アカウント',
+        'Helpers': 'ヘルパー',
+        'HTTP Requests': 'HTTPリクエスト',
+        'Interfaces': 'インターフェース',
+        'Implementations': '実装',
+        'Kinobi': 'Kinobi',
+        'Plugins': 'プラグイン',
+        'Programs': 'プログラム',
+        'PublicKeys and Signers': '公開鍵と署名者',
+        'RPC': 'RPC',
+        'Serializers': 'シリアライザー',
+        'Storage': 'ストレージ',
+        'Transactions': 'トランザクション',
+        'Create Account': 'アカウント作成',
+        'Transfer Sol': 'Sol転送',
+        'Token Managment': 'トークン管理',
+        'Priority Fees and Compute Managment': 'プライオリティ手数料とコンピュート管理',
+        'Address Lookup Table': 'アドレスルックアップテーブル',
+        'Transaction Memo': 'トランザクションメモ',
+        'Optimal transaction landing': '最適なトランザクション着地',
+        'Serializing and Deserializing Transactions': 'トランザクションのシリアライズとデシリアライズ'
+      }
+    },
+    ko: {
+      headline: '클라이언트 래퍼',
+      description: '애플리케이션을 위한 핵심 프로그램 모음집입니다.',
+      sections: {
+        'Introduction': '소개',
+        'Features': '기능',
+        'Toolbox': '툴박스',
+        'Guides': '가이드'
+      },
+      links: {
+        'Overview': '개요',
+        'Getting Started': '시작하기',
+        'Metaplex Umi Plugins': 'Metaplex Umi 플러그인',
+        'Web3js Differences and Adapters': 'Web3js 차이점 및 어댑터',
+        '@solana/kit Adapters': '@solana/kit 어댑터',
+        'Accounts': '계정',
+        'Helpers': '헬퍼',
+        'HTTP Requests': 'HTTP 요청',
+        'Interfaces': '인터페이스',
+        'Implementations': '구현',
+        'Kinobi': 'Kinobi',
+        'Plugins': '플러그인',
+        'Programs': '프로그램',
+        'PublicKeys and Signers': '공개 키 및 서명자',
+        'RPC': 'RPC',
+        'Serializers': '시리얼라이저',
+        'Storage': '스토리지',
+        'Transactions': '트랜잭션',
+        'Create Account': '계정 생성',
+        'Transfer Sol': 'Sol 전송',
+        'Token Managment': '토큰 관리',
+        'Priority Fees and Compute Managment': '우선 수수료 및 컴퓨트 관리',
+        'Address Lookup Table': '주소 조회 테이블',
+        'Transaction Memo': '트랜잭션 메모',
+        'Optimal transaction landing': '최적의 트랜잭션 착지',
+        'Serializing and Deserializing Transactions': '트랜잭션 직렬화 및 역직렬화'
+      }
+    }
+  }
 }

+ 168 - 0
src/config/TRANSLATIONS_REVIEW.md

@@ -0,0 +1,168 @@
+# Navigation Translations Review
+
+## Purpose
+
+This document coordinates native-speaker review of Japanese and Korean translations used in the Metaplex Developer Hub navigation system.
+
+## Location
+
+All centralized navigation translations are located in:
+- **File**: `src/config/navigation-translations.js`
+- **Structure**: Organized by sections, links, and product-specific content
+
+## Review Process
+
+### For Japanese Translations (日本語)
+
+**Native Speaker Needed**: Yes
+**Review Status**: ⚠️ PENDING REVIEW
+**Priority**: High
+
+Please review all translations marked with `ja:` in `navigation-translations.js` for:
+1. **Accuracy**: Does the translation correctly convey the English meaning?
+2. **Naturalness**: Does it sound natural to native Japanese speakers?
+3. **Consistency**: Are similar terms translated consistently throughout?
+4. **Technical Appropriateness**: Are technical terms (NFT, API, etc.) handled correctly?
+
+**Common Terms to Verify**:
+- Overview → 概要
+- Getting Started → はじめに
+- Features → 機能
+- Advanced → 高度
+- FAQ → よくある質問
+- Guides → ガイド
+- Introduction → 紹介
+
+### For Korean Translations (한국어)
+
+**Native Speaker Needed**: Yes
+**Review Status**: ⚠️ PENDING REVIEW
+**Priority**: High
+
+Please review all translations marked with `ko:` in `navigation-translations.js` for:
+1. **Accuracy**: Does the translation correctly convey the English meaning?
+2. **Naturalness**: Does it sound natural to native Korean speakers?
+3. **Consistency**: Are similar terms translated consistently throughout?
+4. **Technical Appropriateness**: Are technical terms (NFT, API, etc.) handled correctly?
+
+**Common Terms to Verify**:
+- Overview → 개요
+- Getting Started → 시작하기
+- Features → 기능
+- Advanced → 고급
+- FAQ → 자주 묻는 질문
+- Guides → 가이드
+- Introduction → 소개
+
+## Known Issues
+
+### Japanese Inconsistencies
+- **"Advanced"** has two variants in original files:
+  - `高度` (short form) - Currently used in centralized file
+  - `高度な機能` (long form with "functions/features")
+  - **Action Needed**: Confirm which is preferred
+
+### Korean Inconsistencies
+- **"Advanced"** has two variants in original files:
+  - `고급` (short form) - Currently used in centralized file
+  - `고급 기능` (long form with "functions")
+  - **Action Needed**: Confirm which is preferred
+
+## How to Submit Review Feedback
+
+### Option 1: Direct File Edit
+1. Open `src/config/navigation-translations.js`
+2. Edit the translations directly
+3. Submit a pull request with your changes
+4. Include reasoning for significant changes in PR description
+
+### Option 2: Issue Report
+1. Create a new GitHub issue
+2. Title: "Translation Review: [Language] - [Section]"
+3. List translations that need changes
+4. Provide corrected translations with explanations
+
+### Option 3: Spreadsheet Review
+1. Request access to review spreadsheet (if available)
+2. Mark corrections in dedicated review columns
+3. Add comments for context
+
+## Translation Coverage
+
+Current translations cover:
+
+### Common Navigation Elements
+- ✅ Section titles (Introduction, SDK, Features, Guides, etc.)
+- ✅ Common links (Overview, Getting Started, FAQ, etc.)
+- ✅ Programming languages (JavaScript, Rust)
+
+### Product-Specific Content
+- ✅ DAS API navigation (~30 unique terms)
+- ✅ Bubblegum v2 navigation (~20 unique terms)
+- ⚠️ Other products: Pending migration to centralized system
+
+### Areas Not Yet Centralized
+- Product headlines and descriptions (partially centralized)
+- Page content and markdown files
+- Error messages and UI text
+
+## Testing Translations
+
+After making changes:
+
+1. **Build the site**:
+   ```bash
+   pnpm run build
+   ```
+
+2. **Test in browser**:
+   - Navigate to `/ja/[product]` for Japanese
+   - Navigate to `/ko/[product]` for Korean
+   - Verify navigation text displays correctly
+
+3. **Check for missing translations**:
+   ```bash
+   pnpm run validate-translations
+   ```
+
+## Contact
+
+For questions about the translation system or review process:
+- File an issue: `https://github.com/metaplex-foundation/developer-hub/issues`
+- Tag with: `translations`, `i18n`, `needs-review`
+
+## Timeline
+
+**Target Review Completion**: [To be determined]
+**Assigned Reviewers**:
+- Japanese: [To be assigned]
+- Korean: [To be assigned]
+
+---
+
+## Review Checklist
+
+### Japanese Reviewer
+
+- [ ] Review all `sections` translations
+- [ ] Review all `links` translations
+- [ ] Review product-specific headlines/descriptions
+- [ ] Verify technical term consistency
+- [ ] Confirm "Advanced" translation preference
+- [ ] Test in browser
+- [ ] Submit feedback
+
+### Korean Reviewer
+
+- [ ] Review all `sections` translations
+- [ ] Review all `links` translations
+- [ ] Review product-specific headlines/descriptions
+- [ ] Verify technical term consistency
+- [ ] Confirm "Advanced" translation preference
+- [ ] Test in browser
+- [ ] Submit feedback
+
+---
+
+**Last Updated**: 2025-01-22
+**Review Status**: Awaiting native speaker review

+ 340 - 0
src/config/TRANSLATION_SYSTEM.md

@@ -0,0 +1,340 @@
+# Navigation Translation System
+
+## Overview
+
+The Metaplex Developer Hub uses a **hybrid translation system** that balances:
+- ✅ **Consistency** - Common terms translated once, used everywhere
+- ✅ **Discoverability** - Product-specific translations visible in product files
+- ✅ **Maintainability** - Single source of truth for shared translations
+- ✅ **Flexibility** - Easy to add/edit translations
+
+## System Components
+
+### 1. Centralized Translations
+**File**: `src/config/navigation-translations.js`
+
+Contains common navigation terms used across multiple products:
+- Section titles: Introduction, SDK, Features, Guides, etc.
+- Common links: Overview, Getting Started, FAQ, etc.
+- Product headlines and descriptions
+
+### 2. Product Translation Builder
+**Function**: `buildProductTranslations(config)`
+
+Generates localized navigation from:
+- **Centralized keys** (e.g., `'links.overview'`)
+- **Inline translations** (e.g., `{ ja: '...', ko: '...' }`)
+- **Mixed approach** (recommended)
+
+## How to Use
+
+### For Common Terms (Recommended)
+
+Use centralized keys for terms that appear in multiple products:
+
+```javascript
+localizedNavigation: buildProductTranslations({
+  linkKeys: {
+    'Overview': 'links.overview',         // ✅ Centralized
+    'Getting Started': 'links.gettingStarted', // ✅ Centralized
+    'FAQ': 'links.faq'                    // ✅ Centralized
+  }
+})
+```
+
+**Benefits**:
+- Change once, updates everywhere
+- Guaranteed consistency
+- Translations vetted by native speakers
+
+### For Product-Specific Terms (Recommended)
+
+Use inline translations for terms unique to your product:
+
+```javascript
+localizedNavigation: buildProductTranslations({
+  linkKeys: {
+    'Creating Bubblegum Trees': {         // ✅ Inline, product-specific
+      ja: 'Bubblegumツリーの作成',
+      ko: 'Bubblegum 트리 생성'
+    },
+    'Minting cNFTs': {                    // ✅ Inline, product-specific
+      ja: 'cNFTのミント',
+      ko: 'cNFT 민팅'
+    }
+  }
+})
+```
+
+**Benefits**:
+- Easy to find (in product file)
+- Easy to edit (no central file search)
+- Context visible (with product code)
+
+### Hybrid Approach (Best Practice)
+
+Combine both approaches for optimal results:
+
+```javascript
+localizedNavigation: buildProductTranslations({
+  productKey: 'bubblegumV2',  // For headline/description
+
+  sectionKeys: {
+    'Introduction': 'sections.introduction',  // Common - centralized
+    'SDK': 'sections.sdk',                     // Common - centralized
+    'Features': 'sections.features'            // Common - centralized
+  },
+
+  linkKeys: {
+    // Common terms - use centralized
+    'Overview': 'links.overview',
+    'FAQ': 'links.faq',
+
+    // Product-specific - inline
+    'Creating Bubblegum Trees': {
+      ja: 'Bubblegumツリーの作成',
+      ko: 'Bubblegum 트리 생성'
+    }
+  }
+})
+```
+
+## How to Edit Translations
+
+### Editing Centralized Translations
+
+1. Open `src/config/navigation-translations.js`
+2. Find the term in `NAVIGATION_TRANSLATIONS.sections` or `.links`
+3. Edit the `ja` or `ko` value
+4. Save - updates all products using that term
+
+**Example**:
+```javascript
+overview: {
+  en: 'Overview',
+  ja: '概要',      // ← Edit here
+  ko: '개요'       // ← Or here
+}
+```
+
+### Editing Inline Translations
+
+1. Open the product file (e.g., `src/components/products/bubblegum-v2/index.js`)
+2. Find the term in `linkKeys` with inline translations
+3. Edit the `ja` or `ko` value directly
+4. Save - only affects this product
+
+**Example**:
+```javascript
+'Creating Bubblegum Trees': {
+  ja: 'Bubblegumツリーの作成',  // ← Edit here
+  ko: 'Bubblegum 트리 생성'     // ← Or here
+}
+```
+
+## Decision Guide: Centralized vs Inline
+
+Use this flowchart to decide:
+
+```
+Is the term used in 2+ products?
+├─ YES → Use centralized key
+└─ NO → Is it product-specific?
+   ├─ YES → Use inline translation
+   └─ NO → Consider adding to centralized (future reuse)
+```
+
+## Adding New Translations
+
+### Adding a New Centralized Term
+
+1. Open `src/config/navigation-translations.js`
+2. Add to appropriate section (`sections`, `links`, or `products`)
+3. Provide `en`, `ja`, and `ko` translations
+4. Use camelCase key name
+
+**Example**:
+```javascript
+links: {
+  // ... existing links
+  myNewFeature: {
+    en: 'My New Feature',
+    ja: '新機能',
+    ko: '새로운 기능'
+  }
+}
+```
+
+### Adding a New Product Translation
+
+1. Open your product file
+2. Add to `buildProductTranslations` config
+3. Choose centralized key or inline translation
+
+**Example**:
+```javascript
+linkKeys: {
+  'My Unique Feature': {
+    ja: 'ユニーク機能',
+    ko: '고유 기능'
+  }
+}
+```
+
+## Migration Guide
+
+### Migrating Existing Products
+
+To migrate a product from old format to new system:
+
+1. **Identify common terms** in your `localizedNavigation`
+2. **Replace with keys** for terms in `navigation-translations.js`
+3. **Keep inline** for product-specific terms
+4. **Test** with `pnpm run build`
+
+**Before**:
+```javascript
+localizedNavigation: {
+  ja: {
+    sections: {
+      'Introduction': '紹介',
+      'Custom Section': 'カスタムセクション'
+    }
+  }
+}
+```
+
+**After**:
+```javascript
+localizedNavigation: buildProductTranslations({
+  sectionKeys: {
+    'Introduction': 'sections.introduction',  // Centralized
+    'Custom Section': {                        // Inline
+      ja: 'カスタムセクション',
+      ko: '커스텀 섹션'
+    }
+  }
+})
+```
+
+## Validation
+
+### Build-Time Validation
+
+Run validation to check translation completeness:
+
+```bash
+pnpm run validate-translations
+```
+
+This checks:
+- All navigation keys have translations
+- No missing `ja` or `ko` translations
+- Consistency across products
+
+### Manual Testing
+
+Test translations in browser:
+- English: `http://localhost:3000/product`
+- Japanese: `http://localhost:3000/ja/product`
+- Korean: `http://localhost:3000/ko/product`
+
+## Best Practices
+
+### ✅ DO
+
+- Use centralized keys for common terms (Overview, FAQ, etc.)
+- Use inline translations for product-specific terms
+- Add helpful comments in product files
+- Test translations in browser
+- Document unique translation choices
+
+### ❌ DON'T
+
+- Duplicate centralized translations as inline
+- Mix language codes (use `ja`, not `jp`)
+- Leave untranslated (always provide `ja` and `ko`)
+- Change centralized translations without considering impact
+- Use machine translation without native speaker review
+
+## Examples
+
+### DAS API (Fully Centralized)
+
+All terms are common and centralized:
+
+```javascript
+// src/components/products/das-api/index.js
+localizedNavigation: buildProductTranslations({
+  productKey: 'dasApi',
+  linkKeys: {
+    'Get Asset': 'links.getAsset',
+    'Search Assets': 'links.searchAssets'
+    // All use centralized keys
+  }
+})
+```
+
+### Bubblegum v2 (Hybrid)
+
+Mix of centralized and inline:
+
+```javascript
+// src/components/products/bubblegum-v2/index.js
+localizedNavigation: buildProductTranslations({
+  productKey: 'bubblegumV2',
+  linkKeys: {
+    // Centralized
+    'Overview': 'links.overview',
+    'FAQ': 'links.faq',
+
+    // Inline (product-specific)
+    'Creating Bubblegum Trees': {
+      ja: 'Bubblegumツリーの作成',
+      ko: 'Bubblegum 트리 생성'
+    }
+  }
+})
+```
+
+## Troubleshooting
+
+### Missing Translation Key Error
+
+**Problem**: Build fails with "translation key not found"
+
+**Solution**:
+1. Check key name spelling in product file
+2. Verify key exists in `navigation-translations.js`
+3. Add key if missing
+
+### Translation Not Showing
+
+**Problem**: Translation shows English instead of Japanese/Korean
+
+**Solution**:
+1. Check locale is passed correctly (`ja` or `ko`)
+2. Verify translation exists in centralized file or inline object
+3. Clear browser cache
+4. Rebuild: `pnpm run build`
+
+### Inconsistent Translations
+
+**Problem**: Same term translated differently across products
+
+**Solution**:
+1. Identify all occurrences
+2. Add to centralized translations
+3. Update all products to use centralized key
+4. Run validation
+
+## Getting Help
+
+- **Documentation**: `src/config/TRANSLATIONS_REVIEW.md`
+- **Examples**: See `das-api` and `bubblegum-v2` products
+- **Issues**: File bug report with `i18n` label
+
+---
+
+**Last Updated**: 2025-01-22
+**System Version**: 2.0 (Hybrid Approach)

+ 113 - 0
src/config/languages.js

@@ -0,0 +1,113 @@
+/**
+ * Language configuration for i18n and SEO
+ *
+ * This configuration is used for:
+ * - hreflang tags
+ * - Language detection
+ * - URL path generation
+ * - Locale imports
+ */
+
+export const LANGUAGES = {
+  en: {
+    code: 'en',           // ISO 639-1 language code (used in hreflang)
+    urlPath: '',          // URL path prefix (empty for default language)
+    name: 'English',
+    nativeName: 'English',
+    isDefault: true,
+  },
+  ja: {
+    code: 'ja',           // ISO 639-1 language code
+    urlPath: '/ja',       // URL path prefix
+    name: 'Japanese',
+    nativeName: '日本語',
+    isDefault: false,
+  },
+  ko: {
+    code: 'ko',           // ISO 639-1 language code
+    urlPath: '/ko',       // URL path prefix
+    name: 'Korean',
+    nativeName: '한국어',
+    isDefault: false,
+  },
+}
+
+// Helper to get language config by URL path
+export function getLanguageByPath(pathname) {
+  // Check if pathname starts with any language prefix
+  for (const [key, lang] of Object.entries(LANGUAGES)) {
+    if (lang.urlPath && pathname.startsWith(lang.urlPath)) {
+      return lang
+    }
+  }
+
+  // Default to English
+  return LANGUAGES.en
+}
+
+// Helper to get language config by locale code
+export function getLanguageByCode(code) {
+  return LANGUAGES[code] || LANGUAGES.en
+}
+
+// Helper to generate alternate URLs for hreflang
+export function generateAlternateUrls(pathname) {
+  const alternates = []
+
+  // Normalize pathname (remove any existing language prefix)
+  let normalizedPath = pathname
+  for (const lang of Object.values(LANGUAGES)) {
+    if (lang.urlPath && pathname.startsWith(lang.urlPath)) {
+      normalizedPath = pathname.slice(lang.urlPath.length) || '/'
+      break
+    }
+  }
+
+  // Generate alternate URLs for each language
+  for (const lang of Object.values(LANGUAGES)) {
+    const url = lang.urlPath ? `${lang.urlPath}${normalizedPath}` : normalizedPath
+    alternates.push({
+      hreflang: lang.code,
+      url: url,
+    })
+  }
+
+  return alternates
+}
+
+// Get all supported language codes
+export const SUPPORTED_LANGUAGES = Object.keys(LANGUAGES)
+
+// Get default language
+export const DEFAULT_LANGUAGE = Object.values(LANGUAGES).find(lang => lang.isDefault)
+
+/**
+ * Get localized href for a given path and locale
+ *
+ * @param {string} path - The path to localize (can be internal or external URL)
+ * @param {string} locale - The locale code (e.g., 'en', 'ja', 'ko')
+ * @returns {string} The localized path
+ *
+ * @example
+ * getLocalizedHref('/docs', 'en') // '/docs'
+ * getLocalizedHref('/docs', 'ja') // '/ja/docs'
+ * getLocalizedHref('docs', 'ko') // '/ko/docs' (normalized with leading slash)
+ * getLocalizedHref('https://example.com', 'ja') // 'https://example.com' (external URLs unchanged)
+ */
+export function getLocalizedHref(path, locale) {
+  // Handle external URLs - don't add locale prefix
+  if (path.startsWith('http://') || path.startsWith('https://') || path.startsWith('//')) {
+    return path
+  }
+
+  // Normalize internal paths to ensure leading slash
+  const normalizedPath = path.startsWith('/') ? path : `/${path}`
+
+  // For English (default locale), return normalized path without locale prefix
+  if (locale === 'en') {
+    return normalizedPath
+  }
+
+  // For other locales, prefix with /{locale}
+  return `/${locale}${normalizedPath}`
+}

+ 523 - 0
src/config/navigation-translations.js

@@ -0,0 +1,523 @@
+/**
+ * Centralized Navigation Translations
+ *
+ * This file contains all common navigation translations used across products.
+ * It eliminates duplication and provides a single source of truth for navigation text.
+ *
+ * Structure:
+ * - Common section titles
+ * - Common link titles
+ * - Product-specific headlines and descriptions
+ *
+ * Usage:
+ * - Use translation keys (e.g., 'nav.overview') instead of literal strings
+ * - Call resolveNavigationTranslation(key, locale) to get translated text
+ *
+ * IMPORTANT: Translations marked with [REVIEW] need native-speaker verification
+ */
+
+export const NAVIGATION_TRANSLATIONS = {
+  // Common Navigation Sections
+  sections: {
+    introduction: {
+      en: 'Introduction',
+      ja: '紹介',
+      ko: '소개'
+    },
+    sdk: {
+      en: 'SDK',
+      ja: 'SDK',
+      ko: 'SDK'
+    },
+    features: {
+      en: 'Features',
+      ja: '機能',
+      ko: '기능'
+    },
+    advanced: {
+      en: 'Advanced',
+      ja: '高度',
+      ko: '고급'
+    },
+    guides: {
+      en: 'Guides',
+      ja: 'ガイド',
+      ko: '가이드'
+    },
+    references: {
+      en: 'References',
+      ja: 'リファレンス',
+      ko: '참조'
+    },
+    methods: {
+      en: 'Methods & Playground',
+      ja: 'メソッドとプレイグラウンド',
+      ko: '메서드와 플레이그라운드'
+    },
+    coreExtension: {
+      en: 'Core Extension SDK',
+      ja: 'Core拡張SDK',
+      ko: 'Core 확장 SDK'
+    }
+  },
+
+  // Common Navigation Links
+  links: {
+    overview: {
+      en: 'Overview',
+      ja: '概要',
+      ko: '개요'
+    },
+    gettingStarted: {
+      en: 'Getting Started',
+      ja: 'はじめに',
+      ko: '시작하기'
+    },
+    faq: {
+      en: 'FAQ',
+      ja: 'よくある質問',
+      ko: '자주 묻는 질문'
+    },
+    javascript: {
+      en: 'JavaScript',
+      ja: 'JavaScript',
+      ko: 'JavaScript'
+    },
+    rust: {
+      en: 'Rust',
+      ja: 'Rust',
+      ko: 'Rust'
+    },
+    guidesOverview: {
+      en: 'Guides Overview',
+      ja: 'ガイド概要',
+      ko: '가이드 개요'
+    },
+    // DAS API specific
+    dasApiProviders: {
+      en: 'DAS API RPC Providers',
+      ja: 'DAS API RPCプロバイダー',
+      ko: 'DAS API RPC 제공업체'
+    },
+    displayOptions: {
+      en: 'Display Options',
+      ja: '表示オプション',
+      ko: '표시 옵션'
+    },
+    methodOverview: {
+      en: 'Method Overview',
+      ja: 'メソッド概要',
+      ko: '메서드 개요'
+    },
+    getAsset: {
+      en: 'Get Asset',
+      ja: 'アセットの取得',
+      ko: '에셋 가져오기'
+    },
+    getAssets: {
+      en: 'Get Assets',
+      ja: '複数アセットの取得',
+      ko: '여러 에셋 가져오기'
+    },
+    getAssetProof: {
+      en: 'Get Asset Proof',
+      ja: 'アセット証明の取得',
+      ko: '에셋 증명 가져오기'
+    },
+    getAssetProofs: {
+      en: 'Get Asset Proofs',
+      ja: '複数アセット証明の取得',
+      ko: '여러 에셋 증명 가져오기'
+    },
+    getAssetSignatures: {
+      en: 'Get Asset Signatures',
+      ja: 'アセット署名の取得',
+      ko: '에셋 서명 가져오기'
+    },
+    getAssetsByAuthority: {
+      en: 'Get Assets By Authority',
+      ja: '権限別アセットの取得',
+      ko: '권한별 에셋 가져오기'
+    },
+    getAssetsByCreator: {
+      en: 'Get Assets By Creator',
+      ja: '作成者別アセットの取得',
+      ko: '생성자별 에셋 가져오기'
+    },
+    getAssetsByGroup: {
+      en: 'Get Assets By Group',
+      ja: 'グループ別アセットの取得',
+      ko: '그룹별 에셋 가져오기'
+    },
+    getAssetsByOwner: {
+      en: 'Get Assets By Owner',
+      ja: '所有者別アセットの取得',
+      ko: '소유자별 에셋 가져오기'
+    },
+    getNftEditions: {
+      en: 'Get NFT Editions',
+      ja: 'NFTエディションの取得',
+      ko: 'NFT 에디션 가져오기'
+    },
+    getTokenAccounts: {
+      en: 'Get Token Accounts',
+      ja: 'トークンアカウントの取得',
+      ko: '토큰 계정 가져오기'
+    },
+    searchAssets: {
+      en: 'Search Assets',
+      ja: 'アセットの検索',
+      ko: '에셋 검색'
+    },
+    pagination: {
+      en: 'Pagination',
+      ja: 'ページネーション',
+      ko: '페이지네이션'
+    },
+    getCollectionNfts: {
+      en: 'Get Collection NFTs',
+      ja: 'コレクションNFTの取得',
+      ko: '컬렉션 NFT 가져오기'
+    },
+    getNftsByOwner: {
+      en: 'Get NFTs by Owner',
+      ja: '所有者別NFTの取得',
+      ko: '소유자별 NFT 가져오기'
+    },
+    getWalletTokens: {
+      en: 'Get Wallet Tokens',
+      ja: 'ウォレットトークンの取得',
+      ko: '지갑 토큰 가져오기'
+    },
+    getFungibleAssets: {
+      en: 'Get Fungible Assets',
+      ja: 'ファンジブルアセットの取得',
+      ko: '대체 가능 에셋 가져오기'
+    },
+    searchByCriteria: {
+      en: 'Search by Criteria',
+      ja: '条件による検索',
+      ko: '조건별 검색'
+    },
+    ownerAndCollection: {
+      en: 'Owner and Collection',
+      ja: '所有者とコレクション',
+      ko: '소유자와 컬렉션'
+    },
+    findCompressedNfts: {
+      en: 'Find Compressed NFTs',
+      ja: '圧縮NFTの検索',
+      ko: '압축 NFT 찾기'
+    },
+    collectionStatistics: {
+      en: 'Collection Statistics',
+      ja: 'コレクション統計',
+      ko: '컬렉션 통계'
+    },
+    findTokenHolders: {
+      en: 'Find Token Holders',
+      ja: 'トークン保有者の検索',
+      ko: '토큰 보유자 찾기'
+    },
+    extensionOverview: {
+      en: 'Extension Overview',
+      ja: '拡張機能の概要',
+      ko: '확장 개요'
+    },
+    getCoreAsset: {
+      en: 'Get Core Asset',
+      ja: 'Coreアセットの取得',
+      ko: 'Core 에셋 가져오기'
+    },
+    getCoreCollection: {
+      en: 'Get Core Collection',
+      ja: 'Coreコレクションの取得',
+      ko: 'Core 컬렉션 가져오기'
+    },
+    getCoreAssetsByAuthority: {
+      en: 'Get Core Assets By Authority',
+      ja: '権限別Coreアセットの取得',
+      ko: '권한별 Core 에셋 가져오기'
+    },
+    getCoreAssetsByCollection: {
+      en: 'Get Core Assets By Collection',
+      ja: 'コレクション別Coreアセットの取得',
+      ko: '컬렉션별 Core 에셋 가져오기'
+    },
+    getCoreAssetsByOwner: {
+      en: 'Get Core Assets By Owner',
+      ja: '所有者別Coreアセットの取得',
+      ko: '소유자별 Core 에셋 가져오기'
+    },
+    searchCoreAssets: {
+      en: 'Search Core Assets',
+      ja: 'Coreアセットの検索',
+      ko: 'Core 에셋 검색'
+    },
+    searchCoreCollections: {
+      en: 'Search Core Collections',
+      ja: 'Coreコレクションの検索',
+      ko: 'Core 컬렉션 검색'
+    },
+    pluginDerivation: {
+      en: 'Plugin Derivation',
+      ja: 'プラグインの導出',
+      ko: '플러그인 파생'
+    },
+    typeConversion: {
+      en: 'Type Conversion',
+      ja: '型変換',
+      ko: '타입 변환'
+    },
+    // Bubblegum specific
+    metaplexDasApiRpcs: {
+      en: 'Metaplex DAS API RPCs',
+      ja: 'Metaplex DAS API RPC',
+      ko: 'Metaplex DAS API RPC'
+    },
+    creatingBubblegumTrees: {
+      en: 'Creating Bubblegum Trees',
+      ja: 'Bubblegumツリーの作成',
+      ko: 'Bubblegum 트리 생성'
+    },
+    mintingCnfts: {
+      en: 'Minting Compressed NFTs (cNFTs)',
+      ja: '圧縮NFT(cNFT)のミント',
+      ko: '압축 NFT(cNFT) 민팅'
+    },
+    fetchingCnfts: {
+      en: 'Fetching cNFTs',
+      ja: 'cNFTの取得',
+      ko: 'cNFT 가져오기'
+    },
+    transferringCnfts: {
+      en: 'Transferring cNFTs',
+      ja: 'cNFTの転送',
+      ko: 'cNFT 전송'
+    },
+    freezeAndThawCnfts: {
+      en: 'Freeze and Thaw cNFTs',
+      ja: 'cNFTの凍結と解凍',
+      ko: 'cNFT 동결 및 해제'
+    },
+    updatingCnfts: {
+      en: 'Updating cNFTs',
+      ja: 'cNFTの更新',
+      ko: 'cNFT 업데이트'
+    },
+    burningCnfts: {
+      en: 'Burning cNFTs',
+      ja: 'cNFTのバーン',
+      ko: 'cNFT 소각'
+    },
+    delegatingCnfts: {
+      en: 'Delegating cNFTs',
+      ja: 'cNFTのデリゲート',
+      ko: 'cNFT 위임'
+    },
+    delegatingTrees: {
+      en: 'Delegating Trees',
+      ja: 'ツリーのデリゲート',
+      ko: '트리 위임'
+    },
+    collections: {
+      en: 'Collections',
+      ja: 'コレクション',
+      ko: '컬렉션'
+    },
+    verifyingCreators: {
+      en: 'Verifying Creators',
+      ja: '作成者の検証',
+      ko: '크리에이터 검증'
+    },
+    concurrentMerkleTrees: {
+      en: 'Concurrent Merkle Trees',
+      ja: '同時マークルツリー',
+      ko: '동시 머클 트리'
+    },
+    storingAndIndexingNftData: {
+      en: 'Storing and Indexing NFT Data',
+      ja: 'NFTデータの保存とインデックス化',
+      ko: 'NFT 데이터 저장 및 인덱싱'
+    },
+    hashingNftData: {
+      en: 'Hashing NFT Data',
+      ja: 'NFTデータのハッシュ化',
+      ko: 'NFT 데이터 해싱'
+    },
+    merkleTreeCanopy: {
+      en: 'Merkle Tree Canopy',
+      ja: 'マークルツリーキャノピー',
+      ko: '머클 트리 캐노피'
+    }
+  },
+
+  // Product-specific headlines and descriptions
+  products: {
+    dasApi: {
+      headline: {
+        en: 'Fetch Digital Asset Data',
+        ja: 'デジタルアセットデータの取得',
+        ko: '디지털 에셋 데이터 가져오기'
+      },
+      description: {
+        en: 'A DAS API Client to access Digital Asset data on chain',
+        ja: 'オンチェーンデジタルアセットデータにアクセスするDAS APIクライアント',
+        ko: '온체인 디지털 에셋 데이터에 접근하는 DAS API 클라이언트'
+      }
+    },
+    bubblegumV2: {
+      headline: {
+        en: 'Improved Compressed NFTs',
+        ja: '改良された圧縮NFT',
+        ko: '개선된 압축 NFT'
+      },
+      description: {
+        en: 'NFTs that scale to new orders of magnitude.',
+        ja: '新たな桁のスケールを実現するNFT。',
+        ko: '새로운 차원의 확장성을 제공하는 NFT입니다.'
+      }
+    }
+  }
+}
+
+/**
+ * Resolve a translation key to its localized value
+ *
+ * @param {string} key - Translation key (e.g., 'nav.sections.introduction')
+ * @param {string} locale - Target locale (en, ja, ko)
+ * @param {string} fallback - Fallback text if translation not found
+ * @returns {string} Translated text or fallback
+ */
+export function resolveNavigationTranslation(key, locale = 'en', fallback = '') {
+  const parts = key.split('.')
+  let current = NAVIGATION_TRANSLATIONS
+
+  for (const part of parts) {
+    if (current && typeof current === 'object' && part in current) {
+      current = current[part]
+    } else {
+      // Key not found, return fallback or key itself
+      return fallback || key
+    }
+  }
+
+  // If we have a translation object, get the locale value
+  if (current && typeof current === 'object' && locale in current) {
+    return current[locale]
+  }
+
+  // Fallback to English if locale not found
+  if (current && typeof current === 'object' && 'en' in current) {
+    return current.en
+  }
+
+  return fallback || key
+}
+
+/**
+ * Build translations object for a product using keys OR inline translations
+ *
+ * This helper generates the localizedNavigation structure from translation keys.
+ * Supports both centralized keys and product-specific inline translations.
+ *
+ * @param {Object} config - Configuration object
+ * @param {string} config.productKey - Product key for headline/description (uses centralized)
+ * @param {Object} config.headlineTranslations - Inline translations for headline { ja: '...', ko: '...' }
+ * @param {Object} config.descriptionTranslations - Inline translations for description { ja: '...', ko: '...' }
+ * @param {Object} config.sectionKeys - Map of section titles to translation keys or inline translations
+ * @param {Object} config.linkKeys - Map of link titles to translation keys or inline translations
+ * @returns {Object} Complete localizedNavigation object for ja, ko
+ *
+ * @example
+ * // Using centralized keys (preferred for common terms)
+ * buildProductTranslations({
+ *   linkKeys: {
+ *     'Overview': 'links.overview'  // Uses centralized translation
+ *   }
+ * })
+ *
+ * @example
+ * // Using inline translations (preferred for product-specific terms)
+ * buildProductTranslations({
+ *   linkKeys: {
+ *     'Custom Feature': { ja: 'カスタム機能', ko: '커스텀 기능' }
+ *   }
+ * })
+ *
+ * @example
+ * // Mixed approach (best of both worlds)
+ * buildProductTranslations({
+ *   linkKeys: {
+ *     'Overview': 'links.overview',  // Common term - use centralized
+ *     'Rare Product Feature': { ja: 'レア機能', ko: '희귀 기능' }  // Specific - inline
+ *   }
+ * })
+ */
+export function buildProductTranslations({
+  productKey,
+  headlineTranslations,
+  descriptionTranslations,
+  sectionKeys = {},
+  linkKeys = {}
+}) {
+  const locales = ['ja', 'ko']
+  const translations = {}
+
+  locales.forEach(locale => {
+    translations[locale] = {}
+
+    // Priority 1: Use inline translations if provided (product-specific)
+    if (headlineTranslations && headlineTranslations[locale]) {
+      translations[locale].headline = headlineTranslations[locale]
+    }
+    // Priority 2: Fall back to centralized productKey
+    else if (productKey && NAVIGATION_TRANSLATIONS.products[productKey]) {
+      translations[locale].headline = NAVIGATION_TRANSLATIONS.products[productKey].headline[locale]
+    }
+
+    // Same for description
+    if (descriptionTranslations && descriptionTranslations[locale]) {
+      translations[locale].description = descriptionTranslations[locale]
+    }
+    else if (productKey && NAVIGATION_TRANSLATIONS.products[productKey]) {
+      translations[locale].description = NAVIGATION_TRANSLATIONS.products[productKey].description[locale]
+    }
+
+    // Build sections translations
+    if (Object.keys(sectionKeys).length > 0) {
+      translations[locale].sections = {}
+      Object.entries(sectionKeys).forEach(([title, keyOrTranslations]) => {
+        // Check if it's an inline translation object { ja: '...', ko: '...' }
+        if (typeof keyOrTranslations === 'object' && keyOrTranslations[locale]) {
+          translations[locale].sections[title] = keyOrTranslations[locale]
+        } else if (typeof keyOrTranslations === 'string') {
+          // It's a key reference - resolve from centralized translations
+          translations[locale].sections[title] = resolveNavigationTranslation(keyOrTranslations, locale, title)
+        } else {
+          // Fallback to title itself
+          translations[locale].sections[title] = title
+        }
+      })
+    }
+
+    // Build links translations
+    if (Object.keys(linkKeys).length > 0) {
+      translations[locale].links = {}
+      Object.entries(linkKeys).forEach(([title, keyOrTranslations]) => {
+        // Check if it's an inline translation object { ja: '...', ko: '...' }
+        if (typeof keyOrTranslations === 'object' && keyOrTranslations[locale]) {
+          translations[locale].links[title] = keyOrTranslations[locale]
+        } else if (typeof keyOrTranslations === 'string') {
+          // It's a key reference - resolve from centralized translations
+          translations[locale].links[title] = resolveNavigationTranslation(keyOrTranslations, locale, title)
+        } else {
+          // Fallback to title itself
+          translations[locale].links[title] = title
+        }
+      })
+    }
+  })
+
+  return translations
+}

+ 83 - 0
src/contexts/LocaleContext.js

@@ -0,0 +1,83 @@
+import { createContext, useContext, useMemo, useEffect, useState } from 'react'
+import { useRouter } from 'next/router'
+
+import enMessages from '@/locales/en.json'
+import jaMessages from '@/locales/ja.json'
+import koMessages from '@/locales/ko.json'
+
+const LocaleContext = createContext()
+
+const messages = {
+  en: enMessages,
+  ja: jaMessages,
+  ko: koMessages,
+}
+
+export function LocaleProvider({ children }) {
+  const { pathname, asPath } = useRouter()
+  const [clientPath, setClientPath] = useState('')
+
+  // Get the actual browser URL on client-side
+  useEffect(() => {
+    if (typeof window !== 'undefined') {
+      setClientPath(window.location.pathname)
+    }
+  }, [])
+
+  // Detect locale from pathname, with fallback to client path for 404 pages
+  const locale = useMemo(() => {
+    let pathToCheck = pathname
+
+    // For 404 pages, use different strategies
+    if (pathname === '/404') {
+      pathToCheck = asPath || clientPath
+    }
+
+    if (pathToCheck.startsWith('/ja')) return 'ja'
+    if (pathToCheck.startsWith('/ko')) return 'ko'
+    return 'en' // Default to English for root paths
+  }, [pathname, asPath, clientPath])
+
+  // Get messages for current locale, fallback to English
+  const currentMessages = messages[locale] || messages.en
+
+  const value = useMemo(() => ({
+    locale,
+    messages: currentMessages,
+    t: (key, fallback = key) => {
+      const keys = key.split('.')
+      let value = currentMessages
+      
+      for (const k of keys) {
+        value = value?.[k]
+        if (value === undefined) break
+      }
+      
+      return value || fallback
+    }
+  }), [locale, currentMessages])
+
+  return (
+    <LocaleContext.Provider value={value}>
+      {children}
+    </LocaleContext.Provider>
+  )
+}
+
+export function useLocale() {
+  const context = useContext(LocaleContext)
+  if (!context) {
+    throw new Error('useLocale must be used within a LocaleProvider')
+  }
+  return context
+}
+
+// Helper hook for translations
+export function useTranslations(namespace = '') {
+  const { t } = useLocale()
+  
+  return (key, fallback) => {
+    const fullKey = namespace ? `${namespace}.${key}` : key
+    return t(fullKey, fallback)
+  }
+}

+ 12 - 0
src/i18n.js

@@ -0,0 +1,12 @@
+import { getRequestConfig } from 'next-intl/server';
+import { notFound } from 'next/navigation';
+import { SUPPORTED_LANGUAGES } from './config/languages';
+
+export default getRequestConfig(async ({ locale }) => {
+  // Validate that the incoming `locale` parameter is valid
+  if (!SUPPORTED_LANGUAGES.includes(locale)) notFound();
+
+  return {
+    messages: (await import(`./locales/${locale}.json`)).default
+  };
+});

+ 63 - 0
src/locales/en.json

@@ -0,0 +1,63 @@
+{
+  "navigation": {
+    "core": "Core",
+    "candyMachine": "Candy Machine", 
+    "coreCandyMachine": "Core Candy Machine",
+    "tokenMetadata": "Token Metadata",
+    "bubblegum": "Bubblegum",
+    "bubblegumV2": "Bubblegum V2",
+    "mplHybrid": "MPL Hybrid",
+    "tokenAuthRules": "Token Auth Rules",
+    "fusion": "Fusion",
+    "hydra": "Hydra",
+    "inscription": "Inscription",
+    "dasApi": "DAS API",
+    "umi": "UMI",
+    "cli": "CLI",
+    "shank": "Shank",
+    "amman": "Amman",
+    "sugar": "Sugar",
+    "aura": "Aura",
+    "legacyDocumentation": "Legacy Documentation",
+    "guides": "Guides"
+  },
+  "sections": {
+    "documentation": "Documentation",
+    "guides": "Guides", 
+    "references": "References",
+    "changelog": "Changelog"
+  },
+  "ui": {
+    "getStarted": "Get Started",
+    "learnMore": "Learn More",
+    "readDocs": "Read Docs",
+    "viewGuides": "View Guides",
+    "github": "GitHub",
+    "search": "Search",
+    "searchPlaceholder": "Search documentation...",
+    "onThisPage": "On this page",
+    "previousPage": "Previous page",
+    "nextPage": "Next page"
+  },
+  "header": {
+    "mpl": "MPL",
+    "devTools": "Dev Tools",
+    "guides": "Guides",
+    "aura": "Aura"
+  },
+  "meta": {
+    "defaultTitle": "Metaplex Documentation",
+    "defaultDescription": "The complete guide to building on Metaplex.",
+    "coreDescription": "Next generation Solana NFT standard.",
+    "candyMachineDescription": "The easiest way to create and manage NFT drops on Solana.",
+    "umiDescription": "A Solana framework for JavaScript clients.",
+    "guidesDescription": "Step-by-step tutorials to help you build on Metaplex."
+  },
+  "404": {
+    "title": "404 - Page Not Found",
+    "metaTitle": "404 - Page Not Found | Metaplex Developer Hub",
+    "description": "We couldn't find this page in the Metaplex Developer Hub.",
+    "message": "We couldn't find this page in the Metaplex Developer Hub.",
+    "callToAction": "Select a product below to get started."
+  }
+}

+ 63 - 0
src/locales/ja.json

@@ -0,0 +1,63 @@
+{
+  "navigation": {
+    "core": "Core",
+    "candyMachine": "Candy Machine", 
+    "coreCandyMachine": "Core Candy Machine",
+    "tokenMetadata": "Token Metadata",
+    "bubblegum": "Bubblegum",
+    "bubblegumV2": "Bubblegum V2",
+    "mplHybrid": "MPL Hybrid",
+    "tokenAuthRules": "Token Auth Rules",
+    "fusion": "Fusion",
+    "hydra": "Hydra",
+    "inscription": "Inscription",
+    "dasApi": "DAS API",
+    "umi": "UMI",
+    "cli": "CLI",
+    "shank": "Shank",
+    "amman": "Amman",
+    "sugar": "Sugar",
+    "aura": "Aura",
+    "legacyDocumentation": "レガシードキュメント",
+    "guides": "ガイド"
+  },
+  "sections": {
+    "documentation": "ドキュメント",
+    "guides": "ガイド", 
+    "references": "リファレンス",
+    "changelog": "変更履歴"
+  },
+  "ui": {
+    "getStarted": "はじめる",
+    "learnMore": "詳しく見る",
+    "readDocs": "ドキュメントを読む",
+    "viewGuides": "ガイドを見る",
+    "github": "GitHub",
+    "search": "検索",
+    "searchPlaceholder": "ドキュメントを検索...",
+    "onThisPage": "このページの内容",
+    "previousPage": "前のページ",
+    "nextPage": "次のページ"
+  },
+  "header": {
+    "mpl": "MPL",
+    "devTools": "開発ツール",
+    "guides": "ガイド",
+    "aura": "Aura"
+  },
+  "meta": {
+    "defaultTitle": "Metaplexドキュメント",
+    "defaultDescription": "Metaplexでの開発に関する完全ガイド。",
+    "coreDescription": "次世代Solana NFT標準。",
+    "candyMachineDescription": "SolanaでNFTドロップを作成・管理する最も簡単な方法。",
+    "umiDescription": "JavaScriptクライアント向けのSolanaフレームワーク。",
+    "guidesDescription": "Metaplexでの開発をサポートするステップバイステップチュートリアル。"
+  },
+  "404": {
+    "title": "404 - ページが見つかりません",
+    "metaTitle": "404 - ページが見つかりません | Metaplex Developer Hub",
+    "description": "Metaplex Developer Hubでこのページが見つかりませんでした。",
+    "message": "Metaplex Developer Hubでこのページが見つかりませんでした。",
+    "callToAction": "下記から製品を選択して始めましょう。"
+  }
+}

+ 63 - 0
src/locales/ko.json

@@ -0,0 +1,63 @@
+{
+  "navigation": {
+    "core": "Core",
+    "candyMachine": "Candy Machine", 
+    "coreCandyMachine": "Core Candy Machine",
+    "tokenMetadata": "Token Metadata",
+    "bubblegum": "Bubblegum",
+    "bubblegumV2": "Bubblegum V2",
+    "mplHybrid": "MPL Hybrid",
+    "tokenAuthRules": "Token Auth Rules",
+    "fusion": "Fusion",
+    "hydra": "Hydra",
+    "inscription": "Inscription",
+    "dasApi": "DAS API",
+    "umi": "UMI",
+    "cli": "CLI",
+    "shank": "Shank",
+    "amman": "Amman",
+    "sugar": "Sugar",
+    "aura": "Aura",
+    "legacyDocumentation": "레거시 문서",
+    "guides": "가이드"
+  },
+  "sections": {
+    "documentation": "문서",
+    "guides": "가이드", 
+    "references": "참조",
+    "changelog": "변경 로그"
+  },
+  "ui": {
+    "getStarted": "시작하기",
+    "learnMore": "자세히 알아보기",
+    "readDocs": "문서 읽기",
+    "viewGuides": "가이드 보기",
+    "github": "GitHub",
+    "search": "검색",
+    "searchPlaceholder": "문서 검색...",
+    "onThisPage": "이 페이지에서",
+    "previousPage": "이전 페이지",
+    "nextPage": "다음 페이지"
+  },
+  "header": {
+    "mpl": "MPL",
+    "devTools": "개발 도구",
+    "guides": "가이드",
+    "aura": "Aura"
+  },
+  "meta": {
+    "defaultTitle": "Metaplex 개발자 허브",
+    "defaultDescription": "Metaplex 기반 개발을 위한 완전한 가이드.",
+    "coreDescription": "차세대 솔라나 NFT 표준.",
+    "candyMachineDescription": "솔라나에서 NFT 드롭을 생성하고 관리하는 가장 쉬운 방법.",
+    "umiDescription": "JavaScript 클라이언트를 위한 솔라나 프레임워크.",
+    "guidesDescription": "Metaplex 기반 개발을 도와주는 단계별 튜토리얼."
+  },
+  "404": {
+    "title": "404 - 페이지를 찾을 수 없습니다",
+    "metaTitle": "404 - 페이지를 찾을 수 없습니다 | Metaplex Developer Hub",
+    "description": "Metaplex Developer Hub에서 이 페이지를 찾을 수 없었습니다.",
+    "message": "Metaplex Developer Hub에서 이 페이지를 찾을 수 없었습니다.",
+    "callToAction": "아래에서 제품을 선택하여 시작하세요."
+  }
+}

+ 1 - 0
src/middleware.js

@@ -59,6 +59,7 @@ const redirectRules = {
 export function middleware(request) {
   const { pathname } = request.nextUrl
 
+  // Handle legacy redirects
   for (const [rootPath, rule] of Object.entries(redirectRules)) {
     if (pathname.startsWith(rootPath)) {
       if (typeof rule === 'string') {

+ 28 - 0
src/pages/404.js

@@ -0,0 +1,28 @@
+import { useLocale } from '@/contexts/LocaleContext'
+import { MarkdocGrid } from '@/components/products/GridAllProducts'
+
+export default function Custom404() {
+  const { t } = useLocale()
+
+  return (
+    <>
+      <p>{t('404.message')}</p>
+      <p>{t('404.callToAction')}</p>
+      <MarkdocGrid />
+    </>
+  )
+}
+
+export async function getStaticProps() {
+  return {
+    props: {
+      markdoc: {
+        frontmatter: {
+          title: 'Page Not Found',
+          metaTitle: 'Page Not Found | Metaplex Developer Hub',
+          description: 'We couldn\'t find this page in the Metaplex Developer Hub.'
+        }
+      }
+    }
+  }
+}

+ 0 - 11
src/pages/404.md

@@ -1,11 +0,0 @@
----
-title: Page Not Found
-metaTitle: Page Not Found | Metaplex Developer Hub
-description: We couldn't find this page in the Metaplex Developer Hub.
----
-
-We couldn't find this page in the Metaplex Developer Hub.
-
-Select a product below to get started.
-
-{% all-product-grid /%}

+ 16 - 24
src/pages/_app.jsx

@@ -1,8 +1,9 @@
-import Head from 'next/head'
 import Script from 'next/script'
 
 import { DialectProvider } from '@/components/DialectContext'
+import { LocaleProvider } from '@/contexts/LocaleContext'
 import { Layout } from '@/components/Layout'
+import { SEOHead } from '@/components/SEOHead'
 import { usePage } from '@/shared/usePage'
 
 import '@/styles/extra.css'
@@ -16,33 +17,24 @@ import { Prism } from 'prism-react-renderer'
 require('prismjs/components/prism-rust')
 
 export default function App({ Component, pageProps }) {
+  return (
+    <LocaleProvider>
+      <AppContent Component={Component} pageProps={pageProps} />
+    </LocaleProvider>
+  )
+}
+
+function AppContent({ Component, pageProps }) {
   const page = usePage(pageProps)
 
   return (
     <>
-      <Head>
-        <title>{page.metaTitle}</title>
-        <meta property="og:title" content={page.metaTitle} />
-        <meta name="twitter:title" content={page.metaTitle} />
-        <meta name="twitter:card" content="summary_large_image" />
-        <meta property="twitter:domain" content="developers.metaplex.com" />
-        <meta
-          property="og:image"
-          content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg"
-        />
-        <meta
-          name="twitter:image"
-          content="https://developers.metaplex.com/assets/social/dev-hub-preview.jpg"
-        />
-
-        {page.description && (
-          <>
-            <meta name="description" content={page.description} />
-            <meta property="og:description" content={page.description} />
-            <meta name="twitter:description" content={page.description} />
-          </>
-        )}
-      </Head>
+      <SEOHead
+        title={page.title}
+        description={page.description}
+        metaTitle={page.metaTitle}
+        locale={page.locale}
+      />
 
       <DialectProvider>
         <Layout page={page}>

+ 27 - 4
src/pages/_document.jsx

@@ -1,4 +1,5 @@
 import { Head, Html, Main, NextScript } from 'next/document'
+import Document from 'next/document'
 
 const themeScript = `
   let isDarkMode = window.matchMedia('(prefers-color-scheme: dark)')
@@ -38,9 +39,28 @@ const themeScript = `
   isDarkMode.addEventListener('change', () => updateThemeWithoutTransitions())
 `
 
-export default function Document() {
-  return (
-    <Html className="antialiased [font-feature-settings:'ss01'] scrollbar" lang="en">
+class MyDocument extends Document {
+  static async getInitialProps(ctx) {
+    const initialProps = await Document.getInitialProps(ctx)
+
+    // Extract locale from the URL path
+    let locale = 'en'
+    const path = ctx.asPath || ctx.req?.url || ''
+
+    if (path.startsWith('/ja')) {
+      locale = 'ja'
+    } else if (path.startsWith('/ko')) {
+      locale = 'ko'
+    }
+
+    return { ...initialProps, locale }
+  }
+
+  render() {
+    const { locale = 'en' } = this.props
+
+    return (
+      <Html className="antialiased [font-feature-settings:'ss01'] scrollbar" lang={locale}>
       <Head>
         <script dangerouslySetInnerHTML={{ __html: themeScript }} />
         <link
@@ -70,5 +90,8 @@ export default function Document() {
         <NextScript />
       </body>
     </Html>
-  )
+    )
+  }
 }
+
+export default MyDocument

+ 48 - 0
src/pages/ja/amman/cli-commands.md

@@ -0,0 +1,48 @@
+---
+title: CLIコマンド
+metaTitle: CLIコマンド | Amman
+description: Metaplex AmmanローカルバリデータツールキットのCLIコマンド。
+---
+
+```sh
+amman [command]
+
+Commands:
+  amman start    solana-test-validatorと、設定された場合はammanリレーや
+                 モックストレージを起動します
+  amman stop     リレーとストレージを停止し、実行中のsolanaテスト
+                 バリデータを終了します
+  amman logs     'solana logs'を起動し、美化機能を通してパイプします
+  amman airdrop  指定されたSolをペイヤーにエアドロップします
+  amman label    アカウントやトランザクションのラベルをammanに追加します
+  amman account  PublicKeyまたはラベルのアカウント情報を取得するか、
+                 すべてのラベル付きアカウントを表示します
+  amman run      すべてのアドレスラベルを展開した後で、指定された
+                 コマンドを実行します
+
+Options:
+  --help     ヘルプを表示                                               [boolean]
+  --version  バージョン番号を表示                                        [boolean]
+```
+
+## コマンドの実行
+
+```sh
+npx amman start <config.js>
+```
+
+`config.js`が提供されない場合、_amman_は現在のディレクトリで`.ammanrc.js`ファイルを探します。
+それも見つからない場合は、デフォルト設定を使用します。
+
+package.jsonスクリプトにAmmanを追加した場合は、お好みのパッケージインストーラーからAmmanをそれぞれ実行できます。
+
+```sh
+// npm
+npm run amman:start
+
+// yarn
+yarn amman:start
+
+// pnpm
+pnpm run amman:start
+```

+ 126 - 0
src/pages/ja/amman/configuration.md

@@ -0,0 +1,126 @@
+---
+title: 設定
+metaTitle: 設定 | Amman
+description: Ammanローカルバリデータツールキットの設定。
+---
+
+実行時、Ammanはプロジェクトルートで設定ファイル`.ammanrc.js`を探します。このファイルが存在しない場合、Ammanはデフォルト設定でロードします。
+
+設定は、以下のプロパティのいずれかを持つ'validator'をエクスポートするJavaScriptモジュールである必要があります:
+
+- **killRunningValidators**: trueの場合、システム上で現在実行中のsolana-test-validatorを終了します。
+- **programs**: テストバリデータにロードする必要があるBPFプログラム
+- **accountsCluster**: リモートアカウントをクローンするデフォルトクラスター
+- **accounts**: テストバリデータにロードするリモートアカウントの配列
+- **jsonRpcUrl**: テストバリデータがJSON RPCリクエストをリッスンするURL
+- **websocketUrl**: RPC WebSocket用
+- **ledgerDir**: solanaテストバリデータが台帳を書き込む場所
+- **resetLedger**: trueの場合、台帳は起動時にジェネシスにリセットされます
+- **verifyFees**: trueの場合、バリデータはトランザクション手数料を請求するまで完全に起動されたとはみなされません
+
+## 設定例
+
+### デフォルトを含むバリデータ/リレー/ストレージ設定
+
+以下は、追加されたプログラムと`relay`および`storage`設定を除いて、すべての値をデフォルトに設定した設定例です。
+
+_amman-explorerリレー_は、_CI_環境で実行されていない限り、バリデータと共に自動的に起動され、既知の_リレーポート_でリレーが既に実行中の場合は、まず終了されます。
+
+_モックストレージ_は、`storage`設定が提供された場合にのみ起動されます。既知の_ストレージポート_でストレージサーバーが既に実行中の場合は、まず終了されます。
+
+```js
+import { LOCALHOST, tmpLedgerDir } from '@metaplex-foundation/amman'
+
+module.exports = {
+  validator: {
+    killRunningValidators: true,
+    programs: [
+      {
+        label: 'Token Metadata Program',
+        programId: programIds.metadata,
+        deployPath: localDeployPath('mpl_token_metadata'),
+      },
+    ],
+    jsonRpcUrl: LOCALHOST,
+    websocketUrl: '',
+    commitment: 'confirmed',
+    ledgerDir: tmpLedgerDir(),
+    resetLedger: true,
+    verifyFees: false,
+    detached: process.env.CI != null,
+  },
+  relay: {
+    enabled: process.env.CI == null,
+    killRunningRelay: true,
+  },
+  storage: {
+    enabled: process.env.CI == null,
+    storageId: 'mock-storage',
+    clearOnStart: true,
+  },
+}
+```
+
+### リモートアカウントとプログラムを含む設定
+
+Ammanは、お好みのクラスターからローカル使用とテスト用にアカウントとプログラムの両方を取得できます。
+
+```js
+module.exports = {
+  validator: {
+    // デフォルトでは、Ammanはaccounts Clusterからアカウントデータを取得します(アカウント単位で上書き可能)
+    accountsCluster: 'https://api.metaplex.solana.com',
+    accounts: [
+      {
+        label: 'Token Metadata Program',
+        accountId: 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s',
+        // executableをtrueとしてマークすると、Ammanは実行可能なデータアカウントも自動的に取得します
+        executable: true,
+      },
+      {
+        label: 'Random other account',
+        accountId: '4VLgNs1jXgdciSidxcaLKfrR9WjATkj6vmTm5yCwNwui',
+        // デフォルトでexecutableはfalseで、設定に含める必要はありません
+        // executable: false,
+
+        // ここでクラスターを提供すると、accountsClusterフィールドを上書きします
+        cluster: 'https://metaplex.devnet.rpcpool.com',
+      },
+    ],
+  },
+}
+```
+
+### テストバリデータ機能の非アクティブ化
+
+_devnet_のような異なるクラスターでは、一部の機能が無効になっています。デフォルトでは、ローカルで実行されるsolana-test-validatorは機能を無効にしないため、提供されるクラスターとは異なる動作をします。
+
+特定のクラスターに対して実行される方法により近いシナリオでテストを実行するために、_matchFeatures_設定プロパティを介してその機能を一致させることができます:
+
+```js
+module.exports = {
+  validator: {
+    ...
+    // 以下は`mainnet-beta`クラスターに対して非アクティブ化された機能を無効にします
+    matchFeatures: 'mainnet-beta',
+  }
+}
+```
+
+機能のセットを明示的に無効にしたい場合は、_deactivateFeatures_プロパティを使用できます:
+
+```js
+module.exports = {
+  validator: {
+    ...
+   deactivateFeatures: ['21AWDosvp3pBamFW91KB35pNoaoZVTM7ess8nr2nt53B'],
+  }
+}
+```
+
+**注意**: 上記のプロパティのうち1つのみを設定できます
+
+#### リソース
+
+- [テストバリデータランタイム機能](https://docs.solana.com/developing/test-validator#appendix-ii-runtime-features)
+- [ランタイム新機能](https://docs.solana.com/developing/programming-model/runtime#new-features)

+ 37 - 0
src/pages/ja/amman/getting-started.md

@@ -0,0 +1,37 @@
+---
+title: はじめに
+metaTitle: はじめに | Amman
+description: Metaplex Ammanローカルバリデータツールキットのインストールとセットアップ。
+---
+
+## 前提条件
+
+Ammanを実行する前に、システムにいくつかのものをインストールする必要があります。
+
+- [Rust](https://www.rust-lang.org/tools/install)
+- [Solana CLI](https://docs.solanalabs.com/cli/install)
+- [NodeJs](https://nodejs.org/en/download)
+
+## インストール
+
+新しいプロジェクトを開始したり、既存のプロジェクトを開いたら、パッケージマネージャーを介してAmmanをインストールできます。
+
+```js
+npm i @metaplex-foundation/amman
+```
+
+## スクリプトに追加(オプション)
+
+使いやすさのために、package.jsonスクリプトにAmmanの実行を追加することをお勧めします。
+
+{% dialect-switcher title="package.json" %}
+{% dialect title="JavaScript" id="js" %}
+
+```js
+"scripts": {
+    ...
+    "amman:start": "npx amman start"
+  },
+```
+{% /dialect %}
+{% /dialect-switcher %}

+ 15 - 0
src/pages/ja/amman/index.md

@@ -0,0 +1,15 @@
+---
+title: 概要
+metaTitle: 概要 | Amman
+description: Ammanローカルバリデーターの概要を説明します。
+---
+
+ローカルで実行されるバリデーター上でSolana SDKライブラリやアプリをテストするための**A** **m** odern **man** datory(現代的で必須の)ツールベルト。 {% .lead %}
+
+{% quick-links %}
+
+{% quick-link title="はじめに" icon="InboxArrowDown" href="/ja/amman/getting-started" description="お好みの言語またはライブラリを見つけて、基本的なプログラムを開始しましょう。" /%}
+
+{% quick-link title="設定" icon="CodeBracketSquare" href="/ja/amman/pre-made-configs" description="試用・修正可能な既成の設定セット。" /%}
+
+{% /quick-links %}

+ 206 - 0
src/pages/ja/amman/pre-made-configs.md

@@ -0,0 +1,206 @@
+---
+title: 事前作成済み設定
+metaTitle: 事前作成済み設定 | Amman
+description: Metaplex Ammanローカルバリデータツールキットからの事前作成済み設定セット。
+---
+
+異なるMetaplexプログラムで動作するAmman設定の基本的な例をいくつか紹介します。ニーズに合わせてこれらのファイルを変更する必要がある場合があります。
+
+## Bubblegum
+
+この設定は、Metaplex Bubblegumでテストと作業を行うために設計されています。
+
+```json
+const { LOCALHOST, tmpLedgerDir } = require("@metaplex-foundation/amman");
+
+module.exports = {
+  validator: {
+    killRunningValidators: true,
+    accountsCluster: "https://api.devnet.solana.com",
+    accounts: [
+       {
+        label: "Bubblegum",
+        accountId: "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY",
+        executable: true,
+      },
+      {
+        label: "Token Metadata Program",
+        accountId: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
+        executable: true,
+      },
+      {
+        label: "Token Auth Rules",
+        accountId: "auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg",
+        executable: true,
+      },
+      {
+        label: "Spl ATA Program",
+        accountId: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
+        executable: true,
+      },
+      {
+        label: "SPL Token Program",
+        accountId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
+        executable: true,
+      },
+      {
+        label: "SPL Account Compression",
+        accountId: "cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK",
+        executable: true
+      },
+      {
+        label: "SPL Noop Program",
+        accountId: "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
+        executable: true
+      },
+
+    ],
+    jsonRpcUrl: LOCALHOST,
+    websocketUrl: "",
+    commitment: "confirmed",
+    ledgerDir: tmpLedgerDir(),
+    resetLedger: true,
+    verifyFees: false,
+    detached: process.env.CI != null,
+  },
+  relay: {
+    enabled: process.env.CI == null,
+    killRunningRelay: true,
+  },
+  storage: {
+    enabled: process.env.CI == null,
+    storageId: "mock-storage",
+    clearOnStart: true,
+  },
+};
+```
+
+## Candy Machine
+
+この設定は、Metaplex Candy Machineでテストと作業を行うために設計されています。
+
+```json
+const { LOCALHOST, tmpLedgerDir } = require("@metaplex-foundation/amman");
+
+module.exports = {
+  validator: {
+    killRunningValidators: true,
+    accountsCluster: "https://api.devnet.solana.com	",
+    accounts: [
+       {
+        label: "Candy Machine v3",
+        accountId: "CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR",
+        executable: true,
+      },
+      {
+        label: "Candy Guard",
+        accountId: "Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g",
+        executable: true,
+      },
+      {
+        label: "Token Metadata Program",
+        accountId: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
+        executable: true,
+      },
+      {
+        label: "Token Auth Rules",
+        accountId: "auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg",
+        executable: true,
+      },
+      {
+        label: "Spl ATA Program",
+        accountId: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
+        executable: true,
+      },
+      {
+        label: "SPL Token Program",
+        accountId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
+        executable: true,
+      },
+    ],
+    jsonRpcUrl: LOCALHOST,
+    websocketUrl: "",
+    commitment: "confirmed",
+    ledgerDir: tmpLedgerDir(),
+    resetLedger: true,
+    verifyFees: false,
+    detached: process.env.CI != null,
+  },
+  relay: {
+    enabled: process.env.CI == null,
+    killRunningRelay: true,
+  },
+  storage: {
+    enabled: process.env.CI == null,
+    storageId: "mock-storage",
+    clearOnStart: true,
+  },
+};
+```
+
+## Core Candy Machine
+
+この設定は、Metaplex Core Candy Machineでテストと作業を行うために設計されています。上記のCandy Machineの例と比較して、異なるCandy MachineプログラムIDとCandy GuardプログラムIDが使用され、MPL-Coreプログラムが追加されています。
+
+```json
+const { LOCALHOST, tmpLedgerDir } = require("@metaplex-foundation/amman");
+
+module.exports = {
+  validator: {
+    killRunningValidators: true,
+    accountsCluster: "https://api.devnet.solana.com",
+    accounts: [
+       {
+        label: "Core Candy Machine",
+        accountId: "CMACYFENjoBMHzapRXyo1JZkVS6EtaDDzkjMrmQLvr4J",
+        executable: true,
+      },
+      {
+        label: "Core Candy Guard",
+        accountId: "CMAGAKJ67e9hRZgfC5SFTbZH8MgEmtqazKXjmkaJjWTJ",
+        executable: true,
+      },
+      {
+        label: "mpl-core",
+        accountId: "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d",
+        executable: true,
+      },      
+      {
+        label: "Token Metadata Program",
+        accountId: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
+        executable: true,
+      },
+      {
+        label: "Token Auth Rules",
+        accountId: "auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg",
+        executable: true,
+      },
+      {
+        label: "Spl ATA Program",
+        accountId: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
+        executable: true,
+      },
+      {
+        label: "SPL Token Program",
+        accountId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
+        executable: true,
+      },
+    ],
+    jsonRpcUrl: LOCALHOST,
+    websocketUrl: "",
+    commitment: "confirmed",
+    ledgerDir: tmpLedgerDir(),
+    resetLedger: true,
+    verifyFees: false,
+    detached: process.env.CI != null,
+  },
+  relay: {
+    enabled: process.env.CI == null,
+    killRunningRelay: true,
+  },
+  storage: {
+    enabled: process.env.CI == null,
+    storageId: "mock-storage",
+    clearOnStart: true,
+  },
+};

+ 13 - 0
src/pages/ja/aura/blockchains/eclipse.md

@@ -0,0 +1,13 @@
+---
+title: Eclipse SVMのAura
+metaTitle: Eclipse SVMのAura | Aura
+description: Eclipse SVMブロックチェーン上のMetaplex Auraについて学びます。
+---
+
+{% callout title="Auraエンドポイント" %}
+
+**以下のノードでEclipse SVM上のAuraをテストできます:**
+- **Devnet**: https://aura-eclipse-devnet.metaplex.com 
+- **Mainnet**: https://aura-eclipse-mainnet.metaplex.com 
+
+{% /callout %}

+ 13 - 0
src/pages/ja/aura/blockchains/solana.md

@@ -0,0 +1,13 @@
+---
+title: SolanaのAura
+metaTitle: SolanaのAura | Aura
+description: Solanaブロックチェーン上のMetaplex Auraについて学びます。
+---
+
+{% callout title="Auraエンドポイント" %}
+
+**以下のノードでSolana上のAuraをテストできます:**
+- **Devnet**: https://aura-devnet.metaplex.com
+- **Mainnet**: https://aura-mainnet.metaplex.com
+
+{% /callout %}

+ 18 - 0
src/pages/ja/aura/faq.md

@@ -0,0 +1,18 @@
+---
+title: FAQ
+metaTitle: FAQ | Aura
+description: Auraに関するよくある質問
+---
+
+## Auraゲートウェイにはどのようにアクセスできますか?
+
+Auraゲートウェイは現在無効になっており、特定のエラーとパフォーマンスの問題に対処するために技術的なレビューが行われています。
+
+## Auraはどのプロトコルをサポートしていますか?
+
+Auraは最新のDASメソッドで以下のプロトコルをサポートしています:
+- MPL Token Metadata(NFT/pNFT)
+- MPL Core(コアアセット/コアコレクション)
+- MPL Bubblegum(cNFT)
+- Fungible SPLトークン
+- SPL Token22

+ 17 - 0
src/pages/ja/aura/index.md

@@ -0,0 +1,17 @@
+---
+title: 概要
+metaTitle: 概要 | Aura
+description: デジタルアセット向けの統合ネットワークであるAuraの概要。
+---
+
+Metaplex Auraは、SolanaとSolana Virtual Machine(SVM)を拡張するオープンソースのデータネットワーク実装です。
+
+分散型アプリケーションは、ブロックチェーンデータの現在の状態を読み取って表示することが困難、コストが高く、低速であるため、中央集権的な代替手段と競合するのに苦労しています。Auraは、SVMとMPLを、アセットデータを高性能でインデックス化し、リアルタイムのデータ可用性を提供するAuraノードの分散型ネットワークで補完することで、これらの問題を解決します。
+
+## Solanaデータの読み取り
+
+ユーザーと開発者は、Solanaネットワーク上での分散型読み取りアクセスの恩恵を受け、デジタルアセットとメディアコンテンツが、圧縮されている場合でも、単一障害点に依存することなく常にアクセス可能であることが保証されます。
+
+## 参考ページ
+
+- [SolanaとSVMデータの読み取り](/ja/aura/reading-solana-and-svm-data): Auraネットワークはアセットデータのデータ可用性と分散型インデックス化を提供し、[Metaplex Digital Asset Standard(DAS)API](/ja/das-api)を使用してクエリできます。

+ 17 - 0
src/pages/ja/aura/reading-solana-and-svm-data.md

@@ -0,0 +1,17 @@
+---
+title: SolanaとSVMデータの読み取り
+metaTitle: SolanaとSVMデータの読み取り | Aura
+description: Metaplex AuraでのSolanaとSVMデータの読み取りについて学びます。
+---
+
+Metaplex Auraデータネットワークは、開発者にSolanaやEclipseなどの他のSVMベースのチェーンのオンチェーン状態への高性能で信頼性があり正確な読み取りアクセスを提供します。
+
+インデクサーとRPCプロバイダーは、データの一貫性とパフォーマンスの維持において、しばしば大きな課題に直面します。これは以下のいくつかの問題によるものです:
+
+- **データの一貫性**: Solanaノードは頻繁に同期から外れ、Geyserプラグインは特にノードの再同期中に更新をスキップすることがあります。これにより、インデクサーが提供するデータに不整合が生じる可能性があります。
+- **ストレージコストの増大**: データ量が継続的に増大するにつれて、インデックスの維持と管理により多くのストレージが必要になり、関連コストが増加します。
+- **ユーザーエクスペリエンス**: 断片化されたデータ可用性は、プロバイダーのロックインを引き起こし、ユーザーが異なるプロトコル間ですべてのデジタルアセットにアクセスするために複数のRPCプロバイダーに依存することを余儀なくされる可能性があります。
+
+## 開発の進捗
+
+Auraとその機能の開発進捗は、GitHubリポジトリ[https://github.com/metaplex-foundation/aura/](https://github.com/metaplex-foundation/aura/)でご確認いただけます。

+ 49 - 0
src/pages/ja/bubblegum-v2/burn-cnfts.md

@@ -0,0 +1,49 @@
+---
+title: 圧縮NFTのバーン
+metaTitle: 圧縮NFTのバーン | Bubblegum V2
+description: Bubblegumで圧縮NFTをバーンする方法を学びます。
+---
+
+**burnV2**命令は、圧縮NFTをバーンし、Bubblegumツリーから永続的に削除するために使用できます。この操作を認証するには、現在の所有者またはデリゲート権限(存在する場合)がトランザクションに署名する必要があります。命令は以下のパラメータを受け入れます:
+
+- **リーフ所有者**、**リーフデリゲート**、または**永続バーンデリゲート**: 圧縮NFTの現在の所有者、そのデリゲート権限(存在する場合)、またはコレクションの永続バーンデリゲート。アセットがコレクションの一部である場合、`coreCollection`パラメータを渡す必要があります。これらのいずれかがトランザクションに署名する必要があります。
+
+この命令はBubblegumツリー上のリーフを置き換えるため、バーンする前に圧縮NFTの整合性を検証するために追加のパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[次のFAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)でドキュメント化されています。幸いなことに、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% callout title="トランザクションサイズ" type="note" %}
+トランザクションサイズエラーが発生した場合は、`getAssetWithProof`で`{ truncateCanopy: true }`の使用を検討してください。詳細については[FAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)を参照してください。
+{% /callout %}
+
+{% callout title="コレクション" type="note" %}
+cNFTがコレクションの一部である場合、`coreCollection`パラメータを渡す必要があります。
+{% /callout %}
+
+{% dialect-switcher title="圧縮NFTのバーン" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, burnV2 } from '@metaplex-foundation/mpl-bubblegum';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await burnV2(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+}).sendAndConfirm(umi)
+```
+
+{% totem-accordion title="デリゲートの使用" %}
+
+```ts
+import { getAssetWithProof, burnV2 } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await burnV2(umi, {
+  ...assetWithProof,
+  leafDelegate: currentLeafDelegate,
+}).sendAndConfirm(umi)
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="永続バーンデリゲートの使用" %}

+ 49 - 0
src/pages/ja/bubblegum-v2/collections.md

@@ -0,0 +1,49 @@
+---
+title: コレクションの検証
+metaTitle: コレクションの検証 | Bubblegum V2
+description: Bubblegumでコレクションの設定、検証、検証解除を行う方法を学びます。
+---
+
+cNFTは、ミント時または後でMPL-Coreコレクションに追加できます。 {% .lead %}
+
+NFTに関するコレクションの概念に馴染みがない場合、これらは他のNFTをグループ化するために使用できる特別な非圧縮NFTです。したがって、**コレクション**のデータは、コレクション全体の名前とブランディングを説明するために使用されます。Bubblegum V2以降、リーフ所有者による相互作用の必要なしに、デリゲートがcNFTを凍結・解凍できるなど、コレクションレベルでの追加機能も可能になります。[MPL-Coreコレクションについてはこちらで詳しく読むことができます](/ja/core/collections)。
+[ここでドキュメント化されている](/ja/bubblegum-v2/mint-cnfts#minting-to-a-collection)**MintV2**命令を使用して、圧縮NFTを直接コレクションにミントすることが可能であることに注意してください。とはいえ、コレクションなしでcNFTをすでにミントしている場合、そのcNFTにコレクションを設定する方法を見てみましょう。「verified」ブール値を持つMetaplex Token Metadataコレクションを使用するBubblegum v1とは異なり、Bubblegum V2はそのブール値を持たないMPL-Coreコレクションを使用します。
+
+MPL-Coreコレクションは[`BubblegumV2`プラグイン](/ja/core/plugins/bubblegum)を含む必要があります。
+
+以下のセクションでは、単一ステップトランザクションでcNFTからコレクションを設定・削除する方法を示します。`coreCollection`と`newCoreCollection`パラメータを追加する際に、単一の命令で両方の操作を行うことも可能です。両方のコレクション権限が同じウォレットでない場合、両方が署名する必要があります。
+
+## 圧縮NFTのコレクションの設定
+**setCollectionV2**命令は、cNFTのコレクションを設定するために使用できます。cNFTからコレクションを削除したり、cNFTのコレクションを変更したりするためにも使用できます。
+
+{% dialect-switcher title="圧縮NFTのコレクションの設定" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  setCollectionV2,
+  MetadataArgsV2Args
+} from '@metaplex-foundation/mpl-bubblegum';
+import {
+  unwrapOption,
+  none,
+} from '@metaplex-foundation/umi';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+
+const collection = unwrapOption(assetWithProof.metadata.collection)
+
+const metadata: MetadataArgsV2Args = {
+  ...assetWithProof.metadata,
+  collection: collection?.key ?? null,
+};
+
+const signature = await setCollectionV2(umi, {
+  ...assetWithProof,
+  newCollectionAuthority: newCollectionUpdateAuthority,
+  metadata,
+  newCoreCollection: newCoreCollection.publicKey,
+}).sendAndConfirm(umi);
+```

+ 60 - 0
src/pages/ja/bubblegum-v2/concurrent-merkle-trees.md

@@ -0,0 +1,60 @@
+---
+title: 同時マークルツリー
+metaTitle: 同時マークルツリー | Bubblegum V2
+description: 同時マークルツリーとBubblegumでの使用方法について詳しく学びます。
+---
+
+## はじめに
+
+マークルツリーは、各リーフノードが何らかのデータを表すハッシュでラベル付けされたツリーデータ構造です。隣接するリーフは一緒にハッシュ化され、結果として得られるハッシュがそれらのリーフの親であるノードのラベルになります。同じレベルのノードは再び一緒にハッシュ化され、結果として得られるハッシュがそれらのノードの親であるノードのラベルになります。このプロセスは、ルートノードに対して単一のハッシュが作成されるまで続きます。この単一のハッシュは、ツリー全体のデータ整合性を暗号学的に表し、マークルルートと呼ばれます。
+
+ほとんどのマークルツリーは二進ツリーですが、そうである必要はありません。Bubblegum圧縮NFT(cNFT)に使用されるマークルツリーは、私たちの図に示されているように二進ツリーです。
+
+{% diagram %}
+
+{% node %}
+{% node #root label="マークルルート" /%}
+{% node label="Hash(ノード 1, ノード 2)" /%}
+{% /node %}
+
+{% node parent="root" y=100 x=-220 %}
+{% node #i-node-1 label="ノード 1" /%}
+{% node label="Hash(ノード 3, ノード 4)" /%}
+{% /node %}
+
+{% node parent="root" y=100 x=220 %}
+{% node #i-node-2 label="ノード 2" /%}
+{% node label="Hash(ノード 5, ノード 6)" /%}
+{% /node %}
+
+{% node parent="i-node-1" y=100 x=-110 %}
+{% node #i-node-3 label="ノード 3" /%}
+{% node label="Hash(リーフ 1, リーフ 2)" /%}
+{% /node %}
+
+{% node parent="i-node-1" y=100 x=110 %}
+{% node #i-node-4 label="ノード 4" /%}
+{% node label="Hash(リーフ 3, リーフ 4)" /%}
+{% /node %}
+
+{% node parent="i-node-2" y=100 x=-110 %}
+{% node #i-node-5 label="ノード 5" /%}
+{% node label="Hash(リーフ 5, リーフ 6)" /%}
+{% /node %}
+
+{% node parent="i-node-2" y=100 x=110 %}
+{% node #i-node-6 label="ノード 6" /%}
+{% node label="Hash(リーフ 7, リーフ 8)" /%}
+{% /node %}
+
+{% node parent="i-node-3" y="100" x="-40" %}
+{% node #leaf-1 label="リーフ 1" /%}
+{% node label="Hash(cNFT 1)" /%}
+{% /node %}
+
+{% node parent="i-node-3" y="100" x="70" %}
+{% node #leaf-2 label="リーフ 2" /%}
+{% node label="Hash(cNFT 2)" /%}
+{% /node %}
+
+{% node parent="i-node-4" y="100" x="-40" %}

+ 50 - 0
src/pages/ja/bubblegum-v2/create-trees.md

@@ -0,0 +1,50 @@
+---
+title: Bubblegumツリーの作成
+metaTitle: Bubblegumツリーの作成 | Bubblegum V2
+description: 圧縮NFTを保持できる新しいマークルツリーの作成と取得方法を学びます。
+---
+
+## はじめに
+
+圧縮NFTのデータはトランザクション内に保存され、オンチェーンアカウントには保存されませんが、マークルツリーとその設定を追跡するためのオンチェーンアカウントが必要です。そのため、圧縮NFTのミントを開始する前に、2つのアカウントを作成する必要があります:
+
+- **マークルツリーアカウント**。このアカウントは、あらゆる種類のデータの真正性を検証するために使用できる一般的なマークルツリーを保持します。これは[SPLアカウント圧縮プログラム](https://spl.solana.com/account-compression)からフォークされた[MPLアカウント圧縮プログラム](https://github.com/metaplex-foundation/mpl-account-compression)によって所有されています。私たちの場合、これを使用して圧縮NFTの真正性を検証します。
+- **TreeConfigV2アカウント**。この2番目のアカウントは、マークルツリーアカウントのアドレスから派生したPDAです。これにより、圧縮NFTに特有のマークルツリーの追加設定(例:ツリー作成者、ミント済みcNFTの数など)を保存できます。
+
+これらの2つのアカウントがあれば、圧縮NFTのミントを開始するために必要なすべてが揃います。関連するツリー設定アカウントを持つマークルツリーアカウントを**Bubblegumツリー**と呼びます。
+
+{% diagram height="h-64 md:h-[200px]" %}
+
+{% node %}
+{% node #merkle-tree label="マークルツリーアカウント" theme="blue" /%}
+{% node label="所有者: アカウント圧縮プログラム" theme="dimmed" /%}
+{% /node %}
+
+{% node #tree-config-pda parent="merkle-tree" x="300" label="PDA" theme="crimson" /%}
+
+{% node parent="tree-config-pda" y="60" %}
+{% node #tree-config label="ツリー設定アカウント" theme="crimson" /%}
+{% node label="所有者: Bubblegumプログラム" theme="dimmed" /%}
+{% /node %}
+
+{% edge from="merkle-tree" to="tree-config-pda" /%}
+{% edge from="tree-config-pda" to="tree-config" /%}
+
+{% /diagram %}
+
+## Bubblegumツリーの作成
+
+これらのアカウントの両方を作成してBubblegumツリーを作成する方法を見てみましょう。幸いなことに、私たちのライブラリはすべてを処理する**ツリー作成**操作を提供することで、このプロセスを簡単にしています。この操作は、さまざまなパラメータ(そのほとんどがオプション)を受け入れ、Bubblegumツリーを私たちのニーズに合わせてカスタマイズできます。最も重要なものは次のとおりです:
+
+- **マークルツリー**: マークルツリーアカウントの作成に使用される新しく生成された署名者。マークルツリーアカウントは、このアドレスでアクセス可能になります。
+- **ツリー作成者**: Bubblegumツリーを管理し、圧縮NFTをミントできるアカウントのアドレス。
+- **最大深度**と**最大バッファサイズ**: **最大深度**パラメータは、マークルツリーが保持できるリーフの最大数、つまり圧縮NFTを計算するために使用されます。この最大値は`2^maxDepth`で計算されます。**最大バッファサイズ**パラメータは、マークルツリーの最小同時実行制限を示します。言い換えると、ツリーで並行して発生できる変更の数を定義します。これらの2つのパラメータは任意に選択することはできず、以下の表に表示されている事前定義された値のセットから選択する必要があります。
+
+以下は、Solanaエコシステム内での互換性のための推奨ツリー設定です。
+
+| cNFTの数 | ツリーの深度 | キャノピーの深度 | 同時実行バッファ | ツリーのコスト | cNFTあたりのコスト |
+| ----------- | ----------- | --------------- | ---------------- | ------------ | ---------------- |
+| 16,384      | 14          | 8               | 64               | 0.3358       | 0.00002550       |
+| 65,536      | 16          | 10              | 64               | 0.7069       | 0.00001579       |
+| 262,144     | 18          | 12              | 64               | 2.1042       | 0.00001303       |
+| 1,048,576   | 20          | 13              | 1024             | 8.5012       | 0.00001311       |

+ 70 - 0
src/pages/ja/bubblegum-v2/delegate-cnfts.md

@@ -0,0 +1,70 @@
+---
+title: 圧縮NFTのデリゲート
+metaTitle: 圧縮NFTのデリゲート | Bubblegum V2
+description: Bubblegumで圧縮NFTをデリゲートする方法を学びます。
+---
+
+圧縮NFTの所有者は、cNFTの所有権を保持したまま、別のアカウントにデリゲートできます。 {% .lead %}
+
+これにより、デリゲートされたアカウント(**デリゲート権限**とも呼ばれます)は、所有者に代わってアクションを実行できます。これらのアクションは以下の通りです:
+
+- [cNFTの転送](/ja/bubblegum-v2/transfer-cnfts):デリゲート権限は転送後にリセット(つまり新しい所有者に設定)されます。
+- [cNFTのバーン](/ja/bubblegum-v2/burn-cnfts)。
+- [cNFTの凍結と解凍](/ja/bubblegum-v2/freeze-cnfts)。
+
+これらのアクションはそれぞれ、デリゲート権限を使用してそれらを実行する方法の例を提供します。通常、**リーフ所有者**アカウントの代わりに**リーフデリゲート**アカウントを署名者として提供するだけです。
+圧縮NFTのデリゲート権限を承認・取り消しする方法を見てみましょう。
+
+## デリゲート権限の承認
+
+デリゲート権限を承認または置き換えるには、所有者が**デリゲート**命令を送信する必要があります。この命令は以下のパラメータを受け入れます:
+
+- **リーフ所有者**:署名者としての圧縮NFTの現在の所有者。デフォルトではトランザクションの支払者です。
+- **前のリーフデリゲート**:前のデリゲート権限(存在する場合)。そうでなければ、これは**リーフ所有者**に設定する必要があります。
+- **新しいリーフデリゲート**:承認する新しいデリゲート権限。
+
+さらに、この命令はBubblegumツリー上のリーフを置き換えるため、圧縮NFTの整合性を検証するためにより多くのパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[次のFAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)でドキュメント化されています。幸いなことに、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTのデリゲート" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true });
+await delegate(umi, {
+  ...assetWithProof,
+  leafOwner,
+  previousLeafDelegate: leafOwner.publicKey,
+  newLeafDelegate: newDelegate,
+}).sendAndConfirm(umi);
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## デリゲート権限の取り消し
+
+既存のデリゲート権限を取り消すには、所有者は単に自分自身を新しいデリゲート権限として設定する必要があります。
+
+{% dialect-switcher title="圧縮NFTのデリゲート権限の取り消し" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await delegate(umi, {
+  ...assetWithProof,
+  leafOwner,
+  previousLeafDelegate: currentDelegate,
+  newLeafDelegate: leafOwner.publicKey,
+}).sendAndConfirm(umi);
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 57 - 0
src/pages/ja/bubblegum-v2/delegate-trees.md

@@ -0,0 +1,57 @@
+---
+title: ツリーのデリゲート
+metaTitle: ツリーのデリゲート | Bubblegum V2
+description: Bubblegumでマークルツリーをデリゲートする方法を学びます。
+---
+
+圧縮NFTの所有者がデリゲート権限を承認できるのと同様に、Bubblegumツリーの作成者も、自分の代わりにアクションを実行する別のアカウントを承認できます。 {% .lead %}
+
+Bubblegumツリーに対してデリゲート権限が承認されると、作成者に代わって[圧縮NFTをミント](/ja/bubblegum-v2/mint-cnfts)できるようになります。これは、誰でも公開ツリーでミントできるため、プライベートツリーにのみ関連することに注意してください。
+
+## ツリーのデリゲート権限の承認
+
+Bubblegumツリーに新しいデリゲート権限を承認するには、その作成者が以下のパラメータを受け入れる**ツリーデリゲート設定**命令を使用できます:
+
+- **マークルツリー**: デリゲートするマークルツリーのアドレス。
+- **ツリー作成者**: 署名者としてのマークルツリーの作成者。
+- **新しいツリーデリゲート**: 承認する新しいデリゲート権限。
+
+{% dialect-switcher title="Bubblegumツリーのデリゲート" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { setTreeDelegate } from '@metaplex-foundation/mpl-bubblegum'
+
+await setTreeDelegate(umi, {
+  merkleTree,
+  treeCreator,
+  newTreeDelegate,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## ツリーのデリゲート権限の取り消し
+
+既存のデリゲート権限を取り消すには、ツリーの作成者は単に自分自身を新しいデリゲート権限として設定する必要があります。
+
+{% dialect-switcher title="Bubblegumツリーのデリゲート権限の取り消し" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { setTreeDelegate } from '@metaplex-foundation/mpl-bubblegum'
+
+await setTreeDelegate(umi, {
+  merkleTree,
+  treeCreator,
+  newTreeDelegate: treeCreator.publicKey,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 60 - 0
src/pages/ja/bubblegum-v2/faq.md

@@ -0,0 +1,60 @@
+---
+title: FAQ
+metaTitle: FAQ | Bubblegum V2
+description: Bubblegumに関するよくある質問。
+---
+
+## Bubblegum V2とは何ですか?
+
+Bubblegum V2は、いくつかの改良と新機能を導入するBubblegumプログラムの新しい反復です。
+これは既知のBubblegumプログラムの一部ですが、命令とデータ構造は異なります。
+Bubblegum V2では、cNFTはMetaplex Token Metadataコレクションの代わりにMPL-Coreコレクションを使用してコレクションにグループ化されます。また、凍結、解凍、ソウルバウンドNFTなどの新機能と以下のような追加機能も導入しています:
+- **凍結・解凍機能**: プロジェクト作成者は、特定のイベント中の転送を防ぐことや権利確定メカニズムの実装など、さまざまなユースケースでアセットをより詳細に制御するためにcNFTを凍結・解凍できるようになりました。
+- **MPL-Coreコレクション統合**: Bubblegum V2 NFTは、トークンメタデータコレクションに限定されることなく、MPL-Coreコレクションに追加できるようになり、より大きなMetaplexエコシステムとの柔軟性と統合を可能にします。
+- **ロイヤリティ強制**: Bubblegum V2は[MPL-Core](https://docs.metaplex.com/ja/core/overview)コレクションを使用しているため、`ProgramDenyList`などを使用してcNFTにロイヤリティを強制することが可能です。
+- **ソウルバウンドNFT**: cNFTをソウルバウンド(転送不可)にすることが可能になり、所有者のウォレットに永続的に紐づけられます。これは資格、出席証明、身元確認などに最適です。コレクションで`PermanentFreezeDelegate`プラグインを有効にする必要があります。
+- **永続転送の許可**: コレクションで`PermanentTransferDelegate`プラグインが有効になっている場合、永続転送デリゲートはリーフ所有者の相互作用なしにcNFTを新しい所有者に転送できます。
+
+## 転送、デリゲート、バーンなどの操作に必要な引数はどのように見つけますか? {% #replace-leaf-instruction-arguments %}
+
+転送、デリゲート、バーンなどのBubblegumツリー内のリーフを置き換える命令を使用するたびに、プログラムは現在のリーフが有効で更新できることを確認するために使用される多くのパラメータを必要とします。これは、圧縮NFTのデータがオンチェーンアカウント内で利用できないため、プログラムがピースを埋めるために**証明**、**リーフインデックス**、**ノンス**などの追加パラメータが必要だからです。
+
+すべての情報は、`getAsset`と`getAssetProof`の両方のRPCメソッドを使用して**Metaplex DAS API**から取得できます。ただし、これらのメソッドからのRPCレスポンスと命令で期待されるパラメータは全く同じではなく、一方から他方への解析は簡単ではありません。
+
+幸いなことに、私たちのSDKは、以下のコード例で見ることができるように、すべての重い作業を行うヘルパーメソッドを提供します。圧縮NFTのアセットIDを受け入れ、バーン、転送、更新などのリーフを置き換える命令に直接注入できる多くのパラメータを返します。
+
+とはいえ、その解析を自分で行う必要がある場合のために、命令で期待されるパラメータとMetaplex DAS APIからそれらを取得する方法の簡単な内訳を以下に示します。ここでは、`getAsset`と`getAssetProof` RPCメソッドの結果が、それぞれ`rpcAsset`と`rpcAssetProof`変数を介してアクセス可能であると仮定します。
+
+- **リーフ所有者**: `rpcAsset.ownership.owner`を介してアクセス可能。
+- **リーフデリゲート**: `rpcAsset.ownership.delegate`を介してアクセス可能で、nullの場合は`rpcAsset.ownership.owner`にデフォルト設定する必要があります。
+- **マークルツリー**: `rpcAsset.compression.tree`または`rpcAssetProof.tree_id`を介してアクセス可能。
+- **ルート**: `rpcAssetProof.root`を介してアクセス可能。
+- **データハッシュ**: `rpcAsset.compression.data_hash`を介してアクセス可能。
+- **作成者ハッシュ**: `rpcAsset.compression.creator_hash`を介してアクセス可能。
+- **ノンス**: `rpcAsset.compression.leaf_id`を介してアクセス可能。
+- **インデックス**: `rpcAssetProof.node_index - 2^max_depth`を介してアクセス可能。ここで`max_depth`はツリーの最大深度で、`rpcAssetProof.proof`配列の長さから推測できます。
+- **証明**: `rpcAssetProof.proof`を介してアクセス可能。
+- **メタデータ**: 現在、`rpcAsset`レスポンス内のさまざまなフィールドから再構築する必要があります。
+
+{% dialect-switcher title="リーフを置き換える命令のパラメータを取得" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+Bubblegum UmiライブラリはPATHの説明に適合する`getAssetWithProof`ヘルパーメソッドを提供します。以下は`transfer`命令を使用した使用例です。この場合、`leafOwner`パラメータをSignerである必要があり、`assetWithProof`は所有者をPublic Keyとして提供するため、オーバーライドしています。
+
+キャノピーサイズによっては、`getAssetWithProof`ヘルパーの`truncateCanopy: true`パラメータを使用することが意味がある場合があります。これによりツリー設定を取得し、不要な証明を切り捨てます。これはトランザクションサイズが大きくなりすぎる場合に役立ちます。
+
+```ts
+import { getAssetWithProof, transfer } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, 
+// {  truncateCanopy: true } // 証明を剪定するためのオプション 
+);
+await transferV2(umi, {
+  ...assetWithProof,
+  leafOwner: leafOwnerA, // 署名者として。
+  newLeafOwner: leafOwnerB.publicKey,
+}).sendAndConfirm(umi);
+
+await transferV2(umi, {
+  ...assetWithProof,

+ 40 - 0
src/pages/ja/bubblegum-v2/fetch-cnfts.md

@@ -0,0 +1,40 @@
+---
+title: 圧縮NFTの取得
+metaTitle: 圧縮NFTの取得 | Bubblegum V2
+description: Bubblegumで圧縮NFTを取得する方法を学びます。
+---
+
+[概要](/ja/bubblegum#read-api)ページで述べたように、圧縮NFTは通常のNFTのようにオンチェーンアカウント内に保存されるのではなく、それらを作成し更新したトランザクションにログされます。 {% .lead %}
+
+そのため、圧縮NFTの取得を容易にするために特別なインデクサーが作成されました。このインデックス化されたデータは、**Metaplex DAS API**と呼ぶSolana RPCメソッドの拡張を通じて利用できます。実際、DAS APIは任意の**デジタルアセット**を取得できます。これは圧縮NFT、通常のNFT、またはFungibleアセットでもかまいません。
+
+すべてのRPCがDAS APIをサポートしているわけではないため、圧縮NFTを扱う予定がある場合は、RPCプロバイダーを慎重に選択する必要があります。Metaplex DAS APIをサポートするすべてのRPCのリストを[専用ページ](/ja/rpc-providers)で維持していることに注意してください。
+
+このページでは、Metaplex DAS APIを使用して圧縮NFTを取得する方法を学習します。
+
+## Metaplex DAS API SDKのインストール
+
+Metaplex DAS APIをサポートするRPCプロバイダーを選択したら、特別なRPCメソッドを送信して圧縮NFTを取得できます。ただし、私たちのSDKは、ヘルパーメソッドを提供することでDAS APIを開始するより便利な方法を提供します。SDKを使用してMetaplex DAS APIを開始するには、以下の手順に従ってください。
+
+{% totem %}
+
+{% dialect-switcher title="Metaplex DAS APIを開始" %}
+{% dialect title="JavaScript" id="js" %}
+
+{% totem-prose %}
+Umiを使用する場合、Metaplex DAS APIプラグインは`mplBubblegum`プラグイン内に自動的にインストールされます。そのため、すでに準備完了です!
+
+`mplBubblegum`プラグイン全体をインポートせずに_のみ_DAS APIプラグインを使用したい場合は、Metaplex DAS APIプラグインを直接インストールできます:
+
+```sh
+npm install @metaplex-foundation/digital-asset-standard-api
+```
+
+その後、Umiインスタンスでライブラリを登録します:
+
+```ts
+import { dasApi } from '@metaplex-foundation/digital-asset-standard-api';
+
+umi.use(dasApi());
+```
+{% /totem-prose %}

+ 50 - 0
src/pages/ja/bubblegum-v2/freeze-cnfts.md

@@ -0,0 +1,50 @@
+---
+title: 圧縮NFTの凍結と解凍
+metaTitle: 圧縮NFTの凍結と解凍 | Bubblegum V2
+description: Bubblegumで圧縮NFTを凍結・解凍する方法を学びます。
+---
+
+Bubblegum V2では、圧縮NFTを凍結・解凍できます。これはステーキングなどのさまざまなユースケースに役立ちます。 {% .lead %}
+
+## 圧縮NFTの凍結
+
+事前にリーフデリゲートに委任された圧縮NFTを凍結するには、`freezeV2`命令を使用できます。まだ委任されていない場合は、以下の`delegateAndFreezeV2`を参照してください。`freezeV2`命令は次のように使用できます:
+
+{% dialect-switcher title="リーフデリゲートとして圧縮NFTを凍結" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+```js
+import {
+  getAssetWithProof,
+  freezeV2,
+} from '@metaplex-foundation/mpl-bubblegum';
+
+const assetWithProof = await getAssetWithProof(umi, assetId);
+await freezeV2(umi, {
+  ...assetWithProof,
+  leafOwner: umi.identity.publicKey,
+  authority: leafDelegate, // これはデフォルトで支払者になります
+  leafDelegate: leafDelegate.publicKey,
+  // cNFTがコレクションの一部である場合、コレクションアドレスを渡します。
+  //coreCollection: collectionSigner.publicKey,
+}).sendAndConfirm(umi);
+```
+{% /totem %}
+{% totem-accordion title="永続凍結デリゲートとして" %}
+```js
+import {
+  getAssetWithProof,
+  freezeV2,
+} from '@metaplex-foundation/mpl-bubblegum';
+
+const assetWithProof = await getAssetWithProof(umi, assetId);
+await freezeV2(umi, {
+  ...assetWithProof,
+  leafOwner: umi.identity.publicKey,
+  authority: permanentFreezeDelegate,
+  leafDelegate: permanentFreezeDelegate.publicKey,
+  coreCollection: collectionSigner.publicKey,
+}).sendAndConfirm(umi);
+```
+{% /totem-accordion %}
+{% /dialect %}

+ 51 - 0
src/pages/ja/bubblegum-v2/hashed-nft-data.md

@@ -0,0 +1,51 @@
+---
+title: NFTデータのハッシュ化
+metaTitle: NFTデータのハッシュ化 | Bubblegum V2
+description: BubblegumでNFTデータがどのようにハッシュ化されるかについて詳しく学びます。
+---
+
+前のセクションで、Bubblegumマークルツリーのリーフノードはそれぞれ圧縮NFT(cNFT)のデータをハッシュ化することで得られると述べました。しかし、これは正確にはどのように行われるのでしょうか?cNFTのメタデータから始めます。Bubblegum V2の各cNFTは、ミント命令への引数として以下のメタデータ構造でミントされることに注意してください。Bubblegum v1は代わりにMetadataArgsを使用します:
+
+```rust
+pub struct MetadataArgsV2 {
+    /// アセットの名前
+    pub name: String,
+    /// アセットのシンボル
+    pub symbol: String,
+    /// アセットを表すJSONを指すURI
+    pub uri: String,
+    /// 二次販売で作成者に行くロイヤリティベーシスポイント(0-10000)
+    pub seller_fee_basis_points: u16,
+    /// 不変、一度変更されると、このメタデータのすべての販売は二次とみなされる。
+    pub primary_sale_happened: bool,
+    /// データ構造が可変かどうか、デフォルトは不変
+    pub is_mutable: bool,
+    /// トークン標準。現在、`NonFungible`のみが許可されている。
+    pub token_standard: Option<TokenStandard>,
+    /// 作成者配列
+    pub creators: Vec<Creator>,
+    /// コレクション。V2では単に`Pubkey`であり、常に検証済みとみなされる。
+    pub collection: Option<Pubkey>,
+}
+```
+
+cNFTのメタデータは、図に示され以下に説明されているように複数回ハッシュ化されます:
+
+{% diagram %}
+
+{% node %}
+{% node #metadata label="メタデータ引数" theme="blue" /%}
+{% node label="名前" /%}
+{% node label="シンボル" /%}
+{% node label="URI" /%}
+{% node label="販売者手数料ベーシスポイント" /%}
+{% node label="主要販売が発生" /%}
+{% node label="可変性" /%}
+{% node label="トークン標準" /%}
+{% node label="コレクション" /%}
+{% node label="作成者" /%}
+{% /node %}
+
+{% node #seller-fee-basis-points parent="metadata" y="305" label="販売者手数料ベーシスポイント" theme="blue" /%}
+
+{% /diagram %}

+ 147 - 0
src/pages/ja/bubblegum-v2/index.md

@@ -0,0 +1,147 @@
+---
+title: 概要
+metaTitle: 概要 | Bubblegum V2
+description: Bubblegum V2と圧縮NFTの概要を説明します。
+---
+
+Bubblegum V2は、Solana上で圧縮NFT(cNFT)を作成し、操作するための最新のMetaplexプロトコルプログラムです。大規模運用向けに構築されたBubblegum V2は、オリジナルのBubblegumの利点をすべて保持しながら、新しい強力な機能を導入しています。圧縮NFTは、オンチェーンでのデータ保存方法を再考することで、NFT作成を新しい規模のレベルまでスケールさせることを可能にします。 {% .lead %}
+
+{% quick-links %}
+
+{% quick-link title="はじめに" icon="InboxArrowDown" href="/ja/bubblegum-v2/sdk" description="お好みの言語またはライブラリを見つけて、圧縮NFTを開始しましょう。" /%}
+
+{% quick-link title="APIリファレンス" icon="CodeBracketSquare" href="https://mpl-bubblegum.typedoc.metaplex.com/" target="_blank" description="特定のものをお探しですか?APIリファレンスをご覧いただき、答えを見つけてください。" /%}
+
+{% /quick-links %}
+
+## Bubblegum V2の新機能
+
+Bubblegum V2は、オリジナルのBubblegumプログラムの基盤の上に構築され、いくつかの強力な新機能を導入しています:
+
+- **凍結・解凍機能**: 2種類の凍結/解凍が利用可能:1) cNFT所有者は、アセットレベルの制御のためにフリーズ権限をリーフデリゲートに委任でき、特定のイベント中の転送を防ぐことや権利確定メカニズムの実装など、さまざまなユースケースに柔軟性を提供します。2) コレクション作成時に`PermanentFreezeDelegate`プラグインが有効になっている場合、プロジェクト作成者は永続フリーズデリゲートを通じてcNFTを凍結・解凍でき、コレクション全体の制御が可能です
+- **MPL-Coreコレクション統合**: Bubblegum V2 NFTは、トークンメタデータコレクションに限定されることなく、MPL-Coreコレクションに追加できるようになり、より大きなMetaplexエコシステムとの柔軟性と統合を可能にします。
+- **ロイヤリティ強制**: Bubblegum V2は[MPL-Core](https://docs.metaplex.com/ja/core/overview)コレクションを使用しているため、`ProgramDenyList`などを使用してcNFTにロイヤリティを強制することが可能です。
+- **ソウルバウンドNFT**: cNFTをソウルバウンド(転送不可)にすることが可能になり、所有者のウォレットに永続的に紐づけられます。これは資格、出席証明、身元確認などに最適です。コレクション作成時に`PermanentFreezeDelegate`プラグインを有効にする必要があります。
+- **永続転送の許可**: コレクションで`PermanentTransferDelegate`プラグインが有効になっている場合、永続転送デリゲートはリーフ所有者の相互作用なしにcNFTを新しい所有者に転送できます。
+- **権限によるバーン**: コレクションに`PermanentBurnDelegate`プラグインが有効になっている場合、デリゲートはリーフ所有者の署名なしにNFTをバーンできます。
+- **属性**: MPL-Coreの`attributes`プラグインを使用して、コレクションレベルでの属性データを追加できます。
+
+上記の機能を動作させるために、Bubblegum V2は新しいリーフスキーマ(`LeafSchemaV2`)を導入しています。Bubblegum V2で使用されるリーフについて詳しく学ぶには、以下のセクションをご確認ください。
+
+## LeafSchemaV2
+
+Bubblegum V2は、後方互換性を維持しながら追加機能をサポートする新しいリーフスキーマ(LeafSchemaV2)を導入しています。この新しいスキーマは以下を可能にします:
+
+- 従来のトークンメタデータではなく、MPL-Coreコレクションとの統合
+- 凍結/解凍機能のサポート
+- ソウルバウンド機能の有効化
+
+プロジェクトは、要件に応じて、Legacy Bubblegumを使用したオリジナルのリーフスキーマまたはBubblegum V2を使用した新しいv2スキーマを選択できます。
+
+新しい`LeafSchemaV2`を使用するには、[`createTreeV2`命令](/ja/bubblegum-v2/create-trees)を使用して作成する必要があるV2マークルツリーを使用する必要があります。V1マークルツリーは新しいリーフスキーマをサポートせず、V2マークルツリーはV1リーフと互換性がありません。
+
+## マークルツリー、リーフ、証明
+
+圧縮NFTは、**マークルツリー**のコンテキストでのみ存在します。マークルツリーが何であるかは[専用の高度なガイド](/ja/bubblegum-v2/concurrent-merkle-trees)で説明していますが、この概要では、マークルツリーを**リーフ**と呼ぶハッシュのコレクションと考えることができます。各リーフは、[圧縮NFTのデータをハッシュ化する](/ja/bubblegum-v2/hashed-nft-data)ことで得られます。
+
+マークルツリー内の各リーフに対して、**証明**と呼ばれるハッシュのリストを提供できます。これにより、誰でも与えられたリーフがそのツリーの一部であることを検証できます。圧縮NFTが更新または転送されるたびに、関連するリーフも変更され、その証明も変更されます。
+
+{% diagram %}
+
+{% node #root label="ルートノード" theme="slate" /%}
+{% node #root-hash label="ハッシュ" parent="root" x="56" y="40" theme="transparent" /%}
+{% node #node-1 label="ノード 1" parent="root" y="100" x="-200" theme="blue" /%}
+{% node #node-1-hash label="ハッシュ" parent="node-1" x="42" y="40" theme="transparent" /%}
+{% node #node-2 label="ノード 2" parent="root" y="100" x="200" theme="mint" /%}
+
+{% node #node-3 label="ノード 3" parent="node-1" y="100" x="-100" theme="mint" /%}
+{% node #node-4 label="ノード 4" parent="node-1" y="100" x="100" theme="blue" /%}
+{% node #node-4-hash label="ハッシュ" parent="node-4" x="42" y="40" theme="transparent" /%}
+{% node #node-5 label="ノード 5" parent="node-2" y="100" x="-100" /%}
+{% node #node-6 label="ノード 6" parent="node-2" y="100" x="100" /%}
+
+{% node #leaf-1 label="リーフ 1" parent="node-3" y="100" x="-45" /%}
+{% node #leaf-2 label="リーフ 2" parent="node-3" y="100" x="55" /%}
+{% node #leaf-3 label="リーフ 3" parent="node-4" y="100" x="-45" theme="blue" /%}
+{% node #leaf-4 label="リーフ 4" parent="node-4" y="100" x="55" theme="mint" /%}
+{% node #leaf-5 label="リーフ 5" parent="node-5" y="100" x="-45" /%}
+{% node #leaf-6 label="リーフ 6" parent="node-5" y="100" x="55" /%}
+{% node #leaf-7 label="リーフ 7" parent="node-6" y="100" x="-45" /%}
+{% node #leaf-8 label="リーフ 8" parent="node-6" y="100" x="55" /%}
+{% node #nft label="NFTデータ" parent="leaf-3" y="100" x="-12" theme="blue" /%}
+
+{% node #proof-1 label="リーフ 4" parent="nft" x="200" theme="mint" /%}
+{% node #proof-2 label="ノード 3" parent="proof-1" x="90" theme="mint" /%}
+{% node #proof-3 label="ノード 2" parent="proof-2" x="97" theme="mint" /%}
+{% node #proof-legend label="証明" parent="proof-1" x="-6" y="-20" theme="transparent" /%}
+
+{% edge from="node-1" to="root" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="node-2" to="root" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+
+{% edge from="node-3" to="node-1" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+{% edge from="node-4" to="node-1" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="node-6" to="node-2" fromPosition="top" toPosition="bottom" /%}
+{% edge from="node-5" to="node-2" fromPosition="top" toPosition="bottom" /%}
+
+{% edge from="leaf-1" to="node-3" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-2" to="node-3" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-4" to="node-4" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+{% edge from="leaf-3" to="node-4" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="leaf-5" to="node-5" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-6" to="node-5" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-7" to="node-6" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-8" to="node-6" fromPosition="top" toPosition="bottom" /%}
+{% edge from="nft" to="leaf-3" fromPosition="top" toPosition="bottom" theme="blue" animated=true label="ハッシュ" /%}
+
+{% /diagram %}
+
+このように、マークルツリーは、与えられた圧縮NFTが存在することを誰でも検証できるオンチェーン構造として機能します。これらは、非常にスケーラブルにするNFTデータを保存せずにこれを行います。
+
+これは重要な質問をもたらします:NFTデータはどこに保存されているのでしょうか?
+
+## Metaplex DAS API
+
+新しい圧縮NFTをミントすると、そのデータがハッシュ化され、マークルツリーに新しいリーフとして追加されます。しかし、それだけではありません。さらに、NFT全体のデータは圧縮NFTを作成したトランザクションに保存されます。同様に、圧縮NFTが更新されると、その更新されたデータは、再度、変更ログとしてトランザクションに保存されます。そのため、そのデータを追跡するアカウントはありませんが、台帳内のすべての以前のトランザクションを見て、その情報を見つけることができます。
+
+{% diagram %}
+
+{% node #tx-1 label="トランザクション 1" /%}
+{% node #tx-2 label="トランザクション 2" parent="tx-1" y="50" /%}
+{% node #tx-3 label="トランザクション 3" parent="tx-2" y="50" /%}
+{% node #tx-4 label="トランザクション 4" parent="tx-3" y="50" /%}
+{% node #tx-5 label="トランザクション 5" parent="tx-4" y="50" /%}
+{% node #tx-rest label="..." parent="tx-5" y="50" /%}
+
+{% node #nft-1 label="初期NFTデータ" parent="tx-2" x="300" theme="blue" /%}
+{% node #nft-2 label="NFTデータ変更ログ" parent="tx-3" x="300" theme="blue" /%}
+{% node #nft-3 label="NFTデータ変更ログ" parent="tx-5" x="300" theme="blue" /%}
+
+{% edge from="nft-1" to="tx-2" label="保存先" /%}
+{% edge from="nft-2" to="tx-3" label="保存先" /%}
+{% edge from="nft-3" to="tx-5" label="保存先" /%}
+
+{% /diagram %}
+
+1つのNFTのデータを取得するためだけに、毎回何百万ものトランザクションをクロールすることは、確実に最良のユーザー体験ではありません。したがって、圧縮NFTは、この情報をリアルタイムでインデックス化し、エンドユーザーからこれを抽象化するために一部のRPCに依存しています。圧縮NFTの取得を可能にする結果のRPC APIを**Metaplex DAS API**と呼びます。
+
+すべてのRPCがDAS APIをサポートしているわけではないことに注意してください。そのため、アプリケーションで圧縮NFTを使用する際に適切なRPCを選択するために、["Metaplex DAS API RPC"](/ja/rpc-providers)ページに興味があるかもしれません。
+
+これについては、高度な["NFTデータの保存とインデックス化"](/ja/bubblegum-v2/stored-nft-data)ガイドで詳しく説明しています。
+
+## 機能
+
+NFTデータがアカウント内に存在しないにもかかわらず、圧縮NFTに対してさまざまな操作を実行することは依然として可能です。これは、現在のNFTデータをリクエストし、そのハッシュ化されたリーフがマークルツリーで有効であることを確認することで可能になります。そのため、圧縮NFTで以下の操作を実行できます:
+
+- 関連するコレクションを持つまたは持たない[cNFTのミント](/ja/bubblegum-v2/mint-cnfts)。
+- [cNFTの転送](/ja/bubblegum-v2/transfer-cnfts)。
+- [cNFTのデータまたはコレクションの更新](/ja/bubblegum-v2/update-cnfts)。
+- [cNFTのバーン](/ja/bubblegum-v2/burn-cnfts)。
+- [cNFTのデリゲート](/ja/bubblegum-v2/delegate-cnfts)。
+- [cNFTコレクションの検証と検証解除](/ja/bubblegum-v2/verify-collections)。
+- [cNFTの作成者の検証と検証解除](/ja/bubblegum-v2/verify-creators)。
+- [cNFTの凍結と解凍](/ja/bubblegum-v2/freeze-cnfts)。
+- [cNFTをソウルバウンドにする](/ja/bubblegum-v2/freeze-cnfts#create-a-soulbound-c-nft)。
+
+## 次のステップ
+
+圧縮NFTが高レベルでどのように機能し、Bubblegum V2の新機能について理解したので、圧縮NFTとの相互作用に使用できるさまざまな言語/フレームワークを列挙した[はじめに](/ja/bubblegum-v2/sdk)ページをチェックすることをお勧めします。その後、さまざまな[機能ページ](/ja/bubblegum-v2/create-trees)を使用して、cNFTで実行できる特定の操作について詳しく学ぶことができます。最後に、cNFTとマークルツリーの知識を深めるための[高度なガイド](/ja/bubblegum-v2/concurrent-merkle-trees)も利用できます。

+ 33 - 0
src/pages/ja/bubblegum-v2/merkle-tree-canopy.md

@@ -0,0 +1,33 @@
+---
+title: マークルツリーキャノピー
+metaTitle: マークルツリーキャノピー | Bubblegum V2
+description: Bubblegumのマークルツリーキャノピーについて詳しく学びます。
+---
+
+## はじめに
+
+SolanaのネットワーキングスタックはMTUサイズ1280バイトを使用し、ヘッダーを考慮すると、データに1232バイトが残ります。これが圧縮NFT(cNFT)に与える影響は、必要な証明がトランザクションサイズを占有しすぎるため、現在深度24を超えるマークルツリーの変更は不可能になることです。
+
+これらの証明サイズの制限を回避するために、SPLアカウント圧縮は、マークルツリーの最上位ノードをキャッシュする機能を提供します。これは**キャノピー**と呼ばれ、同時マークルツリーアカウントの最後に保存されます。
+
+深度*d*のツリーの上位*n*レベルをキャッシュすることで、証明を最初の*d - n*ノードに切り捨てることができます。これにより、アカウント圧縮トランザクションのサイズを削減し、10億以上のcNFTを保存できる深度31までのツリーの変更を可能にします。
+
+マークルツリーアカウントにキャノピーを初期化するには、追加のバイトでアカウントを初期化する必要があります。必要な追加バイト数は(2*ⁿ*⁺¹ - 1)* 32です。ここで*n*は、キャノピーにキャッシュしたいマークルツリーのレベル数です。
+
+キャノピーは、同時マークルツリーが変更されるたびに更新されます。追加の作業は必要ありません。ただし、ツリーの作成後にキャノピーサイズを変更することはできません。
+
+## 組み合わせやすさ vs. コスト削減
+
+以下の表は[compressed.app](https://compressed.app/)の協力で生成され、キャノピーサイズによって1,000,000個のcNFTをミントする総コストがいかに大きく異なるかを示しています。
+
+### 様々なキャノピー深度での1,000,000 cNFTのコスト
+*深度20のマークルツリーは1,048,576個のcNFTを保存できます。*
+| キャノピー深度 | 証明バイト | ストレージコスト | ミントコスト(LUTで3ミント/tx) | 総コスト |
+| -------------- | ---------- | --------------- | ------------------------------- | -------- |
+| 0              | 640        | 0.3091          | 1.6667                          | 1.9758   |
+| 14             | 192        | 7.6067          | 1.6667                          | 9.2734   |
+| 17             | 96         | 58.6933         | 1.6667                          | 60.36    |
+
+キャノピー深度をゼロにする理由は、可能な限り安いミントを行うためです。ただし、これには`transfer`、`delegate`、`burn`などの命令で大量の証明データを送信する必要があります。ゼロ深度キャノピーの場合、トランザクションサイズ制限の半分以上が証明データで消費され、Bubblegum命令を他のプログラム命令と組み合わせる能力に悪影響を与えます。
+
+最終的に、キャノピーサイズの決定は、コストと組み合わせやすさのトレードオフを考慮する必要があります。この評価では、cNFTの使用目的、開発プラットフォームの互換性、ツリーの所有権構造などの要因を考慮に入れる必要があります。

+ 40 - 0
src/pages/ja/bubblegum-v2/mint-cnfts.md

@@ -0,0 +1,40 @@
+---
+title: 圧縮NFTのミント
+metaTitle: 圧縮NFTのミント | Bubblegum V2
+description: Bubblegum V2で圧縮NFTをミントする方法を学びます。
+---
+
+[前のページ](/ja/bubblegum-v2/create-trees)では、圧縮NFTをミントするためにBubblegumツリーが必要であることを確認し、その作成方法を見ました。今度は、与えられたBubblegumツリーから圧縮NFTをミントする方法を見てみましょう。 {% .lead %}
+
+Bubblegumプログラムは、異なるリーフスキーマバージョン用の複数のミント命令を提供します。Bubblegum V2は、与えられたコレクションに、またはコレクションなしで圧縮NFTをミントするために使用される**mintV2**という新しいミント命令を導入しています。
+
+## コレクションなしでのミント
+
+Bubblegumプログラムは、Bubblegumツリーから圧縮NFTをミントすることを可能にする**mintV2**命令を提供します。Bubblegumツリーが公開されている場合、誰でもこの命令を使用できます。そうでなければ、ツリー作成者またはツリーデリゲートのみが使用できます。
+
+**mintV2**命令の主要なパラメータは以下の通りです:
+
+- **マークルツリー**: 圧縮NFTがミントされるマークルツリーのアドレス。
+- **ツリー作成者またはデリゲート**: Bubblegumツリーからのミントを許可された権限 — これはツリーの作成者またはデリゲートのいずれかです。この権限はトランザクションに署名する必要があります。公開ツリーの場合、このパラメータは任意の権限にできますが、それでも署名者である必要があります。
+- **リーフ所有者**: ミントされる圧縮NFTの所有者。デフォルトではトランザクションの支払者です。
+- **リーフデリゲート**: ミント済みcNFTの管理を許可されたデリゲート権限(存在する場合)。そうでなければ、リーフ所有者に設定されます。
+- **コレクション権限**: 与えられたコレクションの管理を許可された権限。
+- **Coreコレクション**: 圧縮NFTが追加されるMPL-CoreコレクションNFT。
+- **メタデータ**: ミントされる圧縮NFTのメタデータ。NFTの**名前**、**URI**、**コレクション**、**作成者**などの情報が含まれます。Bubblegum V2では、メタデータは`uses`やコレクションの`verified`フラグなど、不要なフィールドを除外した`MetadataArgsV2`を使用しています。
+
+{% dialect-switcher title="コレクションなしで圧縮NFTをミント" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { none } from '@metaplex-foundation/umi';
+import { mintV2 } from '@metaplex-foundation/mpl-bubblegum';
+
+await mintV2(umi, {
+  leafOwner: umi.identity.publicKey,
+  merkleTree: merkleTree.publicKey,
+  metadata: {
+    name: 'My NFT',
+    uri: 'https://example.com/my-nft.json',
+    sellerFeeBasisPoints: 550, 
+    collection: none(),
+    creators: [],

+ 15 - 0
src/pages/ja/bubblegum-v2/sdk/index.md

@@ -0,0 +1,15 @@
+---
+title: MPL-Bubblegum SDK
+metaTitle: SDK | Bubblegum V2
+description: MPL-Bubblegum SDKを使用してMetaplexの圧縮NFT標準(cNFT)を開始する方法を学びます。
+---
+
+MPL-Bubblegumを使用した新しいMetaplex圧縮NFT(cNFT)標準を開始するために、以下から使用したい言語/ライブラリを選択してください。
+
+{% quick-links %}
+
+{% quick-link title="JavaScript" icon="JavaScript" href="/ja/bubblegum-v2/sdk/javascript" description="Umiフレームワークベースのaファイル-Bubblegum用JavaScriptライブラリを開始しましょう。" /%}
+
+{% quick-link title="Rust" icon="Rust" href="/ja/bubblegum-v2/sdk/rust" description="MPL-Bubblegum Rustクレートを使用して開始しましょう。" /%}
+
+{% /quick-links %}

+ 46 - 0
src/pages/ja/bubblegum-v2/sdk/javascript.md

@@ -0,0 +1,46 @@
+---
+title: MPL-Bubblegum JavaScript SDK
+metaTitle: JavaScript SDK | MPL-Bubblegum
+description: MPL-Bubblegum JavaScript SDKを実行するためのプロジェクト設定方法を学びます。
+---
+
+MetaplexはMPL-Bubblegumプログラムとの相互作用に使用できるJavaScriptライブラリを提供しています。[Umiフレームワーク](/ja/umi)のおかげで、多くの固有の依存関係なしに配布され、任意のJavaScriptプロジェクトで使用できる軽量なライブラリを提供します。
+
+開始するには、[Umiフレームワークをインストール](/ja/umi/getting-started)し、MPL-Bubblegum JavaScriptライブラリをインストールする必要があります。
+
+## インストール
+
+インストールは任意のJavaScriptパッケージマネージャー(npm、yarn、bunなど)で実行できます。
+```sh
+npm install @metaplex-foundation/mpl-bubblegum
+```
+
+{% quick-links %}
+
+{% quick-link title="typedoc" target="_blank" icon="JavaScript" href="https://mpl-bubblegum.typedoc.metaplex.com/" description="MPL-Bubblegum JavaScript SDK生成パッケージAPIドキュメント。" /%}
+
+{% quick-link title="npmjs.com" target="_blank" icon="JavaScript" href="https://www.npmjs.com/package/@metaplex-foundation/mpl-bubblegum" description="NPM上のMPL-Bubblegum Javascript SDK。" /%}
+
+{% /quick-links %}
+
+## Umiのセットアップ
+
+まだ`umi`インスタンスをセットアップして設定していない場合は、[Umiはじめに](/ja/umi/getting-started)ページをご確認ください。
+
+`umi`インスタンスの初期化中に、以下を使用してMPL-Bubblegumパッケージを`umi`に追加できます
+
+```js
+.use(mplBubblegum())
+```
+
+```ts
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { mplBubblegum } from '@metaplex-foundation/mpl-bubblegum'
+
+// お好みのRPCエンドポイントを使用してください。
+const umi = createUmi('http://api.devnet.solana.com')
+... // 追加のumi設定、パッケージ、署名者
+.use(mplBubblegum())
+```
+
+ここから、`umi`インスタンスはMPL-Bubblegumパッケージにアクセスでき、その機能を探索できます。

+ 116 - 0
src/pages/ja/bubblegum-v2/sdk/rust.md

@@ -0,0 +1,116 @@
+---
+title: MPL-Bubblegum Rust SDK
+metaTitle: Rust SDK | MPL-Bubblegum
+description: MPL-Bubblegum Rust SDKを実行するためのプロジェクト設定方法を学びます。
+---
+
+MetaplexはMPL-Bubblegumプログラムとの相互作用に使用できるRustライブラリを提供しています。Rustライブラリは、Rustスクリプト/ビルドや、CPI命令を介したオンチェーンプログラムで使用できます。
+
+## インストール
+
+MPL-Bubblegum Rust SDKは、スクリプト/デスクトップ/モバイルアプリケーション、およびSolanaオンチェーンプログラムの両方で使用できます。
+
+```rust
+cargo add mpl-bubblegum
+```
+
+{% quick-links %}
+
+{% quick-link title="crates.io" target="_blank" icon="Rust" href="https://crates.io/crates/mpl-bubblegum" description="MPL-Bubblegum Rustクレートを開始しましょう。" /%}
+
+{% quick-link title="docs.rs" target="_blank" icon="Rust" href="https://docs.rs/MPL-Bubblegum/latest/mpl_bubblegum/" description="MPL-BubblegumのRust SDK typedocプラットフォーム。" /%}
+
+{% /quick-links %}
+
+## ローカルスクリプト
+
+ローカルスクリプトの場合、リストされたすべての命令の`Builder`バージョンを使用することをお勧めします。これらのビルダーは多くの作業を抽象化し、トランザクションに追加できる命令を返します。
+
+すべてのBubblegum命令のリストはこちらで確認できます:[MPL-Bubblegum - Rust Instructions](https://docs.rs/mpl-bubblegum/latest/mpl_bubblegum/instructions/index.html)
+
+Rustの使用に関するより包括的なガイドについては、[Metaplex Rust SDKガイド](/ja/guides/rust/metaplex-rust-sdks)ページをご覧ください。
+
+#### CreateTreeConfigBuilder - 例
+
+```rust
+use mpl_bubblegum::{instructions::CreateTreeConfigV2Builder, programs::{SPL_ACCOUNT_COMPRESSION_ID, SPL_NOOP_ID}};
+use solana_client::{nonblocking::rpc_client, rpc_config::RpcSendTransactionConfig};
+use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair, signer::Signer, system_program, transaction::Transaction};
+
+#[tokio::main]
+pub async fn create_tree(keypair: Keypair) {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com/".to_string());
+
+    let payer = keypair;
+        
+
+    let asset = Keypair::new();
+
+    let merkle_tree = Keypair::new();
+
+    let tree_config = Pubkey::find_program_address(
+        &[
+            &merkle_tree.pubkey().to_bytes(),
+        ],
+        &mpl_bubblegum::ID,
+    );
+
+    let create_tree_config_ix = CreateTreeConfigV2Builder::new()
+        .merkle_tree(merkle_tree.pubkey())
+        .tree_config(tree_config.0)
+        .payer(payer.pubkey())
+        .max_depth(20)
+        .max_buffer_size(1024)
+        .public(false)
+        .instruction();
+
+    let signers = vec![&asset, &payer];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await;
+
+    let create_tree_config_tx = Transaction::new_signed_with_payer(
+        &[create_tree_config_ix],
+        Some(&payer.pubkey()),
+        &signers,
+        last_blockhash.unwrap(),
+    );
+
+    let res = rpc_client
+        .send_transaction_with_config(&create_tree_config_tx, RpcSendTransactionConfig {
+            skip_preflight: false,
+            preflight_commitment: Some(CommitmentConfig::confirmed().commitment),
+            encoding: None,
+            max_retries: None,
+            min_context_slot: None,
+        })
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res);
+}
+```
+
+## CPI(クロスプログラム呼び出し)
+
+独自のプログラムからCPI命令を実行することは、`MPL-Bubblegum` Rustクレート内のすべての命令に対して見つけることができる命令関数の`CpiBuilder`バージョンを使用することで簡単に実現できます。
+
+すべてのBubblegum命令のリストはこちらで確認できます:[Metaplex Bubblegum - Rust Instructions](https://docs.rs/mpl-bubblegum/latest/mpl_bubblegum/instructions/index.html)
+
+MetaplexクレートとCPI命令の作成に関するより包括的なガイドについては、[MetaplexプログラムへのCPI方法ガイド](/ja/guides/rust/how-to-cpi-into-a-metaplex-program)ページをご覧ください。
+
+#### CreateTreeConfigCpiBuilder - 例
+
+```rust
+CreateTreeConfigV2CpiBuilder::new()
+        .merkle_tree(context.accounts.merkle_tree)
+        .tree_config(context.accounts.tree_config)
+        .payer(context.accounts.payer)
+        .tree_creator(context.accounts.tree_creator)
+        .log_wrapper(SPL_NOOP_ID)
+        .compression_program(context.accounts.compression_program)
+        .system_program(context.accounts.system_program)
+        .max_depth(20)
+        .max_buffer_size(1024)
+        .public(false)
+        .invoke()
+```

+ 49 - 0
src/pages/ja/bubblegum-v2/stored-nft-data.md

@@ -0,0 +1,49 @@
+---
+title: NFTデータの保存とインデックス化
+metaTitle: NFTデータの保存とインデックス化 | Bubblegum V2
+description: BubblegumでNFTデータがどのように保存されるかについて詳しく学びます。
+---
+
+[概要](/ja/bubblegum#read-api)で述べたように、圧縮NFT(cNFT)が作成または変更されるたびに、対応するトランザクションが台帳にオンチェーンで記録されますが、cNFTの状態データはアカウントスペースに保存されません。これがcNFTの大幅なコスト削減の理由ですが、利便性と使いやすさのために、cNFTの状態データはRPCプロバイダーによってインデックス化され、**Metaplex DAS API**を通じて利用できます。
+
+Metaplexは、DAS APIの[参考実装](https://github.com/metaplex-foundation/digital-asset-rpc-infrastructure)を作成しており、一部のRPCプロバイダーは特定の実装にこのコードの一部またはすべてを使用していますが、他のRPCプロバイダーは独自のものを作成しています。Metaplex DAS APIをサポートする他のRPCプロバイダーのリストについては、["Metaplex DAS API RPC"](/ja/rpc-providers)ページを参照してください。
+
+Metaplexのリファレンス実装であるDAS APIには、以下の主要な項目が含まれています:
+* Solanaノーヴォートバリデーター - このバリデーターは、コンセンサスの下でバリデーター台帳とアカウントデータへの安全なアクセスのみを持つように構成されています。
+* Geyserプラグイン - プラグインは「Plerkle」と呼ばれ、バリデーター上で実行されます。プラグインは、アカウントの更新、スロットステータスの更新、トランザクション、またはブロックメタデータの更新があるたびに通知されます。cNFTインデックス化の目的では、プラグインの`notify_transaction`メソッドは、BubblegumまたはSPLアカウント圧縮トランザクションがバリデーター上で確認されるたびにトランザクションデータを提供するために使用されます。実際には、これらのトランザクションはSPLノープ(「ノー・オペレーション」)プログラムから来ており、これはSPLアカウント圧縮とBubblegumによって、イベントをSPLノープ命令データに変換することでログの切り捨てを回避するために使用されます。
+* Redisクラスター - Redisストリームは、各タイプの更新(アカウント、トランザクションなど)のキューとして使用されます。Geyserプラグインは、これらのストリームに入るデータの生産者です。Geyserプラグインは、Flatbuffersプロトコルを使用するPlerkleシリアル化フォーマットにデータを変換し、シリアル化されたレコードを適切なRedisデータストリームに配置します。
+* インジェスタープロセス - これは、Redisストリームからのデータのコンシューマーです。インジェスターは、シリアル化されたデータを解析し、それをPostgresデータベースに保存されるSeaORMデータオブジェクトに変換します。
+* Postgresデータベース - アセットを表すいくつかのデータベーステーブル、および確認したマークルツリーの状態を保存するための変更ログテーブルがあります。後者は、Bubblegum命令で使用されるアセット証明を要求する際に使用されます。マークルツリーの変更のためのシーケンス番号も、DAS APIがトランザクションを順不同で処理できるようにするために使用されます。
+* APIプロセス - エンドユーザーがRPCプロバイダーからアセットデータを要求する際、APIプロセスはデータベースからアセットデータを取得し、リクエストのためにそれを提供できます。
+
+{% diagram %}
+{% node %}
+{% node #validator label="バリデーター" theme="indigo" /%}
+{% node theme="dimmed" %}
+Geyserプラグインを実行し \
+トランザクション、アカウント \
+更新などで通知される。
+{% /node %}
+{% /node %}
+
+{% node x="200" parent="validator" %}
+{% node #messenger label="メッセージバス" theme="blue" /%}
+{% node theme="dimmed" %}
+各種更新のキューとしての \
+Redisストリーム。
+{% /node %}
+{% /node %}
+
+{% node x="200" parent="messenger" %}
+{% node #ingester label="インジェスタープロセス" theme="indigo" /%}
+{% node theme="dimmed" %}
+データを解析し \
+データベースに保存
+{% /node %}
+{% /node %}
+
+{% node x="28" y="150" parent="ingester" %}
+{% node #database label="データベース" theme="blue" /%}
+{% node theme="dimmed" %}
+Postgres \
+データベース

+ 50 - 0
src/pages/ja/bubblegum-v2/transfer-cnfts.md

@@ -0,0 +1,50 @@
+---
+title: 圧縮NFTの転送
+metaTitle: 圧縮NFTの転送 | Bubblegum v2
+description: Bubblegumで圧縮NFTを転送する方法を学びます。
+---
+
+**transferV2**命令は、圧縮NFTをある所有者から別の所有者に転送するために使用できます。転送を認証するには、現在の所有者またはデリゲート権限(存在する場合)がトランザクションに署名する必要があります。デリゲート権限は、リーフデリゲートまたはコレクションの`permanentTransferDelegate`のいずれかです。
+
+この命令は圧縮NFTを更新するため、Bubblegumツリー上のリーフを置き換えることに注意してください。これは、圧縮NFTの整合性を検証するために追加のパラメータを提供する必要があることを意味します。これらのパラメータはリーフを変更するすべての命令に共通であるため、[次のFAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)でドキュメント化されています。幸いなことに、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% callout title="トランザクションサイズ" type="note" %}
+トランザクションサイズエラーが発生した場合は、`getAssetWithProof`で`{ truncateCanopy: true }`の使用を検討してください。詳細については[FAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)を参照してください。
+{% /callout %}
+
+## Bubblegum V2圧縮NFTの転送
+
+命令は以下のパラメータを受け入れます:
+
+- **リーフ所有者**: 圧縮NFTの現在の所有者。デフォルトではトランザクションの支払者です。
+- **リーフデリゲート**: 圧縮NFTの現在の所有者とそのデリゲート権限(存在する場合)。これらのいずれかがトランザクションに署名する必要があります。
+- **権限**: トランザクションに署名するオプションの権限。リーフ所有者または`permanentTransferDelegate`にすることができ、デフォルトではトランザクションの`payer`です。
+- **新しいリーフ所有者**: 圧縮NFTの新しい所有者のアドレス
+- **マークルツリー**: Bubblegumツリーのアドレス
+- **ルート**: Bubblegumツリーの現在のルート
+- **データハッシュ**: 圧縮NFTのメタデータのハッシュ
+- **作成者ハッシュ**: 圧縮NFTの作成者のハッシュ
+- **ノンス**: 圧縮NFTのノンス
+- **インデックス**: 圧縮NFTのインデックス
+- **コレクション**: 圧縮NFTのコアコレクション(cNFTがコレクションの一部である場合)
+
+JavaScriptを使用する場合は、最初に`getAssetWithProof`関数を使用してパラメータを取得し、それらを`transferV2`命令に渡すことをお勧めします。
+
+{% dialect-switcher title="圧縮NFTの転送" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, transferV2 } from '@metaplex-foundation/mpl-bubblegum';
+const assetWithProof = await getAssetWithProof(umi, assetId, {
+  truncateCanopy: true,
+})
+
+// その後、leafOwnerAはそれを使用してNFTをleafOwnerBに転送できます。
+const leafOwnerB = generateSigner(umi)
+await transferV2(umi, {
+  // プルーフ付きアセットからのパラメータを渡します。
+  ...assetWithProof,
+  authority: leafOwnerA,
+  newLeafOwner: leafOwnerB.publicKey,
+  // cNFTがコレクションの一部である場合、コアコレクションを渡します。

+ 74 - 0
src/pages/ja/bubblegum-v2/update-cnfts.md

@@ -0,0 +1,74 @@
+---
+title: 圧縮NFTの更新
+metaTitle: 圧縮NFTの更新 | Bubblegum V2
+description: Bubblegumで圧縮NFTを更新する方法を学びます。
+---
+
+**updateMetadataV2**命令は、圧縮NFTのメタデータを変更するために使用できます。マークルルートは、データの伝播されたハッシュを反映するように更新され、[Metaplex DAS API](https://github.com/metaplex-foundation/digital-asset-standard-api)に準拠するRPCプロバイダーは、cNFTのインデックスを更新します。
+
+メタデータは、圧縮NFTがコレクション内の検証されたアイテムかどうかによって、2つの権限のうちの1つによって更新できます。
+
+## 更新権限
+
+cNFTには2つの可能な更新権限があります:ツリー所有者、または(コレクションに属している場合)コレクション権限です。
+
+### コレクション権限
+
+cNFTがコレクションに属している場合、そのcNFTの更新権限はコレクションの権限になります。cNFTを更新する際は、更新関数に`coreCollection`引数を渡す必要があります。
+
+権限は現在のumiアイデンティティから推測されます。権限が現在のumiアイデンティティと異なる場合は、`authority`引数をsigner型として渡すか、後で署名するための`noopSigner`を作成する必要があります。
+
+```js
+await updateMetadataV2(umi, {
+  ...
+  authority: collectionAuthority,
+  coreCollection: publicKey("11111111111111111111111111111111"),
+}).sendAndConfirm(umi)
+```
+
+### ツリー権限
+
+cNFTがコレクションに属していない場合、cNFTの更新権限は、cNFTが属するツリーの権限になります。この場合、更新関数から`coreCollection`引数を**省略**します。
+
+権限は現在のumiアイデンティティから推測されます。権限が現在のumiアイデンティティと異なる場合は、`authority`引数をsigner型として渡すか、後で署名するための`noopSigner`を作成する必要があります。
+
+## cNFTの更新
+
+{% dialect-switcher title="圧縮NFTの更新" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  updateMetadataV2,
+  UpdateArgsArgs,
+} from '@metaplex-foundation/mpl-bubblegum'
+import { some } from '@metaplex-foundation/umi'
+
+// ヘルパーを使用してアセットと証明を取得します。
+const assetWithProof = await getAssetWithProof(umi, assetId, {
+  truncateCanopy: true,
+})
+
+// その後、NFTのメタデータを更新するために使用できます。
+const updateArgs: UpdateArgsArgs = {
+  name: some('新しい名前'),
+  uri: some('https://updated-example.com/my-nft.json'),
+}
+await updateMetadataV2(umi, {
+  ...assetWithProof,
+  leafOwner,
+  currentMetadata: assetWithProof.metadata,
+  updateArgs,
+  // オプションパラメータ。権限が現在のumiアイデンティティと
+  // 異なる署名者型の場合、ここでその署名者を割り当てます。
+  authority: <Signer>,
+  // オプションパラメータ。cNFTがコレクションに属している場合はここで渡します。
+  coreCollection: publicKey("22222222222222222222222222222222"),
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 94 - 0
src/pages/ja/bubblegum-v2/verify-creators.md

@@ -0,0 +1,94 @@
+---
+title: 作成者の検証
+metaTitle: 作成者の検証 | Bubblegum V2
+description: Bubblegumで作成者の検証と検証解除を行う方法を学びます。
+---
+
+圧縮NFTのメタデータに作成者のリストが設定されている場合、これらの作成者は特別な命令を使用してcNFT上で自分自身を検証・検証解除できます。 {% .lead %}
+
+これらの命令は、cNFTの**作成者**配列の適切な項目で**検証済み**ブール値を切り替えます。このブール値は、ウォレットやマーケットプレイスなどのアプリがどの作成者が本物でどの作成者がそうでないかを知ることができるため重要です。
+
+作成者は、ミントトランザクションに署名することで、[圧縮NFTをミント](/ja/bubblegum-v2/mint-cnfts)する際に直接自分自身を検証できることに注目する価値があります。とはいえ、今度は作成者が既存の圧縮NFTで自分自身を検証または検証解除する方法を見てみましょう。
+
+## 作成者の検証
+
+Bubblegumプログラムは、検証しようとしている作成者によって署名される必要がある**verifyCreatorV2**命令を提供します。作成者は既に圧縮NFTの**作成者**配列の一部である必要があります。まだ配列の一部でない場合は、最初に[`updateMetadataV2`](/ja/bubblegum-v2/update-cnfts)命令を使用して**作成者**配列に作成者を追加してください。
+
+さらに、この命令はBubblegumツリー上のリーフを置き換えるため、圧縮NFTの整合性を検証するためにより多くのパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[次のFAQ](/ja/bubblegum-v2/faq#replace-leaf-instruction-arguments)でドキュメント化されています。幸いなことに、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTの作成者の検証" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  verifyCreatorV2,
+  MetadataArgsV2Args
+} from '@metaplex-foundation/mpl-bubblegum';
+import {
+  unwrapOption,
+  none,
+} from '@metaplex-foundation/umi';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+const collectionOption = unwrapOption(assetWithProof.metadata.collection);
+const metadata: MetadataArgsV2Args = {
+  name: assetWithProof.metadata.name,
+  uri: assetWithProof.metadata.uri,
+  sellerFeeBasisPoints: assetWithProof.metadata.sellerFeeBasisPoints,
+  collection: collectionOption
+    ? collectionOption.key
+    : none(),
+  creators: assetWithProof.metadata.creators,
+};
+await verifyCreatorV2(umi, {
+  ...assetWithProof,
+  metadata,
+  creator: umi.identity, // またはumiアイデンティティとは異なる署名者
+}).sendAndConfirm(umi);
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## 作成者の検証解除
+
+**verifyCreatorV2**命令と同様に、**unverifyCreatorV2**命令は作成者によって署名される必要があり、圧縮NFT上でそれらを検証解除します。
+
+{% dialect-switcher title="圧縮NFTの作成者の検証解除" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  unverifyCreatorV2,
+  MetadataArgsV2Args
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  unwrapOption,
+  none,
+} from '@metaplex-foundation/umi';
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+const metadata: MetadataArgsV2Args = {
+  name: assetWithProof.metadata.name,
+  uri: assetWithProof.metadata.uri,
+  sellerFeeBasisPoints: assetWithProof.metadata.sellerFeeBasisPoints,
+  collection: unwrapOption(assetWithProof.metadata.collection)
+    ? unwrapOption(assetWithProof.metadata.collection)!.key
+    : none(),
+  creators: assetWithProof.metadata.creators,
+};
+await unverifyCreatorV2(umi, {
+  ...assetWithProof,
+  metadata,
+  creator: umi.identity, // またはumiアイデンティティとは異なる署名者
+}).sendAndConfirm(umi);
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 47 - 0
src/pages/ja/bubblegum/burn-cnfts.md

@@ -0,0 +1,47 @@
+---
+title: 圧縮NFTのバーン
+metaTitle: 圧縮NFTのバーン | Bubblegum
+description: Bubblegumで圧縮NFTをバーンする方法を学びます。
+---
+
+**Burn**命令を使用して圧縮NFTをバーンし、Bubblegum Treeから永続的に削除できます。この操作を承認するには、現在の所有者またはdelegate権限(存在する場合)のいずれかがトランザクションに署名する必要があります。命令は以下のパラメータを受け取ります:
+
+- **Leaf Owner**と**Leaf Delegate**: 圧縮NFTの現在の所有者とそのdelegate権限(存在する場合)。これらのいずれかがトランザクションに署名する必要があります。
+
+この命令はBubblegum Tree上のリーフを置き換えるため、圧縮NFTをバーンする前にその整合性を検証するために追加のパラメータを提供する必要があることに注意してください。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% callout title="トランザクションサイズ" type="note" %}
+トランザクションサイズエラーが発生した場合は、`getAssetWithProof`で`{ truncateCanopy: true }`の使用を検討してください。詳細については[FAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)を参照してください。
+{% /callout %}
+
+{% dialect-switcher title="圧縮NFTのバーン" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, burn } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await burn(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+}).sendAndConfirm(umi)
+```
+
+{% totem-accordion title="delegateを使用する" %}
+
+```ts
+import { getAssetWithProof, burn } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await burn(umi, {
+  ...assetWithProof,
+  leafDelegate: currentLeafDelegate,
+}).sendAndConfirm(umi)
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 50 - 0
src/pages/ja/bubblegum/create-trees.md

@@ -0,0 +1,50 @@
+---
+title: Bubblegumツリーの作成
+metaTitle: Bubblegumツリーの作成 | Bubblegum
+description: 圧縮NFTを保持できる新しいマークルツリーの作成と取得方法を学びます。
+---
+
+## はじめに
+
+圧縮NFTのデータはトランザクション内に保存され、オンチェーンアカウントには保存されませんが、マークルツリーとその設定を追跡するためのオンチェーンアカウントが必要です。そのため、圧縮NFTのミントを開始する前に、2つのアカウントを作成する必要があります:
+
+- **マークルツリーアカウント**。このアカウントは、あらゆる種類のデータの真正性を検証するために使用できる一般的なマークルツリーを保持します。これは、Solanaによって作成・維持されている[アカウント圧縮プログラム](https://spl.solana.com/account-compression)によって所有されています。私たちの場合、これを使用して圧縮NFTの真正性を検証します。
+- **ツリー設定アカウント**。この2番目のアカウントは、マークルツリーアカウントのアドレスから派生したPDAです。これにより、圧縮NFTに特有のマークルツリーの追加設定(例:ツリー作成者、ミント済みcNFTの数など)を保存できます。
+
+これらの2つのアカウントがあれば、圧縮NFTのミントを開始するために必要なすべてが揃います。関連するツリー設定アカウントを持つマークルツリーアカウントを**Bubblegumツリー**と呼びます。
+
+{% diagram height="h-64 md:h-[200px]" %}
+
+{% node %}
+{% node #merkle-tree label="マークルツリーアカウント" theme="blue" /%}
+{% node label="所有者: アカウント圧縮プログラム" theme="dimmed" /%}
+{% /node %}
+
+{% node #tree-config-pda parent="merkle-tree" x="300" label="PDA" theme="crimson" /%}
+
+{% node parent="tree-config-pda" y="60" %}
+{% node #tree-config label="ツリー設定アカウント" theme="crimson" /%}
+{% node label="所有者: Bubblegumプログラム" theme="dimmed" /%}
+{% /node %}
+
+{% edge from="merkle-tree" to="tree-config-pda" /%}
+{% edge from="tree-config-pda" to="tree-config" /%}
+
+{% /diagram %}
+
+## Bubblegumツリーの作成
+
+これらのアカウントの両方を作成してBubblegumツリーを作成する方法を見てみましょう。幸いなことに、私たちのライブラリはすべてを処理する**ツリー作成**操作を提供することで、このプロセスを簡単にしています。この操作は、さまざまなパラメータ(そのほとんどがオプション)を受け入れ、Bubblegumツリーを私たちのニーズに合わせてカスタマイズできます。最も重要なものは次のとおりです:
+
+- **マークルツリー**: マークルツリーアカウントの作成に使用される新しく生成された署名者。マークルツリーアカウントは、このアドレスでアクセス可能になります。
+- **ツリー作成者**: Bubblegumツリーを管理し、圧縮NFTをミントできるアカウントのアドレス。
+- **最大深度**と**最大バッファサイズ**: **最大深度**パラメータは、マークルツリーが保持できるリーフの最大数、つまり圧縮NFTを計算するために使用されます。この最大値は`2^maxDepth`で計算されます。**最大バッファサイズ**パラメータは、マークルツリーの最小同時実行制限を示します。言い換えると、ツリーで並行して発生できる変更の数を定義します。これらの2つのパラメータは任意に選択することはできず、以下の表に表示されている事前定義された値のセットから選択する必要があります。
+
+以下は、Solanaエコシステム内での互換性のための推奨ツリー設定です。
+
+| cNFTの数 | ツリーの深度 | キャノピーの深度 | 同時実行バッファ | ツリーのコスト | cNFTあたりのコスト |
+| ----------- | ----------- | --------------- | ---------------- | ------------ | ---------------- |
+| 16,384      | 14          | 8               | 64               | 0.3358       | 0.00002550       |
+| 65,536      | 16          | 10              | 64               | 0.7069       | 0.00001579       |
+| 262,144     | 18          | 12              | 64               | 2.1042       | 0.00001303       |
+| 1,048,576   | 20          | 13              | 1024             | 8.5012       | 0.00001311       |

+ 157 - 0
src/pages/ja/bubblegum/decompress-cnfts.md

@@ -0,0 +1,157 @@
+---
+title: 圧縮NFTの解凍
+metaTitle: 圧縮NFTの解凍 | Bubblegum
+description: Bubblegumで圧縮NFTを償還および解凍する方法を学びます。
+---
+
+{% callout type="note" title="v1機能" %}
+
+Token Metadata NFTへの解凍は、Bubblegum v1でのみ利用可能です。
+
+{% /callout %}
+
+圧縮NFTの所有者が、それを通常のNFTに解凍することが可能です。 {% .lead %}
+
+これは、Mintアカウント、Metadataアカウント、Master EditionアカウントなどのオンチェーンアカウントがそのNFT用に作成されることを意味します。これにより、NFTは圧縮NFTではできない特定の操作を実行し、圧縮NFTをサポートしていないプラットフォームとやりとりし、一般的にNFTエコシステムとの相互運用性を向上させることができます。
+
+## 解凍プロセス
+
+圧縮NFTの解凍は、NFTの所有者によって開始される2段階のプロセスです。
+
+1. まず、所有者は圧縮NFTをVoucherと**Redeem**(償還)する必要があります。これにより、Bubblegum treeからリーフが削除され、そのリーフが一度treeに存在していたことの証明として機能するVoucherアカウントが作成されます。
+
+2. 次に、所有者はVoucherを通常のNFTに**Decompress**(解凍)する必要があります。この時点で、通常のNFTのすべてのアカウントが圧縮NFTと同じデータで作成されます。あるいは、所有者は**Cancel Redeem**命令を使用してプロセスを逆転させることができ、これによりBubblegum tree上でリーフが復元され、Voucherアカウントが閉じられます。cNFTが完全に解凍されると、**Cancel Redeem**命令はもはや使用できず、したがってプロセスは逆転できなくなることに注意してください。
+
+{% diagram %}
+
+{% node #merkle-tree-wrapper %}
+{% node #merkle-tree label="Merkle Tree Account" theme="blue" /%}
+{% node label="Owner: Account Compression Program" theme="dimmed" /%}
+{% /node %}
+
+{% node #tree-config-pda parent="merkle-tree" x="87" y="-60" label="PDA" theme="crimson" /%}
+
+{% node #tree-config parent="tree-config-pda" x="-63" y="-80" %}
+{% node label="Tree Config Account" theme="crimson" /%}
+{% node label="Owner: Bubblegum Program" theme="dimmed" /%}
+{% /node %}
+
+{% node #voucher-wrapper parent="merkle-tree" x="350" %}
+{% node #voucher label="Voucher Account" theme="crimson" /%}
+{% node label="Owner: Bubblegum Program" theme="dimmed" /%}
+{% /node %}
+
+{% node parent="voucher" x="320" %}
+{% node #mint label="Mint Account" theme="blue" /%}
+{% node label="Owner: Token Program" theme="dimmed" /%}
+{% /node %}
+
+{% node #edition-pda parent="mint" x="80" y="-100" label="PDA" theme="crimson" /%}
+{% node #metadata-pda parent="mint" x="80" y="-200" label="PDA" theme="crimson" /%}
+
+{% node parent="edition-pda" x="-250" %}
+{% node #edition label="Master Edition Account" theme="crimson" /%}
+{% node label="Owner: Token Metadata Program" theme="dimmed" /%}
+{% /node %}
+
+{% node parent="metadata-pda" x="-250" %}
+{% node #metadata label="Metadata Account" theme="crimson" /%}
+{% node label="Owner: Token Metadata Program" theme="dimmed" /%}
+{% /node %}
+
+{% edge from="merkle-tree" to="tree-config-pda" path="straight" /%}
+{% edge from="tree-config-pda" to="tree-config" path="straight" /%}
+{% edge from="merkle-tree" to="voucher" animated=true label="1️⃣  Redeem" theme="mint" /%}
+{% edge from="voucher" to="mint" animated=true label="2️⃣  Decompress" theme="mint" /%}
+{% edge from="voucher-wrapper" to="merkle-tree-wrapper" animated=true label="2️⃣  Cancel Redeem" fromPosition="bottom" toPosition="bottom" theme="red" labelX=175 /%}
+{% edge from="mint" to="edition-pda" fromPosition="right" toPosition="right" /%}
+{% edge from="mint" to="metadata-pda" fromPosition="right" toPosition="right" /%}
+{% edge from="edition-pda" to="edition" path="straight" /%}
+{% edge from="metadata-pda" to="metadata" path="straight" /%}
+
+{% /diagram %}
+
+## 圧縮NFTの償還
+
+解凍プロセスの最初のステップを開始するために、圧縮NFTの所有者は**Redeem**命令を送信し、トランザクションに署名する必要があります。これにより、解凍プロセスの次のステップで使用されるcNFT用のVoucherアカウントが作成されます。
+
+この命令はBubblegum TreeからリーフKを削除することに注意してください。したがって、削除する圧縮NFTの整合性を検証するために追加のパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTの償還" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, redeem } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId)
+await redeem(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## 償還されたNFTの解凍
+
+解凍プロセスを完了するために、cNFTの所有者は償還されたVoucherアカウントを通常のNFTに変換する**Decompress**命令を送信する必要があります。以下のパラメータを提供する必要があります:
+
+- **Mint**: 作成するNFTのmintアドレス。これは圧縮NFTの**Asset ID**、つまりMerkle Treeアドレスとリーフのインデックスから派生したPDAでなければなりません。
+- **Voucher**: 前のステップで作成されたVoucherアカウントのアドレス。このアドレスも、Merkle Treeアドレスとリーフのインデックスから派生します。
+- **Metadata**: cNFTのすべてのデータを含むメタデータオブジェクト。この属性は圧縮NFTのデータと正確に一致する必要があります。そうでないと、ハッシュが一致せず、解凍が失敗します。
+
+ここでも、SDKが提供するヘルパー関数を使用して、Metaplex DAS APIからこれらの属性のほとんどを取得し、解析できます。
+
+{% dialect-switcher title="償還された圧縮NFTの解凍" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  findVoucherPda,
+  decompressV1,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId)
+await decompressV1(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+  mint: assetId,
+  voucher: findVoucherPda(umi, assetWithProof),
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## 償還されたNFTのキャンセル
+
+所有者がcNFTの解凍について気が変わった場合、**Cancel Redeem**命令を送信することで解凍プロセスをキャンセルできます。これにより、リーフがtreeに追加され、Voucherアカウントが閉じられます。**Decompress**命令と同様に、**Voucher**アドレスを提供する必要があり、Metaplex DAS APIを使用して取得できる他の属性も必要です。
+
+{% dialect-switcher title="償還された圧縮NFTの解凍をキャンセル" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  findVoucherPda,
+  cancelRedeem,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId)
+await cancelRedeem(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+  voucher: findVoucherPda(umi, assetWithProof),
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 70 - 0
src/pages/ja/bubblegum/delegate-cnfts.md

@@ -0,0 +1,70 @@
+---
+title: 圧縮NFTのDelegate
+metaTitle: 圧縮NFTのDelegate - Bubblegum
+description: Bubblegumで圧縮NFTをdelegateする方法を学びます。
+---
+
+圧縮NFTの所有者は、cNFTの所有権を保持しながら、別のアカウントにdelegateすることができます。 {% .lead %}
+
+これにより、委任されたアカウント(**Delegate Authority**とも呼ばれます)が所有者に代わってアクションを実行できます。これらのアクションは以下の通りです:
+
+- [cNFTの転送](/ja/bubblegum/transfer-cnfts)。Delegate Authorityは転送後にリセットされます(つまり、新しい所有者に設定されます)。
+- [cNFTのバーン](/ja/bubblegum/burn-cnfts)。
+
+これらのアクションのそれぞれで、Delegate Authorityを使用してそれらを実行する方法の例が提供されていますが、通常は単に**Leaf Owner**アカウントをSignerとして渡す代わりに**Leaf Delegate**アカウントをSignerとして提供するだけです。
+
+圧縮NFTのDelegate Authoritiesを承認および取り消す方法を見てみましょう。
+
+## Delegate Authorityの承認
+
+Delegate Authorityを承認または置き換えるために、所有者は**Delegate**命令を送信する必要があります。この命令は以下のパラメータを受け取ります:
+
+- **Leaf Owner**: 圧縮NFTの現在の所有者(Signerとして)。
+- **Previous Leaf Delegate**: 以前のDelegate Authority(存在する場合)。そうでない場合は、これを**Leaf Owner**に設定する必要があります。
+- **New Leaf Delegate**: 承認する新しいDelegate Authority。
+
+さらに、この命令はBubblegum Tree上のリーフを置き換えることになるため、圧縮NFTの整合性を検証するためにより多くのパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTのDelegate" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await delegate(umi, {
+  ...assetWithProof,
+  leafOwner,
+  previousLeafDelegate: leafOwner.publicKey,
+  newLeafDelegate: newDelegate,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Delegate Authorityの取り消し
+
+既存のDelegate Authorityを取り消すには、所有者は単に自分自身を新しいDelegate Authorityとして設定すればよいです。
+
+{% dialect-switcher title="圧縮NFTのDelegate Authorityの取り消し" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await delegate(umi, {
+  ...assetWithProof,
+  leafOwner,
+  previousLeafDelegate: currentDelegate,
+  newLeafDelegate: leafOwner.publicKey,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 57 - 0
src/pages/ja/bubblegum/delegate-trees.md

@@ -0,0 +1,57 @@
+---
+title: TreeのDelegate
+metaTitle: TreeのDelegate | Bubblegum
+description: BubblegumでMerkle Treesをdelegateする方法を学びます。
+---
+
+圧縮NFTの所有者がDelegate Authorityを承認できるのと同様に、Bubblegum Treeの作成者も他のアカウントに代理でアクションを実行することを承認できます。 {% .lead %}
+
+Bubblegum TreeにDelegate Authorityが承認されると、作成者に代わって[圧縮NFTをミント](/ja/bubblegum/mint-cnfts)できるようになります。これはプライベートtreeにのみ関連することに注意してください。なぜなら、パブリックtreeでは誰でもミントできるからです。
+
+## TreeのDelegate Authorityの承認
+
+Bubblegum Treeで新しいDelegate Authorityを承認するために、その作成者は以下のパラメータを受け取る**Set Tree Delegate**命令を使用できます:
+
+- **Merkle Tree**: delegateするMerkle Treeのアドレス。
+- **Tree Creator**: Merkle Treeの作成者(Signerとして)。
+- **New Tree Delegate**: 承認する新しいDelegate Authority。
+
+{% dialect-switcher title="Bubblegum TreeのDelegate" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { setTreeDelegate } from '@metaplex-foundation/mpl-bubblegum'
+
+await setTreeDelegate(umi, {
+  merkleTree,
+  treeCreator,
+  newTreeDelegate,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## TreeのDelegate Authorityの取り消し
+
+既存のDelegate Authorityを取り消すために、treeの作成者は単に自分自身を新しいDelegate Authorityとして設定すればよいです。
+
+{% dialect-switcher title="Bubblegum TreeのDelegate Authorityの取り消し" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { setTreeDelegate } from '@metaplex-foundation/mpl-bubblegum'
+
+await setTreeDelegate(umi, {
+  merkleTree,
+  treeCreator,
+  newTreeDelegate: treeCreator.publicKey,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 15 - 0
src/pages/ja/bubblegum/guides/index.md

@@ -0,0 +1,15 @@
+---
+title: ガイド
+metaTitle: ガイド | Bubblegum
+description: SolanaブロックチェーンのMetaplex Bubblegum圧縮NFTプログラムのガイドとチュートリアルのリストです。
+---
+
+現在利用可能なMPL Bubblegumのガイドは以下の通りです:
+
+{% quick-links %}
+
+{% quick-link title="Solanaで1,000,000のNFTコレクションを作成する方法" icon="CodeBracketSquare" href="/ja/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana" description="Bubblegum treesからcNFTを作成およびミントする方法を学びます" /%}
+
+{% quick-link title="他のSVMでcNFTとやりとりする方法" icon="CodeBracketSquare" href="/ja/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms" description="Solana devnetおよびmainnet-beta以外のSolana Virtual Machine(SVM)環境で、Metaplex Bubblegumプログラムを使用して圧縮NFTとやりとりする方法。" /%}
+
+{% /quick-links %}

+ 661 - 0
src/pages/ja/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana.md

@@ -0,0 +1,661 @@
+---
+title: Solanaで100万個のNFTを作成する
+metaTitle: Solanaで100万個のNFTを作成する | Bubblegum
+description: Metaplex Bubblegumプログラムを使用してSolanaで100万個のcNFTの圧縮NFTコレクションを作成する方法。
+---
+
+## 前提条件
+
+- お好みのコードエディタ(Visual Studio Codeを推奨)。
+- Node 18.x.x以上。
+- JavaScriptとスクリプトの実行に関する基本的な知識。
+
+## 初期設定
+
+このガイドでは、単一ファイルスクリプトに基づいたJavaScriptでの圧縮NFT(cNFT)アセットの作成について説明します。ニーズに合わせて関数を修正・移動する必要があるかもしれません。
+
+### 初期化
+
+選択したパッケージマネージャー(npm、yarn、pnpm、bun)で新しいプロジェクトを初期化し(任意)、プロンプトが表示されたら必要な詳細を入力します。
+
+```bash
+npm init
+```
+
+### 必要なパッケージ
+
+このガイドに必要なパッケージをインストールします。
+
+{% packagesUsed packages=["umi", "umiDefaults", "bubblegum", "tokenMetadata", "@metaplex-foundation/umi-uploader-irys"] type="npm" /%}
+
+```bash
+npm i @metaplex-foundation/umi
+```
+
+```bash
+npm i @metaplex-foundation/umi-bundle-defaults
+```
+
+```bash
+npm i @metaplex-foundation/mpl-bubblegum
+```
+
+```bash
+npm i @metaplex-foundation/mpl-token-metadata
+```
+
+```bash
+npm i @metaplex-foundation/umi-uploader-irys
+```
+
+### インポートとラッパー関数
+
+ここでは、この特定のガイドに必要なすべてのインポートを定義し、すべてのコードが実行されるラッパー関数を作成します。
+
+```ts
+import {
+  createTree,
+  findLeafAssetIdPda,
+  getAssetWithProof,
+  mintV1,
+  mplBubblegum,
+  parseLeafFromMintV1Transaction,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  createNft,
+  mplTokenMetadata,
+} from '@metaplex-foundation/mpl-token-metadata'
+import {
+  createGenericFile,
+  generateSigner,
+  percentAmount,
+  publicKey,
+  sol,
+} from '@metaplex-foundation/umi'
+import { Network, Wallet, umiInstance } from '../scripts/umi'
+
+import fs from 'fs'
+import { irysUploader } from '@metaplex-foundation/umi-uploader-irys'
+
+// ラッパー関数を作成
+const createCnft = async () => {
+  ///
+  ///
+  ///  すべてのコードはここに記述されます
+  ///
+  ///
+}
+
+// ラッパー関数を実行
+createCnft()
+```
+
+## Umiのセットアップ
+
+この例では`generatedSigner()`を使用してUmiをセットアップします。この例をReactで試したい場合は、`React - Umi w/ Wallet Adapter`ガイドを通してUmiをセットアップする必要があります。ウォレットのセットアップ以外は、このガイドはfileStorageキーとwallet adapterを使用します。
+
+### 新しいウォレットの生成
+
+```ts
+const umi = createUmi('https://api.devnet.solana.com')
+  .use(mplBubblegum())
+  .use(mplTokenMetadata())
+  .use(
+    irysUploader({
+      // mainnetアドレス: "https://node1.irys.xyz"
+      // devnetアドレス: "https://devnet.irys.xyz"
+      address: 'https://devnet.irys.xyz',
+    })
+  )
+
+const signer = generateSigner(umi)
+
+umi.use(signerIdentity(signer))
+
+// これはテスト用にdevnetでのみSOLをエアドロップします。
+console.log('Airdropping 1 SOL to identity')
+await umi.rpc.airdrop(umi.identity.publickey, sol(5))
+```
+
+### 既存のウォレットをローカルで使用
+
+```ts
+const umi = createUmi('https://api.devnet.solana.com')
+  .use(mplBubblegum())
+  .use(mplTokenMetadata())
+  .use(
+    irysUploader({
+      // mainnetアドレス: "https://node1.irys.xyz"
+      // devnetアドレス: "https://devnet.irys.xyz"
+      address: 'https://devnet.irys.xyz',
+    })
+  )
+
+// 新しいkeypair signerを生成。
+const signer = generateSigner(umi)
+
+// fsを使用して相対パスでファイルシステムをナビゲートし、
+// 使用したいウォレットをロードする必要があります。
+const walletFile = fs.readFileSync('./keypair.json')
+
+// walletFileをkeypairに変換。
+let keypair = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(walletFile))
+
+// keypairをumiにロード。
+umi.use(keypairIdentity(keypair))
+```
+
+## cNFTの作成
+
+Solanaでの cNFT の作成は非常に簡単で、実際にミントおよび読み取り操作を実行する前にいくつかの項目を準備する必要があります。
+
+- cNFTデータを保存するMerkle tree。
+- データ作成時にデータを保存しているインデクサからデータを読み取ることができるDAS対応RPC。
+
+#### Merkle Tree
+
+Merkle Treeは、ほとんどの場合、cNFTデータの「データベース」と考えることができます。Merkle Treeが作成され、cNFTが満杯になるまで追加できます。
+
+#### DAS RPC
+
+Merkle Tree cNFTデータの性質により、データはSolanaアカウントに保存されず、代わりにレジャー状態に保存されます。データを効果的に読み戻すために、データが作成/変更されるときにすべてのcNFTデータをインデックスするインデクサを使用する必要があります。DAS対応RPCは、DASインデクササービスを実行しているRPCであり、オンデマンドでこのデータをRPCプロバイダに照会することを可能にします。
+
+DASをサポートするRPCプロバイダの完全なリストについては、[RPCプロバイダページ](/ja/rpc-providers#rp-cs-with-das-support)をご覧ください。
+
+これらのプロバイダのいずれかから無料のアカウントを取得して、このガイドを実行できます。サインアップしたら、以前の`umi`作成中にRPCインスタンスを置き換える必要があります。
+
+```ts
+// 以下にあるアドレスを置き換えます。
+const umi = createUmi('https://rpcAddress.com')
+```
+
+### Treeの作成
+
+{% callout title="Treeコスト" type="warning" %}
+このガイドでは100万個のcNFTを保持するMerkle Treeを作成しており、これには約7.7 SOLのコストが必要です。準備ができるまで、このサンプルはdevnetでのみ試してください。Merkle Treeは閉じることも払い戻すこともできないためです。このコードを実行するには少なくとも7.7 devnet SOLが必要です。これには複数のエアドロップが必要な場合があります。
+{% /callout %}
+
+Solanaブロックチェーンに圧縮NFT(cNFT)を保存するには、データを保存する**Merkle Tree**を作成する必要があります。Merkle Treeのサイズとコストは、Merkle Treeの作成者によって決定され、すべてのcNFTオンチェーンストレージは事前に支払われます。これは通常、支払者がSolanaブロックチェーンでNFT自体をミントする時点で必要なストレージスペースとアカウント作成を支払うToken Metadataの**遅延ミント**アプローチとは異なり、bubblegumでは必要なすべてのデータスペースがtree作成時にtree作成者によって決定され、支払われます。
+
+Token Metadataと比較した**Merkle Tree**に関するいくつかのユニークな機能があり、人々が活用できます:
+
+- 1つのMerkle Tree内の複数のコレクションにcNFTをミントできます。
+
+Merkle Treeはコレクションではありません!
+
+Merkle Treeは多くのコレクションからのcNFTを収容でき、将来的に拡張された成長があることを知っているプロジェクトにとって信じられないほど強力です。Merkle Treeが100万個のcNFTを保持し、そのMerkle Treeに1万個のプロジェクトをリリースしてミントすることを決定した場合、将来的に追加のcNFTを書き込んでリリースするために、treeにはまだ99万個のスペースがあります。
+
+```ts
+//
+// ** Merkle Treeの作成 **
+//
+
+const merkleTree = generateSigner(umi)
+
+console.log(
+  'Merkle Tree Public Key:',
+  merkleTree.publicKey,
+  '\nStore this address as you will need it later.'
+)
+
+//   以下のパラメータでtreeを作成します。
+//   このtreeは約7.7 SOLのコストで作成され、最大
+//   100万個のリーフ/nftを収容できます。このスクリプトを実行する前に
+//   umi identityアカウントにいくつかのSOLをエアドロップする必要があるかもしれません。
+
+const createTreeTx = await createTree(umi, {
+  merkleTree,
+  maxDepth: 20,
+  maxBufferSize: 64,
+  canopyDepth: 14,
+})
+
+await createTreeTx.sendAndConfirm(umi)
+```
+
+### コレクションNFTの作成
+
+cNFTのコレクションは、Token Metadataと元のToken MetadataからミントされたコレクションNFTによって維持および管理されています。cNFTのコレクションを作成し、それにミントしたい場合は、Token MetadataコレクションNFTを作成する必要があります。
+
+```ts
+//
+// ** Token MetadataコレクションNFTの作成 **
+//
+
+//
+// NFTをコレクションにミントしたい場合は、まずコレクションNFTを作成する必要があります。
+// このステップは任意で、NFTをコレクションにミントしたくない場合や
+// 以前にコレクションNFTを作成したことがある場合はスキップできます。
+//
+
+const collectionSigner = generateSigner(umi)
+
+// 画像ファイルへのパス
+const collectionImageFile = fs.readFileSync('./collection.png')
+
+const genericCollectionImageFile = createGenericFile(
+  collectionImageFile,
+  'collection.png'
+)
+
+const collectionImageUri = await umi.uploader.upload([
+  genericCollectionImageFile,
+])
+
+const collectionMetadata = {
+  name: 'My cNFT Collection',
+  image: collectionImageUri[0],
+  externalUrl: 'https://www.example.com',
+  properties: {
+    files: [
+      {
+        uri: collectionImageUri[0],
+        type: 'image/png',
+      },
+    ],
+  },
+}
+
+const collectionMetadataUri = await umi.uploader.uploadJson(collectionMetadata)
+
+await createNft(umi, {
+  mint: collectionSigner.publicKey,
+  name: 'My cNFT Collection',
+  uri: 'https://www.example.com/collection.json',
+  isCollection: true,
+  sellerFeeBasisPoints: percentAmount(0),
+}).sendAndConfirm(umi)
+```
+
+### cNFT用画像とメタデータのアップロード(任意)
+
+cNFTにはデータと画像が必要です。このコードブロックでは、画像をアップロードし、その画像を`metadata`オブジェクトに追加し、最終的にそのオブジェクトをcNFTで使用するためにIrys経由でArweaveにjsonファイルとしてアップロードする方法を示しています。
+
+```ts
+//
+//   ** NFTに使用する画像とメタデータのアップロード(任意) **
+//
+
+//   すでに画像とメタデータファイルがアップロードされている場合は、このステップを
+//   スキップし、mintV1呼び出しでアップロードされたファイルのuriを使用できます。
+
+//   画像ファイルへのパス
+const nftImageFile = fs.readFileSync('./nft.png')
+
+const genericNftImageFile = createGenericFile(nftImageFile, 'nft.png')
+
+const nftImageUri = await umi.uploader.upload([genericNftImageFile])
+
+const nftMetadata = {
+  name: 'My cNFT',
+  image: nftImageUri[0],
+  externalUrl: 'https://www.example.com',
+  attributes: [
+    {
+      trait_type: 'trait1',
+      value: 'value1',
+    },
+    {
+      trait_type: 'trait2',
+      value: 'value2',
+    },
+  ],
+  properties: {
+    files: [
+      {
+        uri: nftImageUri[0],
+        type: 'image/png',
+      },
+    ],
+  },
+}
+
+const nftMetadataUri = await umi.uploader.uploadJson(nftMetadata)
+```
+
+### Merkle TreeにcNFTをミント
+
+treeにcNFTをミントすることは、Solanaブロックチェーン上での追加のアカウント/ストレージコストはかかりません。treeはすでにすべてのcNFTデータを保存するのに十分な部屋で作成されているためです(実際に100万個のcNFT)。ここでの唯一の追加コストは、基本的なSolanaトランザクション手数料のみであり、cNFTを大量にミントすることを信じられないほど効率的にします。
+
+```ts
+//
+// ** Merkle Treeに圧縮NFTをミント **
+//
+
+//
+// NFTをコレクションにミントしたくない場合は、collection
+// フィールドを`none()`に設定できます。
+//
+
+// ミントされるcNFTの所有者。
+const newOwner = publicKey('111111111111111111111111111111')
+
+console.log('Minting Compressed NFT to Merkle Tree...')
+
+const { signature } = await mintToCollectionV1(umi, {
+  leafOwner: newOwner,
+  merkleTree: merkleTree.publicKey,
+  collectionMint: collectionSigner.publicKey,
+  metadata: {
+    name: 'My cNFT',
+    uri: nftMetadataUri, // `nftMetadataUri`または以前にアップロードされたuriのいずれかを使用します。
+    sellerFeeBasisPoints: 500, // 5%
+    collection: { key: collectionSigner.publicKey, verified: false },
+    creators: [
+      {
+        address: umi.identity.publicKey,
+        verified: true,
+        share: 100,
+      },
+    ],
+  },
+}).sendAndConfirm(umi, { send: { commitment: 'finalized' } })
+```
+
+### 新しくミントされたcNFTの取得
+
+```ts
+//
+// ** アセットの取得 **
+//
+
+//
+// ここでは、ミントトランザクションのリーフインデックスを使用して圧縮NFTのアセットIDを見つけ、
+// アセット情報をログに記録します。
+//
+
+console.log('Finding Asset ID...')
+const leaf = await parseLeafFromMintV1Transaction(umi, signature)
+const assetId = findLeafAssetIdPda(umi, {
+  merkleTree: merkleTree.publicKey,
+  leafIndex: leaf.nonce,
+})
+
+console.log('Compressed NFT Asset ID:', assetId.toString())
+
+// DAS付きumiRpcを使用してアセットを取得します。
+const asset = await umi.rpc.getAsset(assetId[0])
+
+console.log({ asset })
+```
+
+### 100万個のcNFTのミント
+
+100万個のcNFTを保持するMerkle Treeを作成し、そのtreeにNFTをミントする方法を理解したので、前のすべてのステップを取り、必要なデータをArweaveにアップロードし、cNFTをtreeにミントするループを作成するようにコードを調整し始めることができます。
+
+Merkle Treeには100万個のcNFTのスペースがあるため、自由にループしてプロジェクトのニーズに応じてtreeを埋めることができます。
+
+以下は、ループインデックスに基づいてcNFTに保存されたデータをインクリメントするアドレス配列にcNFTをミントする例です。これは粗雑で簡単な例/概念であり、本番環境での使用には修正が必要です。
+
+```ts
+  const addresses = [
+    "11111111111111111111111111111111",
+    "22222222222222222222222222222222",
+    "33333333333333333333333333333333",
+    ...
+  ];
+
+  let index = 0;
+
+  for await (const address in addresses) {
+    const newOwner = publicKey(address);
+
+    console.log("Minting Compressed NFT to Merkle Tree...");
+
+    const { signature } = await mintV1(umi, {
+      leafOwner: newOwner,
+      merkleTree: merkleTree.publicKey,
+      metadata: {
+        name: `My Compressed NFT #${index}`,
+        uri: `https://example.com/${index}.json`, //metadataUriまたはアップロードされたメタデータファイルのuriを使用します
+        sellerFeeBasisPoints: 500, // 5%
+        collection: { key: collectionSigner.publicKey, verified: false },
+        creators: [
+          { address: umi.identity.publicKey, verified: true, share: 100 },
+        ],
+      },
+    }).sendAndConfirm(umi, { send: { commitment: "finalized" } });
+
+    index++;
+  }
+```
+
+## 完全なコード例
+
+```ts
+import {
+  createTree,
+  findLeafAssetIdPda,
+  mintToCollectionV1,
+  mplBubblegum,
+  parseLeafFromMintV1Transaction
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  createNft,
+  mplTokenMetadata,
+} from '@metaplex-foundation/mpl-token-metadata'
+import {
+  createGenericFile,
+  generateSigner,
+  keypairIdentity,
+  percentAmount,
+  publicKey
+} from '@metaplex-foundation/umi'
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { irysUploader } from '@metaplex-foundation/umi-uploader-irys'
+import fs from 'fs'
+
+// ラッパー関数を作成
+const createCnft = async () => {
+  //
+  // ** Umiのセットアップ **
+  //
+
+  // この例では、ローカルに保存されたウォレットを使用しています。これは
+  // 必要に応じて「新しいウォレットの生成」からのコードに置き換えることができますが、
+  // 新しいウォレットに少なくとも7.7 SOLをエアドロップ/送信するようにしてください。
+
+  const umi = createUmi('https://api.devnet.solana.com')
+    .use(mplBubblegum())
+    .use(mplTokenMetadata())
+    .use(
+      irysUploader({
+        // mainnetアドレス: "https://node1.irys.xyz"
+        // devnetアドレス: "https://devnet.irys.xyz"
+        address: 'https://devnet.irys.xyz',
+      })
+    )
+
+  // 新しいkeypair signerを生成。
+  const signer = generateSigner(umi)
+
+  // fsを使用して相対パスでファイルシステムをナビゲートし、
+  // 使用したいウォレットをロードする必要があります。
+  const walletFile = fs.readFileSync('./keypair.json')
+
+  // walletFileをkeypairに変換。
+  let keypair = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(walletFile))
+
+  // keypairをumiにロード。
+  umi.use(keypairIdentity(keypair))
+
+  //
+  // ** Merkle Treeの作成 **
+  //
+
+  const merkleTree = generateSigner(umi)
+
+  console.log(
+    'Merkle Tree Public Key:',
+    merkleTree.publicKey,
+    '\nStore this address as you will need it later.'
+  )
+
+  //   以下のパラメータでtreeを作成します。
+  //   このtreeは約7.7 SOLのコストで作成され、最大
+  //   100万個のリーフ/nftを収容できます。このスクリプトを実行する前に
+  //   umi identityアカウントにいくつかのSOLをエアドロップする必要があるかもしれません。
+
+  console.log('Creating Merkle Tree...')
+  const createTreeTx = await createTree(umi, {
+    merkleTree,
+    maxDepth: 20,
+    maxBufferSize: 64,
+    canopyDepth: 14,
+  })
+
+  await createTreeTx.sendAndConfirm(umi)
+
+  //
+  // ** Token Metadataコレクション NFTの作成(任意) **
+  //
+
+  //
+  // NFTをコレクションにミントしたい場合は、まずコレクションNFTを作成する必要があります。
+  // このステップは任意で、NFTをコレクションにミントしたくない場合や
+  // 以前にコレクションNFTを作成したことがある場合はスキップできます。
+  //
+
+  const collectionSigner = generateSigner(umi)
+
+  // 画像ファイルへのパス
+  const collectionImageFile = fs.readFileSync('./collection.png')
+
+  const genericCollectionImageFile = createGenericFile(
+    collectionImageFile,
+    'collection.png'
+  )
+
+  const collectionImageUri = await umi.uploader.upload([
+    genericCollectionImageFile,
+  ])
+
+  const collectionMetadata = {
+    name: 'My cNFT Collection',
+    image: collectionImageUri[0],
+    externalUrl: 'https://www.example.com',
+    properties: {
+      files: [
+        {
+          uri: collectionImageUri[0],
+          type: 'image/png',
+        },
+      ],
+    },
+  }
+
+  console.log('Uploading Collection Metadata...')
+  const collectionMetadataUri = await umi.uploader.uploadJson(
+    collectionMetadata
+  )
+
+  console.log('Creating Collection NFT...')
+  await createNft(umi, {
+    mint: collectionSigner,
+    name: 'My cNFT Collection',
+    uri: 'https://www.example.com/collection.json',
+    isCollection: true,
+    sellerFeeBasisPoints: percentAmount(0),
+  }).sendAndConfirm(umi)
+
+  //
+  //   ** NFTに使用する画像とメタデータのアップロード(任意) **
+  //
+
+  //   すでに画像とメタデータファイルがアップロードされている場合は、このステップを
+  //   スキップし、mintV1呼び出しでアップロードされたファイルのuriを使用できます。
+
+  //   画像ファイルへのパス
+  const nftImageFile = fs.readFileSync('./nft.png')
+
+  const genericNftImageFile = createGenericFile(nftImageFile, 'nft.png')
+
+  const nftImageUri = await umi.uploader.upload([genericNftImageFile])
+
+  const nftMetadata = {
+    name: 'My cNFT',
+    image: nftImageUri[0],
+    externalUrl: 'https://www.example.com',
+    attributes: [
+      {
+        trait_type: 'trait1',
+        value: 'value1',
+      },
+      {
+        trait_type: 'trait2',
+        value: 'value2',
+      },
+    ],
+    properties: {
+      files: [
+        {
+          uri: nftImageUri[0],
+          type: 'image/png',
+        },
+      ],
+    },
+  }
+
+  console.log('Uploading cNFT metadata...')
+  const nftMetadataUri = await umi.uploader.uploadJson(nftMetadata)
+
+  //
+  // ** Merkle Treeに圧縮NFTをミント **
+  //
+
+  //
+  // NFTをコレクションにミントしたくない場合は、collection
+  // フィールドを`none()`に設定できます。
+  //
+
+  // ミントされるcNFTの所有者。
+  const newOwner = publicKey('111111111111111111111111111111')
+
+  console.log('Minting Compressed NFT to Merkle Tree...')
+
+const { signature } = await mintToCollectionV1(umi, {
+  leafOwner: newOwner,
+  merkleTree: merkleTree.publicKey,
+  collectionMint: collectionSigner.publicKey,
+  metadata: {
+    name: 'My cNFT',
+    uri: nftMetadataUri, // `nftMetadataUri`または以前にアップロードされたuriのいずれかを使用します。
+    sellerFeeBasisPoints: 500, // 5%
+    collection: { key: collectionSigner.publicKey, verified: false },
+    creators: [
+      {
+        address: umi.identity.publicKey,
+        verified: true,
+        share: 100,
+      },
+    ],
+  },
+}).sendAndConfirm(umi, { send: { commitment: 'finalized' } })
+
+  //
+  // ** アセットの取得 **
+  //
+
+  //
+  // ここでは、ミントトランザクションのリーフインデックスを使用して圧縮NFTのアセットIDを見つけ、
+  // アセット情報をログに記録します。
+  //
+
+  console.log('Finding Asset ID...')
+  const leaf = await parseLeafFromMintV1Transaction(umi, signature)
+  const assetId = findLeafAssetIdPda(umi, {
+    merkleTree: merkleTree.publicKey,
+    leafIndex: leaf.nonce,
+  })
+
+  console.log('Compressed NFT Asset ID:', assetId.toString())
+
+  // DAS付きumiRpcを使用してアセットを取得します。
+  const asset = await umi.rpc.getAsset(assetId[0])
+
+  console.log({ asset })
+};
+
+// ラッパー関数を実行
+createCnft();
+```

+ 150 - 0
src/pages/ja/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms.md

@@ -0,0 +1,150 @@
+---
+title: 他のSVMでcNFTとやりとりする方法
+metaTitle: 他のSVMでcNFTとやりとりする方法 | Bubblegum
+description: Solanaのdevnetおよびmainnet-beta以外のSolana Virtual Machine(SVM)環境で、Metaplex Bubblegumプログラムを使用して圧縮NFTとやりとりする方法。
+---
+
+## 概要
+
+このガイドでは、Solanaのdevnetおよびmainnet-beta以外のSolana Virtual Machine(SVM)環境でJavaScriptを使用して圧縮NFT(cNFT)アセットとやりとりするための特定の要件について詳しく説明します。cNFTの作成のより包括的な概要については、[BubblegumでSolanaで100万個のNFTを作成する](/ja/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana)ガイドを参照してください。
+
+### 必要なパッケージ
+
+このガイドでは、`@metaplex-foundation/mpl-bubblegum`の特定のベータnpmパッケージを使用します。以下を使用してインストールしてください:
+
+```bash
+npm -i @metaplex-foundation/mpl-bubblegum@4.3.1-beta.0
+```
+
+### SVMへの接続
+
+SVMのエンドポイントを使用してumiインスタンスを作成する必要があることに注意してください。
+
+```ts
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
+
+const umi = createUmi('<SVM用のRPCエンドポイント>')
+  .use(mplBubblegum())
+  .use(mplTokenMetadata())
+  ...
+```
+
+### Treeの作成
+
+{% callout title="Treeコスト" type="warning" %}
+Treeサイズと使用している特定のSVMによって異なる実際の前払いSOLコストを持つMerkle Treeを作成しています。準備ができるまで、このサンプルはdevnetでのみ試してください。Merkle Treeは閉じることも払い戻すこともできないためです。
+{% /callout %}
+
+treeの作成は、Solana devnet/mainnet-betaで使用されるのと同じ`createTree`関数を使用して行うことができます。ただし、デフォルトの`logWrapper`と`compressionProgram`の値をオーバーライドする必要があります。これは次のように簡単に実現できます:
+
+```ts
+import {
+  createTree,
+  MPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
+  MPL_NOOP_PROGRAM_ID,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  publicKey,
+} from '@metaplex-foundation/umi';
+
+// SVM用の正しい`logWrapper`と
+// `compressionProgram`を指定してMerkle Treeを作成します。
+const merkleTree = generateSigner(umi);
+const createTreeTx = await createTree(umi, {
+  merkleTree,
+  maxDepth: 3,
+  maxBufferSize: 8,
+  canopyDepth: 0,
+  logWrapper: MPL_NOOP_PROGRAM_ID,
+  compressionProgram: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
+});
+
+await createTreeTx.sendAndConfirm(umi);
+```
+
+ただし、これらのプログラムIDを自動的に解決するヘルパー関数が提供されており、SolanaのdevnetやmainnetやBubblegumがデプロイされた他のSVMでも動作するため、これが推奨されるアプローチです:
+
+```ts
+import {
+  getCompressionPrograms,
+  createTree,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  publicKey,
+} from '@metaplex-foundation/umi';
+
+// `getCompressionPrograms`ヘルパー関数を使用してMerkle Treeを作成します。
+const merkleTree = generateSigner(umi);
+const createTreeTx = await createTree(umi, {
+  merkleTree,
+  maxDepth: 3,
+  maxBufferSize: 8,
+  canopyDepth: 0,
+  ...(await getCompressionPrograms(umi)),
+});
+
+await createTreeTx.sendAndConfirm(umi);
+```
+
+### cNFTのミントと転送
+
+他のSVMでMerkle Treeを作成する場合と同様に、`mintV1`や`transfer`などの他のSDK関数も圧縮プログラムを指定する必要があります。再び`getCompressionPrograms`ヘルパーを使用します。
+
+```ts
+import {
+  fetchMerkleTree,
+  getCurrentRoot,
+  hashMetadataCreators,
+  hashMetadataData,
+  transfer,
+  getCompressionPrograms,
+  createTree,
+  MetadataArgsArgs,
+  mintV1,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  none,
+} from '@metaplex-foundation/umi';
+
+// ミント前にリーフインデックスを取得します。
+const leafIndex = Number(
+  (await fetchMerkleTree(umi, merkleTree.publicKey)).tree.activeIndex
+);
+
+// メタデータを定義します。
+const metadata: MetadataArgsArgs = {
+  name: 'My NFT',
+  uri: 'https://example.com/my-nft.json',
+  sellerFeeBasisPoints: 500, // 5%
+  collection: none(),
+  creators: [],
+};
+
+// cNFTをミントします。
+const originalOwner = generateSigner(umi);
+const mintTxn = await mintV1(umi, {
+  leafOwner: originalOwner.publicKey,
+  merkleTree: merkleTree.publicKey,
+  metadata,
+  ...(await getCompressionPrograms(umi)),
+}).sendAndConfirm(umi);
+
+// cNFTを新しい所有者に転送します。
+const newOwner = generateSigner(umi);
+const merkleTreeAccount = await fetchMerkleTree(umi, merkleTree.publicKey);
+const transferTxn = await transfer(umi, {
+  leafOwner: originalOwner,
+  newLeafOwner: newOwner.publicKey,
+  merkleTree: merkleTree.publicKey,
+  root: getCurrentRoot(merkleTreeAccount.tree),
+  dataHash: hashMetadataData(metadata),
+  creatorHash: hashMetadataCreators(metadata.creators),
+  nonce: leafIndex,
+  index: leafIndex,
+  proof: [],
+  ...(await getCompressionPrograms(umi)),
+}).sendAndConfirm(umi);
+```

+ 145 - 0
src/pages/ja/bubblegum/index.md

@@ -0,0 +1,145 @@
+---
+title: 概要
+metaTitle: 概要 | Bubblegum
+description: 圧縮NFTの概要を説明します。
+---
+
+{% callout type="note" title="新しいBubblegumバージョン" %}
+
+より多くの柔軟性と機能を可能にするために、[Bubblegum v2](/ja/bubblegum-v2)の使用をお勧めします。
+
+{% /callout %}
+
+Bubblegumは、Solana上で圧縮NFT(cNFT)を作成し、操作するためのMetaplexプロトコルプログラムです。圧縮NFTは、オンチェーンでのデータ保存方法を再考することで、NFT作成を新しい規模のレベルまでスケールさせることを可能にします。 {% .lead %}
+
+{% quick-links %}
+
+{% quick-link title="はじめに" icon="InboxArrowDown" href="/ja/bubblegum/getting-started" description="お好みの言語またはライブラリを見つけて、圧縮NFTを開始しましょう。" /%}
+
+{% quick-link title="APIリファレンス" icon="CodeBracketSquare" href="https://mpl-bubblegum.typedoc.metaplex.com/" target="_blank" description="特定のものをお探しですか?APIリファレンスをご覧いただき、答えを見つけてください。" /%}
+
+{% /quick-links %}
+
+## はじめに
+
+NFTがSolanaブロックチェーン上で繁栄するにつれて、NFTがインターネット上の任意のデジタルアセットと同じように普遍的である必要性が高まっています:ゲームのインベントリ内のすべてのアイテム、お気に入りのコンシューマーアプリでのエンゲージメントの証明、さらには地球上のすべての人間のプロファイルなど。
+
+しかし、これまでのところ、これらのタイプの製品は、比較的安価でありながら線形にスケールするSolana上のNFTのレント費用によって制約されてきました。NFTの圧縮により、NFTのオンチェーンストレージコストが大幅に削減され、クリエイターが技術を望む限り表現できるようになります。
+
+マークルツリーを使用してSolana上でcNFTプロジェクトを開始することは非常に費用対効果が高く、コストは以下のように低く抑えられます:
+
+| cNFTの数 | ストレージコスト | トランザクションコスト | 総コスト | cNFTあたりのコスト |
+| -------- | --------------- | --------------------- | -------- | ----------------- |
+| 10,000   | 0.2222          | 0.05                  | 0.2722   | 0.000027222       |
+| 100,000  | 0.2656          | 0.5                   | 0.7656   | 0.000007656       |
+| 1,000,000| 0.3122          | 5                     | 5.3122   | 0.000005312       |
+| 10,000,000| 0.4236         | 50                    | 50.4236  | 0.000005042       |
+| 100,000,000| 7.2205        | 500                   | 507.2205 | 0.000005072       |
+| 1,000,000,000| 7.2205       | 5,000                 | 5007.2205| 0.000005007       |
+
+これらの圧縮NFTは、転送、デリゲート、さらには既存のスマートコントラクトとの相互運用性のために通常のNFTに解凍することも可能です。
+
+## マークルツリー、リーフ、証明
+
+圧縮NFTは、**マークルツリー**のコンテキストでのみ存在します。マークルツリーが何であるかは[専用の高度なガイド](/ja/bubblegum/concurrent-merkle-trees)で説明していますが、この概要では、マークルツリーを**リーフ**と呼ぶハッシュのコレクションと考えることができます。各リーフは、[圧縮NFTのデータをハッシュ化する](/ja/bubblegum/hashed-nft-data)ことで得られます。
+
+マークルツリー内の各リーフに対して、**証明**と呼ばれるハッシュのリストを提供できます。これにより、誰でも与えられたリーフがそのツリーの一部であることを検証できます。圧縮NFTが更新または転送されるたびに、関連するリーフも変更され、その証明も変更されます。
+
+{% diagram %}
+
+{% node #root label="ルートノード" theme="slate" /%}
+{% node #root-hash label="ハッシュ" parent="root" x="56" y="40" theme="transparent" /%}
+{% node #node-1 label="ノード 1" parent="root" y="100" x="-200" theme="blue" /%}
+{% node #node-1-hash label="ハッシュ" parent="node-1" x="42" y="40" theme="transparent" /%}
+{% node #node-2 label="ノード 2" parent="root" y="100" x="200" theme="mint" /%}
+
+{% node #node-3 label="ノード 3" parent="node-1" y="100" x="-100" theme="mint" /%}
+{% node #node-4 label="ノード 4" parent="node-1" y="100" x="100" theme="blue" /%}
+{% node #node-4-hash label="ハッシュ" parent="node-4" x="42" y="40" theme="transparent" /%}
+{% node #node-5 label="ノード 5" parent="node-2" y="100" x="-100" /%}
+{% node #node-6 label="ノード 6" parent="node-2" y="100" x="100" /%}
+
+{% node #leaf-1 label="リーフ 1" parent="node-3" y="100" x="-45" /%}
+{% node #leaf-2 label="リーフ 2" parent="node-3" y="100" x="55" /%}
+{% node #leaf-3 label="リーフ 3" parent="node-4" y="100" x="-45" theme="blue" /%}
+{% node #leaf-4 label="リーフ 4" parent="node-4" y="100" x="55" theme="mint" /%}
+{% node #leaf-5 label="リーフ 5" parent="node-5" y="100" x="-45" /%}
+{% node #leaf-6 label="リーフ 6" parent="node-5" y="100" x="55" /%}
+{% node #leaf-7 label="リーフ 7" parent="node-6" y="100" x="-45" /%}
+{% node #leaf-8 label="リーフ 8" parent="node-6" y="100" x="55" /%}
+{% node #nft label="NFTデータ" parent="leaf-3" y="100" x="-12" theme="blue" /%}
+
+{% node #proof-1 label="リーフ 4" parent="nft" x="200" theme="mint" /%}
+{% node #proof-2 label="ノード 3" parent="proof-1" x="90" theme="mint" /%}
+{% node #proof-3 label="ノード 2" parent="proof-2" x="97" theme="mint" /%}
+{% node #proof-legend label="証明" parent="proof-1" x="-6" y="-20" theme="transparent" /%}
+
+{% edge from="node-1" to="root" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="node-2" to="root" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+
+{% edge from="node-3" to="node-1" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+{% edge from="node-4" to="node-1" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="node-6" to="node-2" fromPosition="top" toPosition="bottom" /%}
+{% edge from="node-5" to="node-2" fromPosition="top" toPosition="bottom" /%}
+
+{% edge from="leaf-1" to="node-3" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-2" to="node-3" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-4" to="node-4" fromPosition="top" toPosition="bottom" theme="mint" animated=true /%}
+{% edge from="leaf-3" to="node-4" fromPosition="top" toPosition="bottom" theme="blue" animated=true /%}
+{% edge from="leaf-5" to="node-5" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-6" to="node-5" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-7" to="node-6" fromPosition="top" toPosition="bottom" /%}
+{% edge from="leaf-8" to="node-6" fromPosition="top" toPosition="bottom" /%}
+{% edge from="nft" to="leaf-3" fromPosition="top" toPosition="bottom" theme="blue" animated=true label="ハッシュ" /%}
+
+{% /diagram %}
+
+このように、マークルツリーは、与えられた圧縮NFTが存在することを誰でも検証できるオンチェーン構造として機能します。これらは、非常にスケーラブルにするNFTデータを保存せずにこれを行います。
+
+これは重要な質問をもたらします:NFTデータはどこに保存されているのでしょうか?
+
+## Metaplex DAS API
+
+新しい圧縮NFTをミントすると、そのデータがハッシュ化され、マークルツリーに新しいリーフとして追加されます。しかし、それだけではありません。さらに、NFT全体のデータは圧縮NFTを作成したトランザクションに保存されます。同様に、圧縮NFTが更新されると、その更新されたデータは、再度、変更ログとしてトランザクションに保存されます。そのため、そのデータを追跡するアカウントはありませんが、台帳内のすべての以前のトランザクションを見て、その情報を見つけることができます。
+
+{% diagram %}
+
+{% node #tx-1 label="トランザクション 1" /%}
+{% node #tx-2 label="トランザクション 2" parent="tx-1" y="50" /%}
+{% node #tx-3 label="トランザクション 3" parent="tx-2" y="50" /%}
+{% node #tx-4 label="トランザクション 4" parent="tx-3" y="50" /%}
+{% node #tx-5 label="トランザクション 5" parent="tx-4" y="50" /%}
+{% node #tx-rest label="..." parent="tx-5" y="50" /%}
+
+{% node #nft-1 label="初期NFTデータ" parent="tx-2" x="300" theme="blue" /%}
+{% node #nft-2 label="NFTデータ変更ログ" parent="tx-3" x="300" theme="blue" /%}
+{% node #nft-3 label="NFTデータ変更ログ" parent="tx-5" x="300" theme="blue" /%}
+
+{% edge from="nft-1" to="tx-2" label="保存先" /%}
+{% edge from="nft-2" to="tx-3" label="保存先" /%}
+{% edge from="nft-3" to="tx-5" label="保存先" /%}
+
+{% /diagram %}
+
+1つのNFTのデータを取得するためだけに、毎回何百万ものトランザクションをクロールすることは、確実に最良のユーザー体験ではありません。したがって、圧縮NFTは、この情報をリアルタイムでインデックス化し、エンドユーザーからこれを抽象化するために一部のRPCに依存しています。圧縮NFTの取得を可能にする結果のRPC APIを**Metaplex DAS API**と呼びます。
+
+すべてのRPCがDAS APIをサポートしているわけではないことに注意してください。そのため、アプリケーションで圧縮NFTを使用する際に適切なRPCを選択するために、["Metaplex DAS API RPC"](/ja/rpc-providers)ページに興味があるかもしれません。
+
+これについては、高度な["NFTデータの保存とインデックス化"](/ja/bubblegum/stored-nft-data)ガイドで詳しく説明しています。
+
+## 機能
+
+NFTデータがアカウント内に存在しないにもかかわらず、圧縮NFTに対してさまざまな操作を実行することは依然として可能です。これは、現在のNFTデータをリクエストし、そのハッシュ化されたリーフがマークルツリーで有効であることを確認することで可能になります。そのため、圧縮NFTで以下の操作を実行できます:
+
+- 関連するコレクションを持つまたは持たない[cNFTのミント](/ja/bubblegum/mint-cnfts)。
+- [cNFTの転送](/ja/bubblegum/transfer-cnfts)。
+- [cNFTのデータの更新](/ja/bubblegum/update-cnfts)。
+- [cNFTのバーン](/ja/bubblegum/burn-cnfts)。
+- [cNFTを通常のNFTに解凍](/ja/bubblegum/decompress-cnfts)。これは既存のスマートコントラクトとの相互運用性を可能にしますが、レント費用付きのオンチェーンアカウントを作成することに注意してください。
+- [cNFTのデリゲート](/ja/bubblegum/delegate-cnfts)。
+- [cNFTコレクションの検証と検証解除](/ja/bubblegum/verify-collections)。
+- [cNFTの作成者の検証と検証解除](/ja/bubblegum/verify-creators)。
+
+## 次のステップ
+
+圧縮NFTが高レベルでどのように機能するかを理解したので、圧縮NFTとの相互作用に使用できるさまざまな言語/フレームワークを列挙した[はじめに](/ja/bubblegum/getting-started)ページをチェックすることをお勧めします。その後、さまざまな[機能ページ](/ja/bubblegum/create-trees)を使用して、cNFTで実行できる特定の操作について詳しく学ぶことができます。最後に、cNFTとマークルツリーの知識を深めるための[高度なガイド](/ja/bubblegum/concurrent-merkle-trees)も利用できます。

+ 195 - 0
src/pages/ja/bubblegum/mint-cnfts.md

@@ -0,0 +1,195 @@
+---
+title: 圧縮NFTのミント
+metaTitle: 圧縮NFTのミント | Bubblegum
+description: Bubblegumで圧縮NFTをミントする方法を学びます。
+---
+{% callout title="Bubblegum v2" type="note" %}
+このページはBubblegum v1に固有です。拡張された機能セットについては、Bubblegum v2の使用をお勧めします。Bubblegum v2を使用している場合は、詳細について[Bubblegum v2](/ja/bubblegum-v2/mint-cnfts)のドキュメントを参照してください。
+{% /callout %}
+
+[前のページ](/ja/bubblegum/create-trees)では、圧縮NFTをミントするためにBubblegum Treeが必要であり、その作成方法を見てきました。今度は、指定されたBubblegum Treeから圧縮NFTをミントする方法を見てみましょう。 {% .lead %}
+
+BubblegumプログラムはTwo つのミント命令を提供します。1つはNFTをコレクションに関連付けずにミントするもの、もう1つはNFTを指定されたコレクションにミントするものです。後者は単にいくつかの追加パラメータが必要なだけなので、まず前者から見てみましょう。
+
+## コレクションなしでのミント
+
+BubblegumプログラムはBubblegum Treeから圧縮NFTをミントできる**Mint V1**命令を提供します。Bubblegum Treeが公開されている場合、誰でもこの命令を使用できます。そうでない場合は、Tree CreatorまたはTree Delegateのみが使用できます。
+
+Mint V1命令の主要パラメータは以下の通りです:
+
+- **Merkle Tree**: 圧縮NFTがミントされるMerkle Treeのアドレス。
+- **Tree Creator Or Delegate**: Bubblegum Treeからのミントを許可された権限 — これはtreeの作成者またはdelegateのいずれかです。この権限はトランザクションに署名する必要があります。パブリックtreeの場合、このパラメータは任意の権限にできますが、それでも署名者である必要があります。
+- **Leaf Owner**: ミントされる圧縮NFTの所有者。
+- **Leaf Delegate**: ミントされたcNFTを管理することが許可されたdelegate権限(存在する場合)。そうでない場合は、Leaf Ownerに設定されます。
+- **Metadata**: ミントされる圧縮NFTのメタデータ。これには、NFTの**名前**、その**URI**、その**コレクション**、その**作成者**などの情報が含まれます。
+  - メタデータ内で**Collection**オブジェクトを提供することは可能ですが、Collection Authorityがこの命令で要求されておらず、したがってトランザクションに署名できないため、その**Verified**フィールドは`false`に設定する必要があることに注意してください。
+  - また、作成者はミント時にcNFT上で自分自身を検証できることにも注意してください。これを機能させるには、**Creator**オブジェクトの**Verified**フィールドを`true`に設定し、残りのアカウントに作成者をSignerとして追加する必要があります。これは、すべての作成者がトランザクションに署名し、残りのアカウントに追加される限り、複数の作成者に対して実行できます。
+
+{% dialect-switcher title="コレクションなしで圧縮NFTをミント" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { none } from '@metaplex-foundation/umi'
+import { mintV1 } from '@metaplex-foundation/mpl-bubblegum'
+
+await mintV1(umi, {
+  leafOwner,
+  merkleTree,
+  metadata: {
+    name: 'My Compressed NFT',
+    uri: 'https://example.com/my-cnft.json',
+    sellerFeeBasisPoints: 500, // 5%
+    collection: none(),
+    creators: [
+      { address: umi.identity.publicKey, verified: false, share: 100 },
+    ],
+  },
+}).sendAndConfirm(umi)
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### ミントトランザクションからのリーフスキーマとアセットIDの取得 {% #get-leaf-schema-from-mint-transaction %}
+
+`parseLeafFromMintV1Transaction`ヘルパーを使用して、`mintV1`トランザクションからリーフを取得し、アセットIDを決定できます。この関数はTransactionを解析するため、`parseLeafFromMintV1Transaction`を呼び出す前にトランザクションが最終化されていることを確認する必要があります。
+
+{% callout type="note" title="トランザクションの最終化" %}
+`parseLeafFromMintV1Transaction`を呼び出す前に、トランザクションが最終化されていることを確認してください。
+{% /callout %}
+
+{% dialect-switcher title="ミントトランザクションからリーフスキーマを取得" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import {
+    findLeafAssetIdPda,
+    mintV1,
+    parseLeafFromMintV1Transaction
+} from "@metaplex-foundation/mpl-bubblegum";
+
+const { signature } = await mintV1(umi, {
+  leafOwner,
+  merkleTree,
+  metadata,
+}).sendAndConfirm(umi, { confirm: { commitment: "finalized" } });
+
+const leaf: LeafSchema = await parseLeafFromMintV1Transaction(umi, signature);
+const assetId = findLeafAssetIdPda(umi, { merkleTree, leafIndex: leaf.nonce });
+// or const assetId = leaf.id;
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## コレクションへのミント
+
+圧縮NFTがミントされた_後に_コレクションを設定および検証することは可能ですが、Bubblegumプログラムは圧縮NFTを指定されたコレクションに直接ミントする便利な命令を提供します。BubblegumはMetaplex Token MetadataコレクションNFTを使用して圧縮NFTをグループ化します。この命令は**MintToCollectionV1**と呼ばれ、**MintV1**命令と同じパラメータを使用しますが、以下のパラメータが追加されます:
+
+- **Collection Mint**: 圧縮NFTが属する[Token Metadata Collection NFT](https://developers.metaplex.com/ja/token-metadata/collections#creating-collection-nfts)のミントアドレス。
+- **Collection Authority**: 指定されたCollection NFTを管理することが許可された権限。これは、Collection NFTのupdate authorityまたは委任されたコレクション権限のいずれかです。Bubblegum Treeが公開されているかどうかに関係なく、この権限はトランザクションに署名する必要があります。
+- **Collection Authority Record Pda**: 委任されたコレクション権限を使用する場合、権限がCollection NFTを管理することが許可されていることを確認するために、Delegate Record PDAを提供する必要があります。これは、新しい「Metadata Delegate」PDAまたはレガシーの「Collection Authority Record」PDAのいずれかを使用できます。
+
+さらに、**Metadata**パラメータには次のような**Collection**オブジェクトが含まれている必要があることに注意してください:
+
+- その**Address**フィールドが**Collection Mint**パラメータと一致している。
+- その**Verified**フィールドは`true`または`false`のいずれかとして渡すことができます。`false`として渡された場合、トランザクション中に`true`に設定され、cNFTは**Verified**を`true`に設定してミントされます。
+
+また、**Mint V1**命令と同様に、作成者はトランザクションに署名し、残りのアカウントに自分自身を追加することで自分自身を検証できることにも注意してください。
+
+{% dialect-switcher title="圧縮NFTをコレクションにミント" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { none } from '@metaplex-foundation/umi'
+import { mintToCollectionV1 } from '@metaplex-foundation/mpl-bubblegum'
+
+await mintToCollectionV1(umi, {
+  leafOwner,
+  merkleTree,
+  collectionMint,
+  metadata: {
+    name: 'My Compressed NFT',
+    uri: 'https://example.com/my-cnft.json',
+    sellerFeeBasisPoints: 500, // 5%
+    collection: { key: collectionMint, verified: false },
+    creators: [
+      { address: umi.identity.publicKey, verified: false, share: 100 },
+    ],
+  },
+}).sendAndConfirm(umi)
+```
+
+デフォルトでは、Collection AuthorityはUmi identityに設定されますが、以下の例に示すようにカスタマイズできます。
+
+```ts
+const customCollectionAuthority = generateSigner(umi)
+await mintToCollectionV1(umi, {
+  // ...
+  collectionAuthority: customCollectionAuthority,
+})
+```
+
+{% totem-accordion title="コレクションNFTの作成" %}
+
+まだコレクションNFTをお持ちでない場合は、`@metaplex-foundation/mpl-token-metadata`ライブラリを使用して作成できます。
+
+```shell
+npm install @metaplex-foundation/mpl-token-metadata
+```
+
+そして、次のようにコレクションNFTを作成します:
+
+```ts
+import { generateSigner, percentAmount } from '@metaplex-foundation/umi'
+import { createNft } from '@metaplex-foundation/mpl-token-metadata'
+
+const collectionMint = generateSigner(umi)
+await createNft(umi, {
+  mint: collectionMint,
+  name: 'My Collection',
+  uri: 'https://example.com/my-collection.json',
+  sellerFeeBasisPoints: percentAmount(5.5), // 5.5%
+  isCollection: true,
+}).sendAndConfirm(umi)
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+### mintToCollectionトランザクションからのリーフスキーマとアセットIDの取得 {% #get-leaf-schema-from-mint-to-collection-transaction %}
+
+同様に、`parseLeafFromMintToCollectionV1Transaction`ヘルパーを使用して、`mintToCollectionV1`トランザクションからリーフを取得し、アセットIDを決定できます。
+
+{% callout type="note" title="トランザクションの最終化" %}
+`parseLeafFromMintToCollectionV1Transaction`を呼び出す前に、トランザクションが最終化されていることを確認してください。
+{% /callout %}
+
+{% dialect-switcher title="mintToCollectionV1トランザクションからリーフスキーマを取得" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import {
+    findLeafAssetIdPda,
+    mintV1,
+    parseLeafFromMintToCollectionV1Transaction
+} from "@metaplex-foundation/mpl-bubblegum";
+
+const { signature } = await mintToCollectionV1(umi, {
+  leafOwner,
+  merkleTree,
+  metadata,
+  collectionMint: collectionMint.publicKey,
+}).sendAndConfirm(umi);
+
+const leaf: LeafSchema = await parseLeafFromMintToCollectionV1Transaction(umi, signature);
+const assetId = findLeafAssetIdPda(umi, { merkleTree, leafIndex: leaf.nonce });
+// or const assetId = leaf.id;
+```
+
+{% /dialect %}
+{% /dialect-switcher %}

+ 15 - 0
src/pages/ja/bubblegum/sdk/index.md

@@ -0,0 +1,15 @@
+---
+title: MPL-Bubblegum SDK
+metaTitle: SDK | Bubblegum
+description: MPL-Bubblegum SDKを使用してMetaplexの圧縮NFT標準(cNFT)を開始する方法を学びます。
+---
+
+MPL-Bubblegumを使用した新しいMetaplex圧縮NFT(cNFT)標準を開始するために、以下から使用したい言語/ライブラリを選択してください。
+
+{% quick-links %}
+
+{% quick-link title="JavaScript" icon="JavaScript" href="/ja/bubblegum/sdk/javascript" description="Umiフレームワークベースの Bubblegum 用 JavaScript ライブラリを開始しましょう。" /%}
+
+{% quick-link title="Rust" icon="Rust" href="/ja/bubblegum/sdk/rust" description="MPL-Bubblegum Rustクレートを使用して開始しましょう。" /%}
+
+{% /quick-links %}

+ 47 - 0
src/pages/ja/bubblegum/sdk/javascript.md

@@ -0,0 +1,47 @@
+---
+title: MPL-Bubblegum JavaScript SDK
+metaTitle: JavaScript SDK | MPL-Bubblegum
+description: MPL-Bubblegum JavaScript SDKを実行するためのプロジェクト設定方法を学びます。
+---
+
+MetaplexはMPL-BubblegumプログラムとやりとりするためのJavaScriptライブラリを提供しています。[Umiフレームワーク](/ja/umi)のおかげで、多くの独断的な依存関係なしに配布されるため、任意のJavaScriptプロジェクトで使用できる軽量なライブラリを提供します。
+
+開始するには、[Umiフレームワークをインストール](/ja/umi/getting-started)し、MPL-Bubblegum JavaScriptライブラリをインストールする必要があります。
+
+## インストール
+
+インストールは、npm、yarn、bunなどの任意のJSパッケージマネージャーで実行できます。
+
+```sh
+npm install @metaplex-foundation/mpl-bubblegum
+```
+
+{% quick-links %}
+
+{% quick-link title="typedoc" target="_blank" icon="JavaScript" href="https://mpl-bubblegum.typedoc.metaplex.com/" description="MPL-Bubblegum JavaScript SDK生成パッケージAPIドキュメント。" /%}
+
+{% quick-link title="npmjs.com" target="_blank" icon="JavaScript" href="https://www.npmjs.com/package/@metaplex-foundation/MPL-Bubblegum" description="NPM上のMPL-Bubblegum JavaScript SDK。" /%}
+
+{% /quick-links %}
+
+## Umiセットアップ
+
+Metaplex JavaScript SDKとやりとりするには`umi`インスタンスが必要です。まだ`umi`インスタンスをセットアップおよび設定していない場合は、[Umi入門](/ja/umi/getting-started)ページをチェックし、RPCエンドポイントと`umi`のidentity/signerを設定してください。
+
+`umi`インスタンスの初期化中に、以下を使用してMPL-Bubblegumパッケージを`umi`に追加できます:
+
+```js
+.use(mplBubblegum())
+```
+
+```ts
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { mplBubblegum } from '@metaplex-foundation/mpl-bubblegum'
+
+// 選択したRPCエンドポイントを使用します。
+const umi = createUmi('http://api.devnet.solana.com')
+... // 追加のumi設定、パッケージ、signers
+.use(mplBubblegum())
+```
+
+ここから、あなたの`umi`インスタンスはMPL-Bubblegumパッケージにアクセスでき、MPL-Bubblegum機能セットの探索を開始できます。

+ 119 - 0
src/pages/ja/bubblegum/sdk/rust.md

@@ -0,0 +1,119 @@
+---
+title: MPL-Bubblegum Rust SDK
+metaTitle: Rust SDK | MPL-Bubblegum
+description: MPL-Bubblegum Rust SDKを実行するためのプロジェクト設定方法を学びます。
+---
+
+MetaplexはMPL-BubblegumプログラムとやりとりするためのRustライブラリを提供しています。Rustライブラリは、rustスクリプト/ビルドとCPI命令を介したオンチェーンプログラムの両方で使用できます。
+
+## インストール
+
+MPL-Bubblegum Rust SDKは、スクリプト/デスクトップ/モバイルアプリケーションとSolanaオンチェーンプログラムの両方で使用できます。
+
+```rust
+cargo add mpl-bubblegum
+```
+
+{% quick-links %}
+
+{% quick-link title="crates.io" target="_blank" icon="Rust" href="https://crates.io/crates/mpl-bubblegum" description="MPL-Bubblegum Rustクレートで始めましょう。" /%}
+
+{% quick-link title="docs.rs" target="_blank" icon="Rust" href="https://docs.rs/MPL-Bubblegum/latest/mpl_bubblegum/" description="MPL-BubblegumのRust SDK typedocプラットフォーム。" /%}
+
+{% /quick-links %}
+
+## ローカルスクリプト
+
+ローカルスクリプトでは、リストされているすべての命令の`Builder`バージョンを使用することをお勧めします。これらのビルダーは多くの作業を抽象化し、トランザクションに追加できる命令を返します。
+
+すべてのBubblegum命令のリストはここにあります:[MPL-Bubblegum - Rust Instructions](https://docs.rs/mpl-bubblegum/latest/mpl_bubblegum/instructions/index.html)
+
+Rustの使用に関するより包括的なガイドについては、[Metaplex Rust SDKsガイド](/ja/guides/rust/metaplex-rust-sdks)ページをご覧ください。
+
+#### CreateTreeConfigBuilder - 例
+
+```rust
+use mpl_bubblegum::{instructions::CreateTreeConfigBuilder, programs::{SPL_ACCOUNT_COMPRESSION_ID, SPL_NOOP_ID}};
+use solana_client::{nonblocking::rpc_client, rpc_config::RpcSendTransactionConfig};
+use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Keypair, signer::Signer, system_program, transaction::Transaction};
+
+#[tokio::main]
+pub async fn create_tree(keypair: Keypair) {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com/".to_string());
+
+    let payer = keypair;
+        
+
+    let asset = Keypair::new();
+
+    let merkle_tree = Keypair::new();
+
+    let tree_config = Pubkey::find_program_address(
+        &[
+            &merkle_tree.pubkey().to_bytes(),
+        ],
+        &mpl_bubblegum::ID,
+    );
+
+    let create_tree_config_ix = CreateTreeConfigBuilder::new()
+        .merkle_tree(merkle_tree.pubkey())
+        .tree_config(tree_config.0)
+        .payer(payer.pubkey())
+        .log_wrapper(SPL_NOOP_ID)
+        .compression_program(SPL_ACCOUNT_COMPRESSION_ID)
+        .system_program(system_program::ID)
+        .max_depth(20)
+        .max_buffer_size(1024)
+        .public(false)
+        .instruction();
+
+    let signers = vec![&asset, &payer];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await;
+
+    let create_tree_config_tx = Transaction::new_signed_with_payer(
+        &[create_tree_config_ix],
+        Some(&payer.pubkey()),
+        &signers,
+        last_blockhash.unwrap(),
+    );
+
+    let res = rpc_client
+        .send_transaction_with_config(&create_tree_config_tx, RpcSendTransactionConfig {
+            skip_preflight: false,
+            preflight_commitment: Some(CommitmentConfig::confirmed().commitment),
+            encoding: None,
+            max_retries: None,
+            min_context_slot: None,
+        })
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res)
+}
+```
+
+## CPI(Cross Program Invocation)
+
+独自のプログラムからCPI命令を実行することは、`MPL-Bubblegum` Rustクレートのすべての命令で見つけることができる命令関数の`CpiBuilder`バージョンを使用することで簡単に実現できます。
+
+すべてのBubblegum命令のリストはここにあります:[Metaplex Bubblegum - Rust Instructions](https://docs.rs/mpl-bubblegum/latest/mpl_bubblegum/instructions/index.html)
+
+Metaplexクレートを使用してCPI命令を作成するより包括的なガイドについては、[Metaplexプログラムへのcpi方法ガイド](/ja/guides/rust/how-to-cpi-into-a-metaplex-program)ページをご覧ください。
+
+#### CreateTreeConfigCpiBuilder - 例
+
+```rust
+CreateTreeConfigCpiBuilder::new()
+        .merkle_tree(context.accounts.merkle_tree)
+        .tree_config(context.accounts.tree_config)
+        .payer(context.accounts.payer)
+        .tree_creator(context.accounts.tree_creator)
+        .log_wrapper(SPL_NOOP_ID)
+        .compression_program(context.accounts.compression_program)
+        .system_program(context.accounts.system_program)
+        .max_depth(20)
+        .max_buffer_size(1024)
+        .public(false)
+        .invoke()
+```

+ 50 - 0
src/pages/ja/bubblegum/transfer-cnfts.md

@@ -0,0 +1,50 @@
+---
+title: 圧縮NFTの転送
+metaTitle: 圧縮NFTの転送 | Bubblegum
+description: Bubblegumで圧縮NFTを転送する方法を学びます。
+---
+
+**Transfer**命令を使用して、圧縮NFTを1つの所有者から別の所有者に転送できます。転送を承認するには、現在の所有者またはdelegate権限(存在する場合)のいずれかがトランザクションに署名する必要があります。命令は以下のパラメータを受け取ります:
+
+- **Leaf Owner**と**Leaf Delegate**: 圧縮NFTの現在の所有者とそのdelegate権限(存在する場合)。これらのいずれかがトランザクションに署名する必要があります。
+- **New Leaf Owner**: 圧縮NFTの新しい所有者のアドレス。
+
+この命令は圧縮NFTを更新するため、Bubblegum Tree上のリーフを置き換えることに注意してください。これは、圧縮NFTの整合性を検証するために追加のパラメータを提供する必要があることを意味します。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% callout title="トランザクションサイズ" type="note" %}
+トランザクションサイズエラーが発生した場合は、`getAssetWithProof`で`{ truncateCanopy: true }`の使用を検討してください。詳細については[FAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)を参照してください。
+{% /callout %}
+
+{% dialect-switcher title="圧縮NFTの転送" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import { getAssetWithProof, transfer } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await transfer(umi, {
+  ...assetWithProof,
+  leafOwner: currentLeafOwner,
+  newLeafOwner: newLeafOwner.publicKey,
+}).sendAndConfirm(umi)
+```
+
+{% totem-accordion title="delegateを使用する" %}
+
+```ts
+import { getAssetWithProof, transfer } from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await transfer(umi, {
+  ...assetWithProof,
+  leafDelegate: currentLeafDelegate,
+  newLeafOwner: newLeafOwner.publicKey,
+}).sendAndConfirm(umi)
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 72 - 0
src/pages/ja/bubblegum/update-cnfts.md

@@ -0,0 +1,72 @@
+---
+title: 圧縮NFTの更新
+metaTitle: 圧縮NFTの更新 | Bubblegum
+description: Bubblegumで圧縮NFTを更新する方法を学びます。
+---
+
+**Update**命令を使用して、圧縮NFTのメタデータを変更できます。Merkle rootは、データの伝播ハッシュを反映するように更新され、[Metaplex DAS API](https://github.com/metaplex-foundation/digital-asset-standard-api)に準拠するRPCプロバイダーは、cNFTのインデックスを更新します。
+
+メタデータは、圧縮NFTがコレクション内の検証済みアイテムかどうかに応じて、2つの権限のいずれかによって更新できます。
+
+## 更新権限
+
+cNFTには2つの可能な更新権限があり、それはtreeの所有者またはcNFTがコレクションに属している場合はコレクション権限のいずれかです。
+
+### コレクション権限
+
+cNFTがコレクションに属している場合、そのcNFTの更新権限はコレクションの権限になります。cNFTを更新するときは、更新関数に`collectionMint`引数を渡す必要があります。
+
+権限は現在のumi identityから推測されます。権限が現在のumi identityと異なる場合は、`authority`引数をsigner typeとして渡すか、後で署名するための「noopSigner」を作成する必要があります。
+
+```js
+await updateMetadata(umi, {
+  ...
+  collectionMint: publicKey("11111111111111111111111111111111"),
+}).sendAndConfirm(umi)
+```
+
+### Tree権限
+
+cNFTがコレクションに属さない場合、cNFTの更新権限はcNFTが属するtreeの権限になります。この場合、更新関数から`collectionMint`引数を**省略**します。
+
+権限は現在のumi identityから推測されます。権限が現在のumi identityと異なる場合は、`authority`引数をsigner typeとして渡すか、後で署名するための「noopSigner」を作成する必要があります。
+
+## cNFTの更新
+
+{% dialect-switcher title="圧縮NFTの更新" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  updateMetadata,
+  UpdateArgsArgs,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+// ヘルパーを使用してアセットとプルーフを取得します。
+const assetWithProof = await getAssetWithProof(umi, assetId, {
+  truncateCanopy: true,
+})
+
+// その後、NFTのメタデータを更新するために使用できます。
+const updateArgs: UpdateArgsArgs = {
+  name: some('New name'),
+  uri: some('https://updated-example.com/my-nft.json'),
+}
+await updateMetadata(umi, {
+  ...assetWithProof,
+  leafOwner,
+  currentMetadata: assetWithProof.metadata,
+  updateArgs,
+  // オプションのパラメータ。権限が現在のumi identityとは異なるsigner typeの場合、
+  // そのsignerをここに割り当てます。
+  authority: <Signer>
+  // オプションのパラメータ。cNFTがコレクションに属している場合は、ここに渡します。
+  collectionMint: publicKey("22222222222222222222222222222222"),
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 99 - 0
src/pages/ja/bubblegum/verify-collections.md

@@ -0,0 +1,99 @@
+---
+title: コレクションの検証
+metaTitle: コレクションの検証 | Bubblegum
+description: Bubblegumでコレクションの設定、検証、および検証解除の方法を学びます。
+---
+
+圧縮NFTにコレクションが設定されているときは常に、コレクションのupdate authority — または承認されたコレクションdelegate — はそのcNFT上でそのコレクションを検証および/または検証解除できます。 {% .lead %}
+
+技術的には、これはcNFTの**Collection**オブジェクトの**Verified**ブール値を切り替えることになり、コレクションの権限がこの圧縮NFTをコレクションの一部として承認したことを誰もが知ることができます。
+
+NFTに関するコレクションの概念に馴染みがない場合、それらは他のNFTをグループ化するために使用できる特別な非圧縮NFTです。**Collection NFT**のデータは、コレクション全体の名前とブランディングを記述するために使用されます。[Metaplex Verified Collectionsについて詳しく読むことができます](/ja/token-metadata/collections)。
+
+**Mint to Collection V1**命令を使用して圧縮NFTを直接コレクションにミントすることが可能であることに注意してください([ここに文書化されています](/ja/bubblegum/mint-cnfts#minting-to-a-collection))。とはいえ、コレクションなしでcNFTを既にミントしている場合は、そのcNFTのコレクションを検証、検証解除、および設定する方法を見てみましょう。
+
+## コレクションの検証
+
+BubblegumプログラムのbusineCollectionV**Verify Collection**命令を使用して、圧縮NFTの**Verified**ブール値を`true`に設定できます。これが機能するためには、**Collection**オブジェクトがすでにcNFT上に設定されている必要があります — 例えば、ミント時に。
+
+命令は以下のパラメータを受け取ります:
+
+- **Collection Mint**: Collection NFTのmintアカウント。
+- **Collection Authority**: Collection NFTのupdate authority — または承認されたコレクションdelegate — をSignerとして。コレクション権限がdelegate権限の場合、プログラムは新しい統合された**Metadata Delegate**システムとレガシーの**Collection Authority Records**アカウントの両方をサポートすることに注意してください。適切なPDAを**Collection Authority Record Pda**パラメータに渡すだけです。
+
+さらに、この命令はBubblegum Tree上のリーフを置き換えることになるため、圧縮NFTの整合性を検証するためにより多くのパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTのコレクションの検証" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  verifyCollection,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await verifyCollection(umi, {
+  ...assetWithProof,
+  collectionMint,
+  collectionAuthority,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## コレクションの設定と検証
+
+**Collection**オブジェクトがまだ圧縮NFTに設定されていない場合、**Set and Verify Collection**命令を使用してそれを設定し、同時に検証できます。この命令は**Verify Collection**命令と同じパラメータを受け取りますが、コレクション権限と異なる場合は**Tree Creator Or Delegate**属性をSignerとして渡すことも必要です。
+
+{% dialect-switcher title="圧縮NFTのコレクションの設定と検証" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  setAndVerifyCollection,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await setAndVerifyCollection(umi, {
+  ...assetWithProof,
+  treeCreatorOrDelegate,
+  collectionMint,
+  collectionAuthority,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## コレクションの検証解除
+
+コレクションのupdate authorityは、**Unverify Collection**命令を使用して圧縮NFTのコレクションを検証解除することもできます。この命令を送信するためには、cNFTの**Collection**オブジェクトがすでに設定され、検証されていることが期待されます。**Unverify Collection**命令で必要な属性は、**Verify Collection**命令で必要なものと同じです。
+
+{% dialect-switcher title="圧縮NFTのコレクションの検証解除" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  unverifyCollection,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await unverifyCollection(umi, {
+  ...assetWithProof,
+  collectionMint,
+  collectionAuthority,
+}).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 57 - 0
src/pages/ja/bubblegum/verify-creators.md

@@ -0,0 +1,57 @@
+---
+title: 作成者の検証
+metaTitle: 作成者の検証 | Bubblegum
+description: Bubblegumで作成者を検証および検証解除する方法を学びます。
+---
+
+圧縮NFTのメタデータに作成者のリストが設定されている場合、これらの作成者は特別な命令を使用してcNFT上で自分自身を検証および検証解除できます。 {% .lead %}
+
+これらの命令は、cNFTの**Creators**配列の適切なアイテムの**Verified**ブール値を切り替えます。そのブール値は、ウォレットやマーケットプレイスなどのアプリがどの作成者が本物でどの作成者がそうでないかを知ることができるため重要です。
+
+作成者は[圧縮NFTをミント](/ja/bubblegum/mint-cnfts)するときに、ミントトランザクションに署名することで直接自分自身を検証できることに注目する価値があります。とはいえ、既存の圧縮NFT上で作成者が自分自身を検証または検証解除する方法を見てみましょう。
+
+## 作成者の検証
+
+Bubblegumプログラムは、検証しようとしている作成者によって署名される必要がある**Verify Creator**命令を提供します。
+
+さらに、この命令はBubblegum Tree上のリーフを置き換えることになるため、圧縮NFTの整合性を検証するためにより多くのパラメータを提供する必要があります。これらのパラメータはリーフを変更するすべての命令に共通であるため、[以下のFAQ](/ja/bubblegum/faq#replace-leaf-instruction-arguments)に文書化されています。幸い、Metaplex DAS APIを使用してこれらのパラメータを自動的に取得するヘルパーメソッドを使用できます。
+
+{% dialect-switcher title="圧縮NFTの作成者の検証" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  verifyCreator,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await verifyCreator(umi, { ...assetWithProof, creator }).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}
+
+## 作成者の検証解除
+
+**Verify Creator**命令と同様に、**Unverify Creator**命令は作成者によって署名される必要があり、圧縮NFT上で彼らを検証解除します。
+
+{% dialect-switcher title="圧縮NFTの作成者の検証解除" %}
+{% dialect title="JavaScript" id="js" %}
+{% totem %}
+
+```ts
+import {
+  getAssetWithProof,
+  unverifyCreator,
+} from '@metaplex-foundation/mpl-bubblegum'
+
+const assetWithProof = await getAssetWithProof(umi, assetId, {truncateCanopy: true});
+await unverifyCreator(umi, { ...assetWithProof, creator }).sendAndConfirm(umi)
+```
+
+{% /totem %}
+{% /dialect %}
+{% /dialect-switcher %}

+ 191 - 0
src/pages/ja/candy-machine/custom-guards/generating-client.md

@@ -0,0 +1,191 @@
+---
+title: Candy Machineのカスタムガードクライアント生成
+metaTitle: カスタムガードクライアント生成 | Candy Machine
+description: カスタムガード用のUmi互換クライアントを生成する方法。
+---
+
+Candy Machine Guardプログラム用のカスタムガードを作成したら、フロントエンドなどでガードを使用できるようにするために、Umi SDKと連携するKinobiクライアントを生成する必要があります。
+
+## IDLと初期クライアントの生成
+
+### Shankjsの設定
+
+ShankjsはAnchorと非Anchorプログラムの両方で動作するIDLジェネレーターです。新しいカスタムCandy Guardデプロイメントキーでこれを設定して、適切に動作するクライアントを生成する必要があります。mpl-candy-machineリポジトリの`/configs/shank.cjs`にあるファイルを編集してください。
+
+```js
+/configs/shank.cjs
+
+generateIdl({
+  generator: "anchor",
+  programName: "candy_guard",
+  programId: "Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g", // あなたのカスタムCandy Guardデプロイ済みプログラムキー
+  idlDir,
+  binaryInstallDir,
+  programDir: path.join(programDir, "candy-guard", "program"),
+});
+
+```
+
+{% callout %}
+anchor 28を使用して生成する場合、crates.ioクレートが不足しているため、ShankjsのIDLジェネレーターにanchor 27へのフォールバックを追加する必要があります。
+{% /callout %}
+
+```js
+/configs/shank.cjs
+
+generateIdl({
+  generator: "anchor",
+  programName: "candy_guard",
+  programId: "Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g", // あなたのカスタムCandy Guardデプロイ済みプログラムキー
+  idlDir,
+  binaryInstallDir,
+  programDir: path.join(programDir, "candy-guard", "program"),
+  rustbin: {
+    locked: true,
+    versionRangeFallback: "0.27.0",
+  },
+});
+
+```
+
+### IDLとクライアントの生成
+
+これでIDLと初期クライアントを生成できるはずです。プロジェクトのルートから以下を実行してください:
+
+```shell
+pnpm run generate
+```
+
+これにより`pnpm generate:idls`と`pnpm generate:clients`の両方のスクリプトが実行され、初期クライアントが構築されます。
+何らかの理由でこれらを別々に実行する必要がある場合は、それも可能です。
+
+## クライアントへのガードの追加
+
+### ガードファイルの作成
+
+初期クライアントの生成が成功したら、`/clients/js/src`に移動してください。
+
+最初のステップは、`/clients/js/src/defaultGuards`フォルダに新しいガードを追加することです。
+
+以下は、作成したガードのタイプに基づいて調整し、あなたのニーズに合わせて使用できるテンプレートです。
+ガードには任意の名前を付けることができますが、例では`customGuard.ts`と名付けます:
+
+```ts
+import { PublicKey } from '@metaplex-foundation/umi'
+import {
+  getCustomGuardSerializer,
+  CustomGuard,
+  CustomGuardArgs,
+} from '../generated'
+import { GuardManifest, noopParser } from '../guards'
+
+export const customGuardManifest: GuardManifest<
+  CustomGuardArgs,
+  CustomGuard,
+  CustomGuardMintArgs
+> = {
+  name: 'customGuard',
+  serializer: getCustomGuardSerializer,
+  mintParser: (context, mintContext, args) => {
+    const { publicKeyArg1, arg1 } = args
+    return {
+      data: new Uint8Array(),
+      // カスタムガードに必要なアカウントをmint argsから渡します。
+      // あなたのガードは残りのアカウントが必要な場合とそうでない場合があります。
+      remainingAccounts: [
+        { publicKey: publicKeyArg1, isWritable: true },
+        { publicKey: publicKeyArg2, isWritable: false },
+      ],
+    }
+  },
+  routeParser: noopParser,
+}
+
+// ここで、ガードが動作するために必要なカスタムMint引数を記入します。
+// あなたのガードはMintArgsが必要な場合とそうでない場合があります。
+
+export type CustomGuardMintArgs = {
+  /**
+   * カスタムガードMint引数1
+   */
+  publicKeyArg1: PublicKey
+
+  /**
+   * カスタムガードMint引数2
+   */
+  publicKeyArg2: PublicKey
+
+  /**
+   * カスタムガードMint引数3
+   */
+  arg3: Number
+}
+```
+
+### 既存ファイルへのガードの追加
+
+ここから、いくつかの既存ファイルに新しいガードを追加する必要があります。
+
+`/clients/js/src/defaultGuards/index.ts`から新しいガードをエクスポートしてください:
+
+```ts
+...
+export * from './tokenGate';
+export * from './tokenPayment';
+export * from './token2022Payment';
+// ガードをリストに追加
+export * from './customGuard';
+```
+
+`/clients/js/src/defaultGuards/defaults.ts`内で、以下の場所にガードを追加してください:
+
+```ts
+import { CustomGuardArgs } from "../generated"
+
+export type DefaultGuardSetArgs = GuardSetArgs & {
+    ...
+     // ガードをリストに追加
+    customGuard: OptionOrNullable<CustomGuardArgs>;
+}
+```
+
+```ts
+import { CustomGuard } from "../generated"
+
+export type DefaultGuardSet = GuardSet & {
+    ...
+     // ガードをリストに追加
+    customGuard: Option<CustomGuard>
+}
+```
+
+```ts
+import { CustomGuardMintArgs } from "./defaultGuards/customGuard.ts"
+export type DefaultGuardSetMintArgs = GuardSetMintArgs & {
+    ...
+    // ガードをリストに追加
+    customGuard: OptionOrNullable<CustomGuardMintArgs>
+}
+```
+
+```ts
+export const defaultCandyGuardNames: string[] = [
+  ...// ガードをリストに追加
+  'customGuard',
+]
+```
+
+最後に、`/clients/js/src/plugin.ts`にあるプラグインファイルにエクスポートしたcustomGuardManifestを追加する必要があります:
+
+```ts
+import {customGuardManifest} from "./defaultGuards"
+
+ umi.guards.add(
+  ...// ガードマニフェストをリストに追加
+  customGuardManifest
+)
+```
+
+この時点で、クライアントパッケージをビルドしてnpmにアップロードするか、新しいガードクライアントにアクセスしたいプロジェクトフォルダーにリンク/移動することができます。
+
+AVAの内蔵テストスイートを使用して、複数のシナリオでガードを完全にテストするテストを書くことは価値があります。テストの例は`/clients/js/tests`で見つけることができます。

+ 17 - 0
src/pages/ja/candy-machine/getting-started/index.md

@@ -0,0 +1,17 @@
+---
+title: はじめに
+metaTitle: はじめに | キャンディマシン
+description: キャンディマシンを始めましょう。
+---
+
+キャンディマシンを始めるために、以下から使用したい言語/ライブラリを選択してください。
+
+{% quick-links %}
+
+{% quick-link title="JavaScript" icon="JavaScript" href="/ja/candy-machine/getting-started/js" description="Umiフレームワークベースの私たちのJavaScriptライブラリを始めましょう。" /%}
+
+{% quick-link title="Rust" icon="Rust" href="/ja/candy-machine/getting-started/rust" description="私たちのRustクレートを使って始めましょう。" /%}
+
+{% quick-link title="Sugar" icon="SolidCommandLine" href="/ja/candy-machine/sugar/getting-started" description="コマンドラインツールSugarを使って始めましょう。" /%}
+
+{% /quick-links %}

+ 50 - 0
src/pages/ja/candy-machine/getting-started/js.md

@@ -0,0 +1,50 @@
+---
+title: JavaScriptを使ったはじめに
+metaTitle: JavaScript SDK | キャンディマシン
+description: JavaScriptを使ってキャンディマシンを始めましょう
+---
+
+Metaplexは、キャンディマシンとやり取りするために使用できるJavaScriptライブラリを提供しています。[Umiフレームワーク](https://github.com/metaplex-foundation/umi)のおかげで、多くの主観的な依存関係なしで提供され、任意のJavaScriptプロジェクトで使用できる軽量なライブラリを提供します。
+
+始めるには、[Umiフレームワークをインストール](https://github.com/metaplex-foundation/umi/blob/main/docs/installation.md)し、キャンディマシンJavaScriptライブラリをインストールする必要があります。
+
+```sh
+npm install \
+  @metaplex-foundation/umi \
+  @metaplex-foundation/umi-bundle-defaults \
+  @solana/web3.js@1 \
+  @metaplex-foundation/mpl-candy-machine
+```
+
+次に、`Umi`インスタンスを作成し、以下のように`mplCandyMachine`プラグインをインストールできます。
+
+```ts
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { mplCandyMachine } from '@metaplex-foundation/mpl-candy-machine'
+
+// 選択したRPCエンドポイントを使用してください。
+const umi = createUmi('http://127.0.0.1:8899').use(mplCandyMachine())
+```
+
+次に、使用するウォレットをUmiに伝える必要があります。これは[キーペア](/ja/umi/connecting-to-umi#connecting-w-a-secret-key)または[Solanaウォレットアダプター](/ja/umi/connecting-to-umi#connecting-w-wallet-adapter)のいずれかです。
+
+これで完了です。[ライブラリが提供するさまざまな関数](https://mpl-candy-machine.typedoc.metaplex.com/)を使用し、`Umi`インスタンスを渡すことでNFTと相互作用できるようになりました。以下は、キャンディマシンアカウントとそれに関連するキャンディガードアカウントを取得する例です。
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import {
+  fetchCandyMachine,
+  fetchCandyGuard,
+} from '@metaplex-foundation/mpl-candy-machine'
+
+const candyMachinePublicKey = publicKey('...')
+const candyMachine = await fetchCandyMachine(umi, candyMachinePublicKey)
+const candyGuard = await fetchCandyGuard(umi, candyMachine.mintAuthority)
+```
+
+🔗 **役立つリンク:**
+
+- [Umiフレームワーク](https://github.com/metaplex-foundation/umi)
+- [GitHubリポジトリ](https://github.com/metaplex-foundation/mpl-candy-machine)
+- [NPMパッケージ](https://www.npmjs.com/package/@metaplex-foundation/mpl-candy-machine)
+- [APIリファレンス](https://mpl-candy-machine.typedoc.metaplex.com/)

+ 20 - 0
src/pages/ja/candy-machine/getting-started/rust.md

@@ -0,0 +1,20 @@
+---
+title: Rustを使ったはじめに
+metaTitle: キャンディマシン - はじめに - Rust SDK | キャンディマシン
+description: Rustを使ってキャンディマシンを始めましょう
+---
+
+Rust開発者であれば、キャンディマシンプログラムとやり取りするためにRustクレートを使用することもできます。プログラムはRustで書かれているため、このクレートには、命令を準備するヘルパーメソッドを含む、プログラムのすべてのロジックが含まれています。
+
+これは、Rustクライアントを開発している場合や、プログラム内でキャンディマシンプログラムへの[CPI呼び出し](https://solanacookbook.com/references/programs.html#how-to-do-cross-program-invocation)を行いたい場合に役立ちます。
+
+キャンディマシンは2つのプログラムで構成されているため、2つのライブラリをインストールする必要があります。
+
+- **キャンディマシンコア**
+  - [GitHubリポジトリ](https://github.com/metaplex-foundation/mpl-candy-machine/tree/main/programs/candy-machine-core)
+  - [クレートページ](https://crates.io/crates/mpl-candy-machine-core)
+  - [APIリファレンス](https://docs.rs/mpl-candy-machine-core/0.1.0/mpl_candy_machine_core/)
+- **キャンディガード**
+  - [GitHubリポジトリ](https://github.com/metaplex-foundation/mpl-candy-machine/tree/main/programs/candy-guard)
+  - [クレートページ](https://crates.io/crates/mpl-candy-guard)
+  - [APIリファレンス](https://docs.rs/mpl-candy-guard/0.1.0/mpl_candy_guard/)

Some files were not shown because too many files changed in this diff