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 #1339

Merged
merged 46 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
84e85b2
merge feat-i18n
brc-dd Sep 9, 2022
cf77a39
fix carbon ads
brc-dd Sep 9, 2022
533ebd8
Merge branch 'main' into feat/i18n-next
brc-dd Sep 9, 2022
5ed5ff9
feat: support using .vitepress/config/index.ts
brc-dd Sep 9, 2022
74f0206
feat: merge theme config too
brc-dd Sep 10, 2022
2509ab9
fix: don't add base to relative links
brc-dd Sep 10, 2022
ad8a368
fix: don't remove relative part from url during normalization
brc-dd Sep 10, 2022
b2a89ca
Merge branch 'main' into feat/i18n-next
brc-dd Sep 14, 2022
fd0beb5
feat: allow specifying dir attribute for html
brc-dd Sep 14, 2022
df14075
remove stuff from translations
brc-dd Sep 16, 2022
f18132c
Merge branch 'main' into feat/i18n-next
brc-dd Sep 16, 2022
89ed8eb
Merge branch 'main' into feat/i18n-next
brc-dd Sep 21, 2022
a0f4e1e
Merge branch 'main' into feat/i18n-next
brc-dd Sep 24, 2022
7ea68f4
Merge branch 'main' into feat/i18n-next
brc-dd Sep 28, 2022
335c0b3
feat: use current locale link in 404 page
brc-dd Sep 28, 2022
4d35ae5
Merge branch 'main' into feat/i18n-next
brc-dd Sep 28, 2022
4884707
Merge branch 'main' into feat/i18n-next
brc-dd Sep 28, 2022
01bae65
Merge branch 'main' into feat/i18n-next
brc-dd Oct 11, 2022
064857c
fix(theme): properly apply base in locale links
brc-dd Oct 11, 2022
5df3e19
Merge branch 'main' into feat/i18n-next
brc-dd Oct 13, 2022
49fe1b0
Merge branch 'main' into feat/i18n-next
brc-dd Oct 20, 2022
50d1202
Merge branch 'main' into feat/i18n-next
brc-dd Nov 2, 2022
3f89591
Merge branch 'main' into feat/i18n-next
brc-dd Nov 7, 2022
b4e1483
feat(theme): add rtlcss directive (#1512)
sadeghbarati Nov 7, 2022
43fdc7c
Merge branch 'main' into feat/i18n-next
brc-dd Nov 18, 2022
bf129ec
Merge branch 'main' into feat/i18n-next
brc-dd Dec 5, 2022
0075b27
feat: multilingual search
brc-dd Dec 7, 2022
5322e4b
revert: useData type to any
brc-dd Dec 7, 2022
907de58
use typed useData in theme-default
brc-dd Dec 7, 2022
2b65d80
Merge branch 'main' into feat/i18n-next
brc-dd Dec 11, 2022
8392442
Merge branch 'main' into feat/i18n-next
brc-dd Dec 21, 2022
f77b51f
feat: go to corresponding link on changing locale
brc-dd Dec 21, 2022
e842edf
feat: allow disabling automatic locale link mapping
brc-dd Dec 21, 2022
f185032
fix i18n routing for root locale
brc-dd Dec 21, 2022
aa82162
Merge branch 'main' into feat/i18n-next
brc-dd Jan 4, 2023
4306708
fix merge
brc-dd Jan 4, 2023
857d97e
fix?
brc-dd Jan 4, 2023
f748f8a
Merge branch 'main' into feat/i18n-next
brc-dd Jan 8, 2023
7e217a8
Merge branch 'main' into feat/i18n-next
brc-dd Jan 12, 2023
3d0ce5d
fix ts
brc-dd Jan 12, 2023
12c9a1f
fix locale change on home page
brc-dd Jan 13, 2023
b2ac950
docs
brc-dd Jan 13, 2023
5559bb3
fix conflicts
brc-dd Jan 17, 2023
511911b
Merge branch 'main' into feat/i18n-next
brc-dd Jan 17, 2023
7ca1b9f
fix conflicts
brc-dd Jan 17, 2023
12f8253
Merge branch 'main' into feat/i18n-next
brc-dd Jan 17, 2023
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
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ function sidebarGuide() {
{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Configuration', link: '/guide/configuration' },
{ text: 'Deploying', link: '/guide/deploying' }
{ text: 'Deploying', link: '/guide/deploying' },
{ text: 'Internationalization', link: '/guide/i18n' }
]
},
{
Expand Down
6 changes: 6 additions & 0 deletions docs/config/theme-configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export default {

Here it describes the settings for the VitePress default theme. If you're using a custom theme created by others, these settings may not have any effect, or might behave differently.

## i18nRouting

- Type: `boolean`

Changing locale to say `zh` will change the URL from `/foo` (or `/en/foo/`) to `/zh/foo`. You can disable this behavior by setting `themeConfig.i18nRouting` to `false`.

## logo

- Type: `ThemeableImage`
Expand Down
11 changes: 6 additions & 5 deletions docs/guide/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ Methods that start with `use*` indicates that it is a [Vue 3 Composition API](ht
Returns page-specific data. The returned object has the following type:

```ts
interface VitePressData {
site: Ref<SiteData>
interface VitePressData<T = any> {
site: Ref<SiteData<T>>
page: Ref<PageData>
theme: Ref<any> // themeConfig from .vitepress/config.js
theme: Ref<T> // themeConfig from .vitepress/config.js
frontmatter: Ref<PageData['frontmatter']>
lang: Ref<string>
title: Ref<string>
description: Ref<string>
localePath: Ref<string>
lang: Ref<string>
isDark: Ref<boolean>
dir: Ref<string>
localeIndex: Ref<string>
}
```

Expand Down
99 changes: 99 additions & 0 deletions docs/guide/i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Internationalization

To use the built-in i18n features, one needs to create a directory structure as follows:

```
docs/
├─ es/
│ ├─ foo.md
├─ fr/
│ ├─ foo.md
├─ foo.md
```

Then in `docs/.vitepress/config.ts`:

```ts
import { defineConfig } from 'vitepress'

export default defineConfig({
// shared properties and other top-level stuff...

locales: {
root: {
label: 'English',
lang: 'en'
},
fr: {
label: 'French',
lang: 'fr', // optional, will be added as `lang` attribute on `html` tag
link: '/fr/guide' // default /fr/ -- shows on navbar translations menu, can be external

// other locale specific properties...
}
}
})
```

The following properties can be overridden for each locale (including root):

```ts
interface LocaleSpecificConfig<ThemeConfig = any> {
lang?: string
dir?: string
title?: string
titleTemplate?: string | boolean
description?: string
head?: HeadConfig[] // will be merged with existing head entries, duplicate meta tags are automatically removed
themeConfig?: ThemeConfig // will be shallow merged, common stuff can be put in top-level themeConfig entry
}
```

Refer [`DefaultTheme.Config`](https://github.com/vuejs/vitepress/blob/main/types/default-theme.d.ts) interface for details on customizing the placeholder texts of the default theme. Don't override `themeConfig.algolia` or `themeConfig.carbonAds` at locale-level. Refer [Algolia docs](./theme-search#i18n) for using multilingual search.

**Pro tip:** Config file can be stored at `docs/.vitepress/config/index.ts` too. It might help you organize stuff by creating a configuration file per locale and then merge and export them from `index.ts`.

## Separate directory for each locale

The following is a perfectly fine structure:

```
docs/
├─ en/
│ ├─ foo.md
├─ es/
│ ├─ foo.md
├─ fr/
├─ foo.md
```

However, VitePress won't redirect `/` to `/en/` by default. You'll need to configure your server for that. For example, on Netlify, you can add a `docs/public/_redirects` file like this:

```
/* /es/:splat 302 Language=es
/* /fr/:splat 302 Language=fr
/* /en/:splat 302
```

**Pro tip:** If using the above approach, you can use `nf_lang` cookie to persist user's language choice. A very basic way to do this is register a watcher inside the [setup](./theme-introduction#using-a-custom-theme) function of custom theme:

```ts
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'

export default {
...DefaultTheme,
setup() {
const { lang } = useData()
watchEffect(() => {
if (inBrowser) {
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2024 00:00:00 UTC; path=/`
}
})
}
}
```

## RTL Support (Experimental)

For RTL support, specify `dir: 'rtl'` in config and use some RTLCSS PostCSS plugin like <https://github.com/MohammadYounes/rtlcss>, <https://github.com/vkalinichev/postcss-rtl> or <https://github.com/elchininet/postcss-rtlcss>. You'll need to configure your PostCSS plugin to use `:where([dir="ltr"])` and `:where([dir="rtl"])` as prefixes to prevent CSS specificity issues.
84 changes: 83 additions & 1 deletion docs/guide/theme-search.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,85 @@
# Search

Documentation coming soon...
VitePress supports searching your docs site using [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Refer their getting started guide. In your `.vitepress/config.ts` you'll need to provide at least the following to make it work:

```ts
import { defineConfig } from 'vitepress'

export default defineConfig({
themeConfig: {
algolia: {
appId: '...',
apiKey: '...',
indexName: '...'
}
}
})
```

If you are not eligible for DocSearch, you might wanna use some community plugins like <https://github.com/emersonbottero/vitepress-plugin-search> or explore some custom solutions on [this GitHub thread](https://github.com/vuejs/vitepress/issues/670).

## i18n

You can use a config like this to use multilingual search:

```ts
import { defineConfig } from 'vitepress'

export default defineConfig({
// ...
themeConfig: {
// ...

algolia: {
appId: '...',
apiKey: '...',
indexName: '...',
locales: {
zh: {
placeholder: '搜索文档',
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '搜索文档'
},
modal: {
searchBox: {
resetButtonTitle: '清除查询条件',
resetButtonAriaLabel: '清除查询条件',
cancelButtonText: '取消',
cancelButtonAriaLabel: '取消'
},
startScreen: {
recentSearchesTitle: '搜索历史',
noRecentSearchesText: '没有搜索历史',
saveRecentSearchButtonTitle: '保存至搜索历史',
removeRecentSearchButtonTitle: '从搜索历史中移除',
favoriteSearchesTitle: '收藏',
removeFavoriteSearchButtonTitle: '从收藏中移除'
},
errorScreen: {
titleText: '无法获取结果',
helpText: '你可能需要检查你的网络连接'
},
footer: {
selectText: '选择',
navigateText: '切换',
closeText: '关闭',
searchByText: '搜索提供者'
},
noResultsScreen: {
noResultsText: '无法找到相关结果',
suggestedQueryText: '你可以尝试查询',
reportMissingResultsText: '你认为该查询应该有结果?',
reportMissingResultsLinkText: '点击反馈'
}
}
}
}
}
}
}
})
```

[These options](https://github.com/vuejs/vitepress/blob/main/types/docsearch.d.ts) can be overridden. Refer official Algolia docs to learn more about them.
2 changes: 1 addition & 1 deletion rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { promises as fs } from 'fs'
import { builtinModules, createRequire } from 'module'
import { resolve } from 'path'
import { fileURLToPath } from 'url'
import { RollupOptions, defineConfig } from 'rollup'
import { type RollupOptions, defineConfig } from 'rollup'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import esbuild from 'rollup-plugin-esbuild'
Expand Down
15 changes: 5 additions & 10 deletions src/client/app/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
resolveSiteDataByRoute,
createTitle
} from '../shared.js'
import { withBase } from './utils.js'

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

Expand All @@ -27,8 +26,9 @@ export interface VitePressData<T = any> {
title: Ref<string>
description: Ref<string>
lang: Ref<string>
localePath: Ref<string>
isDark: Ref<boolean>
dir: Ref<string>
localeIndex: Ref<string>
}

// site data is a singleton
Expand All @@ -48,7 +48,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 @@ -57,13 +57,8 @@ 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 || '/')
}),
dir: computed(() => site.value.dir),
localeIndex: computed(() => site.value.localeIndex || 'root'),
title: computed(() => {
return createTitle(site.value, route.data)
}),
Expand Down
13 changes: 5 additions & 8 deletions src/client/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
defineComponent,
h,
onMounted,
watch
watchEffect
} from 'vue'
import Theme from '@theme/index'
import { inBrowser, pathToFile } from './utils.js'
Expand All @@ -28,13 +28,10 @@ const VitePressApp = defineComponent({

// change the language on the HTML element based on the current lang
onMounted(() => {
watch(
() => site.value.lang,
(lang: string) => {
document.documentElement.lang = lang
},
{ immediate: true }
)
watchEffect(() => {
document.documentElement.lang = site.value.lang
document.documentElement.dir = site.value.dir
})
})

if (import.meta.env.PROD) {
Expand Down
4 changes: 2 additions & 2 deletions 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, sanitizeFileName } from '../shared.js'

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

/**
* Join two paths by resolving the slash collision.
Expand All @@ -11,7 +11,7 @@ export function joinPath(base: string, path: string): string {
}

export function withBase(path: string) {
return EXTERNAL_URL_RE.test(path)
return EXTERNAL_URL_RE.test(path) || path.startsWith('.')
? path
: joinPath(siteDataRef.value.base, path)
}
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
3 changes: 2 additions & 1 deletion src/client/theme-default/Layout.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed, provide, useSlots, watch } from 'vue'
import { useData, useRoute } from 'vitepress'
import { useRoute } from 'vitepress'
import { useData } from './composables/data.js'
import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar.js'
import VPSkipLink from './components/VPSkipLink.vue'
import VPBackdrop from './components/VPBackdrop.vue'
Expand Down
Loading