))}
diff --git a/src/components/LocalizedLink.jsx b/src/components/LocalizedLink.jsx
new file mode 100644
index 000000000..ae7d12f55
--- /dev/null
+++ b/src/components/LocalizedLink.jsx
@@ -0,0 +1,28 @@
+import { useEffect, useState } from 'react'
+import Link from 'next/link'
+import { useLocale } from '@/context/LocaleContext'
+
+export function LocalizedLink({ href, children, ...props }) {
+ const { locale } = useLocale()
+ const [localizedHref, setLocalizedHref] = useState(href)
+
+ useEffect(() => {
+ if (href.startsWith('/docs/')) {
+ const parts = href.split('/')
+ if (parts[2] !== 'en' && parts[2] !== 'ja') {
+ parts.splice(2, 0, locale)
+ } else if (parts[2] !== locale) {
+ parts[2] = locale
+ }
+ setLocalizedHref(parts.join('/'))
+ } else {
+ setLocalizedHref(href)
+ }
+ }, [href, locale])
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/components/MobileNavigation.jsx b/src/components/MobileNavigation.jsx
index f3b6b46fc..b6d168893 100644
--- a/src/components/MobileNavigation.jsx
+++ b/src/components/MobileNavigation.jsx
@@ -2,9 +2,11 @@ import { useEffect, useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { Dialog } from '@headlessui/react'
-import { navigation } from '@/navigation/docs'
import { Logomark } from '@/components/Logo'
import { SideBar } from './Navigation'
+import { useLocale } from '@/context/LocaleContext'
+import { navigation as englishNavigation } from '@/navigation/docs'
+import { navigation as japaneseNavigation } from '@/navigation/docs.ja'
function MenuIcon(props) {
return (
@@ -36,9 +38,19 @@ function CloseIcon(props) {
)
}
-export function MobileNavigation({}) {
+export function MobileNavigation() {
let router = useRouter()
let [isOpen, setIsOpen] = useState(false)
+ const { locale } = useLocale()
+ const [navigation, setNavigation] = useState(englishNavigation)
+
+ useEffect(() => {
+ if (locale.startsWith('ja')) {
+ setNavigation(japaneseNavigation)
+ } else {
+ setNavigation(englishNavigation)
+ }
+ }, [locale])
useEffect(() => {
if (!isOpen) return
@@ -99,12 +111,6 @@ export function MobileNavigation({}) {
*/}
>
diff --git a/src/components/Navigation.jsx b/src/components/Navigation.jsx
index 5ff4913a9..7921eddc5 100644
--- a/src/components/Navigation.jsx
+++ b/src/components/Navigation.jsx
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router'
import clsx from 'clsx'
import { useState, useEffect } from 'react'
import { ChevronDownIcon } from '@heroicons/react/24/solid'
+import { useLocale } from '@/context/LocaleContext' // Import the context
const isActive = (item, router) => {
if (
@@ -15,17 +16,17 @@ const isActive = (item, router) => {
}
export const MenuBar = ({ navigation }) => {
- const router = useRouter()
-
return (
)
}
@@ -138,10 +154,10 @@ export const Dropdown = ({ children, isActive }) => {
export const Navigation = ({ className, links, title = '' }) => {
const router = useRouter()
- const isActive = (item) =>
- item.href &&
- (router.pathname === item.href || router.pathname === item.href)
-
+ const isActive = (item) => {
+ const { pathname } = router
+ return item.href && (pathname === item.href || pathname === `${item.href}/`)
+ }
const renderNavItems = (items) =>
items.map((item) => {
const itemIsActive = isActive(item)
diff --git a/src/components/Search.jsx b/src/components/Search.jsx
index 0c80baad1..6c937a931 100644
--- a/src/components/Search.jsx
+++ b/src/components/Search.jsx
@@ -16,14 +16,14 @@ function SearchIcon(props) {
)
}
-function useAutocomplete() {
+function useAutocomplete(locale) {
let id = useId()
let router = useRouter()
let [autocompleteState, setAutocompleteState] = useState({})
let typingTimeout = useRef(null)
let lastQueryRef = useRef('')
- let [autocomplete] = useState(() =>
+ let [autocomplete, setAutocomplete] = useState(() =>
createAutocomplete({
id,
placeholder: 'Find something...',
@@ -49,12 +49,13 @@ function useAutocomplete() {
},
getSources({ query }) {
return import('@/markdoc/search.mjs').then(({ search }) => {
- let items = search(query, { limit: 5 })
+ let allItems = search(query, { locale }) // Filter by locale first
+ let filteredItems = allItems.slice(0, 5) // Apply the limit after filtering
return [
{
sourceId: 'documentation',
getItems() {
- return items
+ return filteredItems
},
getItemUrl({ item }) {
return item.url
@@ -69,6 +70,55 @@ function useAutocomplete() {
})
)
+ useEffect(() => {
+ setAutocomplete(
+ createAutocomplete({
+ id,
+ placeholder: 'Find something...',
+ defaultActiveItemId: 0,
+ onStateChange({ state }) {
+ setAutocompleteState(state)
+
+ if (typingTimeout.current) clearTimeout(typingTimeout.current)
+ if (state.query !== '' && state.query !== lastQueryRef.current) {
+ typingTimeout.current = setTimeout(() => {
+ event('search', {
+ search_term: state.query,
+ result_count: state.collections.flatMap(
+ (collection) => collection.items
+ ).length,
+ })
+ lastQueryRef.current = state.query
+ }, 1000)
+ }
+ },
+ shouldPanelOpen({ state }) {
+ return state.query !== ''
+ },
+ getSources({ query }) {
+ return import('@/markdoc/search.mjs').then(({ search }) => {
+ let allItems = search(query, { locale }) // Filter by locale first
+ let filteredItems = allItems.slice(0, 5) // Apply the limit after filtering
+ return [
+ {
+ sourceId: 'documentation',
+ getItems() {
+ return filteredItems
+ },
+ getItemUrl({ item }) {
+ return item.url
+ },
+ onSelect({ itemUrl }) {
+ router.push(itemUrl)
+ },
+ },
+ ]
+ })
+ },
+ })
+ )
+ }, [locale, id, router])
+
return { autocomplete, autocompleteState }
}
@@ -320,6 +370,26 @@ const SearchInput = forwardRef(function SearchInput(
)
})
+function LanguageFilterButton({ emoji, isActive, onClick, query }) {
+ return (
+
+ )
+}
+
function FilterButton({ label, isActive, onClick, query }) {
return (