Skip to content
Draft
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
210 changes: 210 additions & 0 deletions TD-21-i18n-glossary.md

Large diffs are not rendered by default.

524 changes: 524 additions & 0 deletions TD-21-i18n_project.md

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
import { defineConfig } from 'astro/config'
import starlight from '@astrojs/starlight'
import sidebar from './src/siteNavigation.json'
import { remarkLocalizeLinks } from './src/lib/remark-localize-links.ts'

// https://astro.build/config
export default defineConfig({
site: 'https://docs.archivesspace.org',
markdown: {
remarkPlugins: [remarkLocalizeLinks]
},
integrations: [
starlight({
title: 'Tech Docs',
defaultLocale: 'root',
locales: {
root: {
label: 'English',
lang: 'en'
},
nl: {
label: 'Nederlands',
lang: 'nl'
},
fr: {
label: 'Français',
lang: 'fr'
},
de: {
label: 'Deutsch',
lang: 'de'
},
ja: {
label: '日本語',
lang: 'ja'
},
es: {
label: 'Español',
lang: 'es'
},
uk: {
label: 'Українська',
lang: 'uk'
}
},
routeMiddleware: './src/blogRouteData.js',
logo: {
dark: './src/images/logo-full-dark.svg',
Expand Down
5 changes: 4 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
"@astrojs/starlight": "^0.41.5",
"astro": "^7.1.6",
"mermaid": "^11.14.0",
"sharp": "^0.35.3"
"sharp": "^0.35.3",
"unist-util-visit": "^5.1.0"
},
"devDependencies": {
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
"cypress": "^15.13.0",
"postcss-html": "^1.8.0",
"prettier": "^3.8.1",
Expand Down
7 changes: 4 additions & 3 deletions src/content.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineCollection } from 'astro:content'
import { glob } from 'astro/loaders'
import { docsLoader } from '@astrojs/starlight/loaders'
import { docsSchema } from '@astrojs/starlight/schema'
import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders'
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'
import { z } from 'astro/zod'
import { DEFAULT_ISSUE_TEXT, DEFAULT_ISSUE_URL } from '@lib/constants.ts'

Expand Down Expand Up @@ -30,5 +30,6 @@ export const collections = {
blog: defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
schema: blogSchema
})
}),
i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() })
}
78 changes: 78 additions & 0 deletions src/lib/remark-localize-links.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { visit } from 'unist-util-visit'
import type { Root } from 'mdast'
import type { VFile } from 'vfile'

/**
* Locale folders under `src/content/docs/` for translated content, in
* addition to the `en.yml` languages listed in `TD-21-i18n_project.md`.
* Keep in sync with the `locales` configured for Starlight once i18n is
* enabled in `astro.config.mjs`.
*/
const LOCALE_CODES = ['fr', 'de', 'ja', 'es', 'uk', 'nl'] as const

type LocaleCode = (typeof LOCALE_CODES)[number]

const DOCS_COLLECTION_SEGMENT = 'content/docs/'

/**
* Remark plugin that rewrites internal documentation links so they point at
* the current file's own locale instead of always pointing at the English
* (root) path.
*
* Starlight does not localize plain Markdown/MDX link hrefs on its own --
* that only happens for helpers like `getRelativeLocaleUrl()` used inside
* `.astro` components. Without this plugin, a link such as
* `[Public user interface](/architecture/public)` written in a translated
* page would keep sending readers back to the English page.
*
* English is the `root` locale (unprefixed paths, e.g. `/architecture/public`
* stays as-is), so files outside a locale folder are left untouched. Files
* under `src/content/docs/<locale>/` get every internal, non-blog doc link
* prefixed with `/<locale>`. This is safe even before a page has been
* translated: Starlight generates a route for every locale-prefixed path and
* automatically falls back to the English content (with a "not yet
* translated" notice) until a real translation exists -- see "Fallback
* content" in `TD-21-i18n_project.md`.
*/
export function remarkLocalizeLinks() {
return (tree: Root, file: VFile) => {
const locale = getFileLocale(file.path)
if (!locale) return // root (English) content -- links are already correct

visit(tree, 'link', (node) => {
if (shouldLocalize(node.url)) {
node.url = `/${locale}${node.url}`
}
})
}
}

function getFileLocale(filePath: string | undefined): LocaleCode | undefined {
if (!filePath) return undefined

const normalizedPath = filePath.replaceAll('\\', '/')
const collectionIndex = normalizedPath.indexOf(DOCS_COLLECTION_SEGMENT)
if (collectionIndex === -1) return undefined

const relativePath = normalizedPath.slice(
collectionIndex + DOCS_COLLECTION_SEGMENT.length
)
const [firstSegment] = relativePath.split('/')

return (LOCALE_CODES as readonly string[]).includes(firstSegment)
? (firstSegment as LocaleCode)
: undefined
}

function shouldLocalize(url: string): boolean {
if (!url.startsWith('/')) return false // relative, anchor-only, or external
if (url.startsWith('/blog/')) return false // blog is not a localized collection

return !isAlreadyLocalized(url)
}

function isAlreadyLocalized(url: string): boolean {
return LOCALE_CODES.some(
(locale) => url === `/${locale}` || url.startsWith(`/${locale}/`)
)
}
Loading