vuejs / vitepress

Vite & Vue powered static site generator.
https://vitepress.dev
MIT License
12.98k stars 2.09k forks source link

Support i18n in `contentLoader` #4072

Closed Barbapapazes closed 4 weeks ago

Barbapapazes commented 3 months ago

Is your feature request related to a problem? Please describe.

Hello,

Actually, I have to create a loader for each language, and then import all of them in my vue component.

// @ts-expect-error - vitepress types are not available
import { data as rootData } from '../../data/posts.data'
// @ts-expect-error - vitepress types are not available
import { data as frData } from '../../data/fr-posts.data'
import { computed } from 'vue';

const data: Record<string, any> = {
  root: rootData,
  fr: frData
}

const {  localeIndex  } = useData()

const posts = computed(() => data[localeIndex.value])

Describe the solution you'd like

I would like to have to only create a single content loader and based on my locales configuration (and maybe an option) retrive all content.

For example: posts/*.md should get posts/*.md (the root) and fr/posts/*.md (the fr locale).

Describe alternatives you've considered

No response

Additional context

No response

Validations

brc-dd commented 3 months ago

This should already work. Can you share an example where createContentLoader doesn't respect the specified glob pattern?

Also, regarding those ts-expect-error comments in your code, you need to declare types in your data file - those files are not generated by vitepress - https://vitepress.dev/guide/data-loading#typed-data-loaders (for createContentLoader the Data type should be array of ContentData, refer docs above that section too)

brc-dd commented 3 months ago

Ah, if you want to have a single content loader, you can adjust your glob:

import { createContentLoader, type ContentData, type SiteConfig } from 'vitepress'

export type Data = Record<string, ContentData[]>
export declare const data: Data

const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
const locales = Object.keys(config.userConfig.locales ?? {})

// or simply - const locales = ['root', 'fr']

export default createContentLoader('**/posts/**/*.md', {
  transform(data) {
    const grouped: Data = {}

    data.forEach((item) => {
      let locale = item.url.split('/')[1]
      locale = locales.includes(locale) ? locale : 'root'
      ;(grouped[locale] ??= []).push(item)
    })

    return grouped
  }
})
Barbapapazes commented 4 weeks ago

This works wonderfully! Thank you.

export type Data = Record<string, ContentData[]> export declare const data: Data

This is cool to have types! Until now, I had a ts-expect-error comment. 😅