Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support i18n #1310

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions src/client/app/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
PageData,
SiteData,
resolveSiteDataByRoute,
createTitle
createTitle,
DefaultTheme
} from '../shared.js'
import { withBase } from './utils.js'

export const dataSymbol: InjectionKey<VitePressData> = Symbol()

Expand All @@ -19,7 +19,7 @@ export interface VitePressData<T = any> {
title: Ref<string>
description: Ref<string>
lang: Ref<string>
localePath: Ref<string>
localeIndex: Ref<string>
}

// site data is a singleton
Expand All @@ -39,7 +39,7 @@ if (import.meta.hot) {
// per-app data
export function initData(route: Route): VitePressData {
const site = computed(() =>
resolveSiteDataByRoute(siteDataRef.value, route.path)
resolveSiteDataByRoute(siteDataRef.value, route.data.relativePath)
)

return {
Expand All @@ -48,13 +48,7 @@ export function initData(route: Route): VitePressData {
page: computed(() => route.data),
frontmatter: computed(() => route.data.frontmatter),
lang: computed(() => site.value.lang),
localePath: computed(() => {
const { langs, lang } = site.value
const path = Object.keys(langs).find(
(langPath) => langs[langPath].lang === lang
)
return withBase(path || '/')
}),
localeIndex: computed(() => site.value.localeIndex || 'root'),
title: computed(() => {
return createTitle(site.value, route.data)
}),
Expand All @@ -64,7 +58,7 @@ export function initData(route: Route): VitePressData {
}
}

export function useData<T = any>(): VitePressData<T> {
export function useData<T = DefaultTheme.Config>(): VitePressData<T> {
const data = inject(dataSymbol)
if (!data) {
throw new Error('vitepress data not properly injected in app')
Expand Down
2 changes: 1 addition & 1 deletion src/client/app/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { siteDataRef } from './data.js'
import { inBrowser, EXTERNAL_URL_RE } from '../shared.js'

export { inBrowser }
export { inBrowser } from '../shared.js'

/**
* Join two paths by resolving the slash collision.
Expand Down
3 changes: 1 addition & 2 deletions src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export type {
PageData,
SiteData,
HeadConfig,
Header,
LocaleConfig
Header
} from '../../types/shared.js'

// composables
Expand Down
8 changes: 6 additions & 2 deletions src/client/theme-default/components/VPAlgoliaSearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { default as docsearch } from '@docsearch/js'
import { onMounted } from 'vue'
import { useRouter, useRoute, useData } from 'vitepress'

const props = defineProps<{
algolia: DefaultTheme.AlgoliaSearchOptions
}>()

const router = useRouter()
const route = useRoute()
const { theme, site } = useData()
const { site } = useData()

onMounted(() => {
initialize(theme.value.algolia)
initialize(props.algolia)
setTimeout(poll, 16)
})

Expand Down
9 changes: 6 additions & 3 deletions src/client/theme-default/components/VPCarbonAds.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import { ref, watch, onMounted } from 'vue'
import { useData } from 'vitepress'
import { useAside } from '../composables/aside.js'

const { theme } = useData()
const carbonOptions = theme.value.carbonAds
const props = defineProps<{
carbonAds: DefaultTheme.CarbonAdsOptions
}>()

const carbonOptions = props.carbonAds
const { isAsideEnabled } = useAside()
const container = ref()

Expand Down
8 changes: 6 additions & 2 deletions src/client/theme-default/components/VPDocAside.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
import { useData } from 'vitepress'
import VPDocAsideOutline from './VPDocAsideOutline.vue'
import VPDocAsideCarbonAds from './VPDocAsideCarbonAds.vue'

const { theme } = useData()

const VPCarbonAds = __CARBON__
? defineAsyncComponent(() => import('./VPCarbonAds.vue'))
: () => null
</script>

<template>
Expand All @@ -17,7 +21,7 @@ const { theme } = useData()
<div class="spacer" />

<slot name="aside-ads-before" />
<VPDocAsideCarbonAds v-if="theme.carbonAds" />
<VPCarbonAds v-if="theme.carbonAds" :carbonAds="theme.carbonAds" />
<slot name="aside-ads-after" />

<slot name="aside-bottom" />
Expand Down
13 changes: 0 additions & 13 deletions src/client/theme-default/components/VPDocAsideCarbonAds.vue

This file was deleted.

2 changes: 1 addition & 1 deletion src/client/theme-default/components/VPDocAsideOutline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function handleClick({ target: el }: Event) {
<div class="outline-marker" ref="marker" />

<div class="outline-title">
{{ theme.outlineTitle || 'On this page' }}
{{ theme.translations?.outlineTitle || 'On this page' }}
</div>

<nav aria-labelledby="doc-outline-aria-label">
Expand Down
4 changes: 2 additions & 2 deletions src/client/theme-default/components/VPDocFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ const showFooter = computed(() => {
<div v-if="control.prev || control.next" class="prev-next">
<div class="pager">
<a v-if="control.prev" class="pager-link prev" :href="normalizeLink(control.prev.link)">
<span class="desc">{{ theme.docFooter?.prev ?? 'Previous page' }}</span>
<span class="desc">{{ theme.translations?.docFooter?.prev || 'Previous page' }}</span>
<span class="title">{{ control.prev.text }} </span>
</a>
</div>
<div class="pager" :class="{ 'has-prev': control.prev }">
<a v-if="control.next" class="pager-link next" :href="normalizeLink(control.next.link)">
<span class="desc">{{ theme.docFooter?.next ?? 'Next page' }}</span>
<span class="desc">{{ theme.translations?.docFooter?.next || 'Next page' }}</span>
<span class="title">{{ control.next.text }}</span>
</a>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ onMounted(() => {

<template>
<p class="VPLastUpdated">
{{ theme.lastUpdatedText ?? 'Last updated' }}:
{{ theme.translations?.lastUpdatedText || 'Last updated' }}:
<time :datatime="isoDatetime">{{ datetime }}</time>
</p>
</template>
Expand Down
8 changes: 6 additions & 2 deletions src/client/theme-default/components/VPLocalNav.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar.js'
import VPIconAlignLeft from './icons/VPIconAlignLeft.vue'

Expand All @@ -10,6 +11,7 @@ defineEmits<{
(e: 'open-menu'): void
}>()

const { theme } = useData()
const { hasSidebar } = useSidebar()

function scrollToTop() {
Expand All @@ -26,11 +28,13 @@ function scrollToTop() {
@click="$emit('open-menu')"
>
<VPIconAlignLeft class="menu-icon" />
<span class="menu-text">Menu</span>
<span class="menu-text">
{{ theme.translations?.sidebarMenuLabel || 'Menu' }}
</span>
</button>

<a class="top-link" href="#" @click="scrollToTop">
Return to top
{{ theme.translations?.returnToTopLabel || 'Return to top' }}
</a>
</div>
</template>
Expand Down
12 changes: 8 additions & 4 deletions src/client/theme-default/components/VPNavBarExtra.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@ import VPFlyout from './VPFlyout.vue'
import VPMenuLink from './VPMenuLink.vue'
import VPSwitchAppearance from './VPSwitchAppearance.vue'
import VPSocialLinks from './VPSocialLinks.vue'
import { useLangs } from '../composables/langs.js'

const { site, theme } = useData()
const { localeLinks, currentLang } = useLangs()
</script>

<template>
<VPFlyout class="VPNavBarExtra" label="extra navigation">
<div v-if="theme.localeLinks" class="group">
<p class="trans-title">{{ theme.localeLinks.text }}</p>
<div v-if="localeLinks.length && currentLang.label" class="group">
<p class="trans-title">{{ currentLang.label }}</p>

<template v-for="locale in theme.localeLinks.items" :key="locale.link">
<template v-for="locale in localeLinks" :key="locale.link">
<VPMenuLink :item="locale" />
</template>
</div>

<div v-if="site.appearance" class="group">
<div class="item appearance">
<p class="label">Appearance</p>
<p class="label">
{{ theme.translations?.darkModeSwitchLabel || 'Appearance' }}
</p>
<div class="appearance-action">
<VPSwitchAppearance />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/client/theme-default/components/VPNavBarSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function load() {

<template>
<div v-if="theme.algolia" class="VPNavBarSearch">
<VPAlgoliaSearchBox v-if="loaded" />
<VPAlgoliaSearchBox v-if="loaded" :algolia="theme.algolia" />

<div v-else id="docsearch" @click="load">
<button
Expand Down
7 changes: 5 additions & 2 deletions src/client/theme-default/components/VPNavBarTitle.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar.js'
import { useLangs } from '../composables/langs.js'
import { normalizeLink } from '../support/utils.js'
import VPImage from './VPImage.vue'

const { site, theme } = useData()
const { hasSidebar } = useSidebar()
const { currentLang } = useLangs()
</script>

<template>
<div class="VPNavBarTitle" :class="{ 'has-sidebar': hasSidebar }">
<a class="title" :href="site.base">
<a class="title" :href="normalizeLink(currentLang.link)">
<slot name="nav-bar-title-before" />
<VPImage class="logo" :image="theme.logo" />
<VPImage v-if="theme.logo" class="logo" :image="theme.logo" />
<template v-if="theme.siteTitle">{{ theme.siteTitle }}</template>
<template v-else-if="theme.siteTitle === undefined">{{ site.title }}</template>
<slot name="nav-bar-title-after" />
Expand Down
10 changes: 5 additions & 5 deletions src/client/theme-default/components/VPNavBarTranslations.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<script lang="ts" setup>
import { useData } from 'vitepress'
import VPIconLanguages from './icons/VPIconLanguages.vue'
import VPFlyout from './VPFlyout.vue'
import VPMenuLink from './VPMenuLink.vue'
import { useLangs } from '../composables/langs.js'

const { theme } = useData()
const { localeLinks, currentLang } = useLangs()
</script>

<template>
<VPFlyout
v-if="theme.localeLinks"
v-if="localeLinks.length && currentLang.label"
class="VPNavBarTranslations"
:icon="VPIconLanguages"
>
<div class="items">
<p class="title">{{ theme.localeLinks.text }}</p>
<p class="title">{{ currentLang.label }}</p>

<template v-for="locale in theme.localeLinks.items" :key="locale.link">
<template v-for="locale in localeLinks" :key="locale.link">
<VPMenuLink :item="locale" />
</template>
</div>
Expand Down
6 changes: 4 additions & 2 deletions src/client/theme-default/components/VPNavScreenAppearance.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
import { useData } from 'vitepress'
import VPSwitchAppearance from './VPSwitchAppearance.vue'

const { site } = useData()
const { site, theme } = useData()
</script>

<template>
<div v-if="site.appearance" class="VPNavScreenAppearance">
<p class="text">Appearance</p>
<p class="text">
{{ theme.translations?.darkModeSwitchLabel || 'Appearance' }}
</p>
<VPSwitchAppearance />
</div>
</template>
Expand Down
15 changes: 9 additions & 6 deletions src/client/theme-default/components/VPNavScreenTranslations.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useData } from 'vitepress'
import VPIconChevronDown from './icons/VPIconChevronDown.vue'
import VPIconLanguages from './icons/VPIconLanguages.vue'
import { useLangs } from '../composables/langs.js'

const { theme } = useData()

const { localeLinks, currentLang } = useLangs()
const isOpen = ref(false)

function toggle() {
Expand All @@ -14,15 +13,19 @@ function toggle() {
</script>

<template>
<div v-if="theme.localeLinks" class="VPNavScreenTranslations" :class="{ open: isOpen }">
<div
v-if="localeLinks.length && currentLang.label"
class="VPNavScreenTranslations"
:class="{ open: isOpen }"
>
<button class="title" @click="toggle">
<VPIconLanguages class="icon lang" />
{{ theme.localeLinks.text }}
{{ currentLang.label }}
<VPIconChevronDown class="icon chevron" />
</button>

<ul class="list">
<li v-for="locale in theme.localeLinks.items" :key="locale.link" class="item">
<li v-for="locale in localeLinks" :key="locale.link" class="item">
<a class="link" :href="locale.link">{{ locale.text }}</a>
</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/client/theme-default/composables/edit-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function useEditLink() {
const { theme, page } = useData()

return computed(() => {
const { text = 'Edit this page', pattern } = theme.value.editLink || {}
const { text = 'Edit this page', pattern = '' } = theme.value.editLink || {}
const { relativePath } = page.value
const url = pattern.replace(/:path/g, relativePath)

Expand Down
32 changes: 32 additions & 0 deletions src/client/theme-default/composables/langs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { computed } from 'vue'
import { useData } from 'vitepress'
import { isExternal } from '../support/utils.js'

export function useLangs() {
const { site, localeIndex } = useData()
const currentLang = computed(() => ({
label: site.value.locales[localeIndex.value]?.label,
link:
localeIndex.value === 'root'
? '/'
: site.value.locales[localeIndex.value].link || `/${localeIndex.value}/`
}))

const localeLinks = computed(() =>
Object.entries(site.value.locales).flatMap(([key, value]) =>
currentLang.value.label === value.label
? []
: {
text: value.label,
link:
key === 'root'
? '/'
: isExternal(key)
? key
: value.link || `/${key}/`
}
)
)

return { localeLinks, currentLang }
}
Loading