|
| 1 | +--- |
| 2 | +title: Configuration |
| 3 | +description: Configure your Explainer project globally and per-app using @explainer/config. |
| 4 | +icon: settings |
| 5 | +order: 3 |
| 6 | +--- |
| 7 | + |
| 8 | +# Configuration |
| 9 | + |
| 10 | +Explainer uses a centralized configuration system via the `@explainer/config` package. It provides global defaults that can be overridden per-app. |
| 11 | + |
| 12 | +## Package structure |
| 13 | + |
| 14 | +```text |
| 15 | +packages/config/src/ |
| 16 | +├── contracts.ts # TypeScript interfaces (SiteConfig, Sponsor, FooterLink, I18nMessages) |
| 17 | +├── config.ts # Default configuration values (single structure with i18n keys) |
| 18 | +├── i18n.ts # Translations keyed by locale (flat key-value pairs) |
| 19 | +├── utils.ts # Helper functions (defineConfig, formatTitle, t, resolveHref) |
| 20 | +└── index.ts # Public exports |
| 21 | +``` |
| 22 | + |
| 23 | +## Global configuration |
| 24 | + |
| 25 | +The default configuration is defined in `packages/config/src/config.ts`. Translatable properties reference i18n keys instead of containing raw strings: |
| 26 | + |
| 27 | +```ts |
| 28 | +import type { SiteConfig } from './contracts' |
| 29 | + |
| 30 | +export const defaultConfig: SiteConfig = { |
| 31 | + name: 'Explainer', |
| 32 | + titleTemplate: '%s — Explainer', |
| 33 | + favicon: '/favicon.svg', |
| 34 | + logo: '/logo.svg', |
| 35 | + thumbnail: '/thumbnail.png', |
| 36 | + twitterCard: 'summary_large_image', |
| 37 | + ogType: 'website', |
| 38 | + github: 'https://github.com/LeadcodeDev/explainer_v2', |
| 39 | + sponsors: [/* ... */], |
| 40 | + defaultLocale: 'en', |
| 41 | + locales: ['en', 'fr'], |
| 42 | + footer: { |
| 43 | + description: 'footer.description', // i18n key |
| 44 | + columns: { |
| 45 | + documentation: 'footer.columns.documentation', // i18n key |
| 46 | + resources: 'footer.columns.resources', |
| 47 | + community: 'footer.columns.community', |
| 48 | + }, |
| 49 | + copyright: 'footer.copyright', |
| 50 | + builtWith: 'footer.builtWith', |
| 51 | + links: { |
| 52 | + documentation: [ |
| 53 | + { label: 'footer.links.gettingStarted', href: '/{locale}/explainer/getting-started' }, |
| 54 | + // ... |
| 55 | + ], |
| 56 | + resources: [ |
| 57 | + { label: 'footer.links.github', href: 'https://github.com/...', external: true }, |
| 58 | + { label: 'footer.links.blog', href: '', appId: 'blog' }, |
| 59 | + ], |
| 60 | + community: [ |
| 61 | + { label: 'footer.links.issues', href: 'https://github.com/.../issues', external: true }, |
| 62 | + ], |
| 63 | + }, |
| 64 | + }, |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Link `href` values support a `{locale}` placeholder that is resolved at render time. |
| 69 | + |
| 70 | +## SiteConfig reference |
| 71 | + |
| 72 | +| Property | Type | Description | |
| 73 | +|----------|------|-------------| |
| 74 | +| `name` | `string` | Project name displayed in navbar and footer | |
| 75 | +| `titleTemplate` | `string` | Page title template. Use `%s` as placeholder (e.g. `%s — Explainer`) | |
| 76 | +| `favicon` | `string` | Path to the favicon file | |
| 77 | +| `logo` | `string` | Path to the logo file (used in navbar) | |
| 78 | +| `thumbnail` | `string` | Default OG image path | |
| 79 | +| `twitterCard` | `'summary' \| 'summary_large_image'` | Twitter card type | |
| 80 | +| `ogType` | `string` | Open Graph type | |
| 81 | +| `github` | `string` | GitHub repository URL | |
| 82 | +| `sponsors` | `Sponsor[]` | List of sponsors | |
| 83 | +| `defaultLocale` | `string` | Default locale for translations | |
| 84 | +| `locales` | `string[]` | Supported locales | |
| 85 | +| `footer` | `object` | Footer configuration with i18n keys and links | |
| 86 | + |
| 87 | +## Per-app override |
| 88 | + |
| 89 | +Each app can override the global configuration using `defineConfig()` in its `src/config.ts`: |
| 90 | + |
| 91 | +```ts [apps/docs/src/config.ts] |
| 92 | +import { defineConfig } from '@explainer/config' |
| 93 | + |
| 94 | +export const siteConfig = defineConfig({ |
| 95 | + titleTemplate: '%s — Explainer', |
| 96 | +}) |
| 97 | +``` |
| 98 | + |
| 99 | +```ts [apps/blog/src/config.ts] |
| 100 | +import { defineConfig } from '@explainer/config' |
| 101 | + |
| 102 | +export const siteConfig = defineConfig({ |
| 103 | + titleTemplate: '%s — Blog', |
| 104 | +}) |
| 105 | +``` |
| 106 | + |
| 107 | +`defineConfig()` deep-merges your overrides with the default configuration. You only need to specify the properties you want to change. |
| 108 | + |
| 109 | +## Internationalization (i18n) |
| 110 | + |
| 111 | +Translations are stored in `packages/config/src/i18n.ts` as flat key-value maps per locale: |
| 112 | + |
| 113 | +```ts |
| 114 | +import type { I18nMessages } from './contracts' |
| 115 | + |
| 116 | +export const i18n: Record<string, I18nMessages> = { |
| 117 | + en: { |
| 118 | + 'description': 'Documentation boilerplate for developers.', |
| 119 | + 'footer.description': 'A modern documentation framework...', |
| 120 | + 'footer.columns.documentation': 'Documentation', |
| 121 | + 'footer.columns.resources': 'Resources', |
| 122 | + 'footer.columns.community': 'Community', |
| 123 | + 'footer.copyright': '© {year} Explainer. All rights reserved.', |
| 124 | + 'footer.builtWith': 'Built with {icon} using Astro', |
| 125 | + 'footer.links.gettingStarted': 'Getting Started', |
| 126 | + // ... |
| 127 | + }, |
| 128 | + fr: { |
| 129 | + 'description': 'Boilerplate de documentation pour les développeurs.', |
| 130 | + 'footer.description': 'Un framework de documentation moderne...', |
| 131 | + 'footer.columns.documentation': 'Documentation', |
| 132 | + 'footer.columns.resources': 'Ressources', |
| 133 | + 'footer.columns.community': 'Communauté', |
| 134 | + 'footer.copyright': '© {year} Explainer. Tous droits réservés.', |
| 135 | + 'footer.builtWith': 'Construit avec {icon} grâce à Astro', |
| 136 | + 'footer.links.gettingStarted': 'Premiers pas', |
| 137 | + // ... |
| 138 | + }, |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +Supported placeholders: `{year}` (current year), `{icon}` (heart icon), `{locale}` (in hrefs). |
| 143 | + |
| 144 | +### Using translations |
| 145 | + |
| 146 | +Use `t()` to resolve an i18n key for a given locale: |
| 147 | + |
| 148 | +```ts |
| 149 | +import { t } from '@explainer/config' |
| 150 | + |
| 151 | +t('fr', 'footer.description') |
| 152 | +// "Un framework de documentation moderne..." |
| 153 | +``` |
| 154 | + |
| 155 | +Use `resolveHref()` to replace `{locale}` in URLs: |
| 156 | + |
| 157 | +```ts |
| 158 | +import { resolveHref } from '@explainer/config' |
| 159 | + |
| 160 | +resolveHref('/{locale}/explainer/getting-started', 'fr') |
| 161 | +// "/fr/explainer/getting-started" |
| 162 | +``` |
| 163 | + |
| 164 | +If the requested locale is not found, `t()` falls back to `defaultLocale`. |
| 165 | + |
| 166 | +## Sponsors |
| 167 | + |
| 168 | +Sponsors are defined globally in the configuration: |
| 169 | + |
| 170 | +```ts |
| 171 | +sponsors: [ |
| 172 | + { |
| 173 | + id: 'mineral', |
| 174 | + name: 'Mineral', |
| 175 | + href: 'https://mineral-dart.dev/', |
| 176 | + logoUrl: 'https://mineral-dart.dev/logo.svg', |
| 177 | + tier: 'silver', // 'gold' | 'silver' | 'bronze' |
| 178 | + }, |
| 179 | +] |
| 180 | +``` |
| 181 | + |
| 182 | +Each sponsor has a `tier` property that determines its visual styling. |
| 183 | + |
| 184 | +## Utility functions |
| 185 | + |
| 186 | +| Function | Signature | Description | |
| 187 | +|----------|-----------|-------------| |
| 188 | +| `defineConfig` | `(overrides?) => SiteConfig` | Creates a config by deep-merging overrides with defaults | |
| 189 | +| `formatTitle` | `(config, pageTitle) => string` | Applies the title template to a page title | |
| 190 | +| `t` | `(locale, key) => string` | Resolves an i18n key for a given locale with fallback | |
| 191 | +| `resolveHref` | `(href, locale?) => string` | Replaces `{locale}` placeholder in URLs | |
| 192 | +| `getMessages` | `(locale?) => I18nMessages` | Returns the full message map for a locale | |
0 commit comments