fimo 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-templates/broken-link-sweeper/capabilities.yaml +0 -4
- package/assets/agent-templates/content-translator/capabilities.yaml +0 -2
- package/assets/agent-templates/seo-audit/capabilities.yaml +0 -1
- package/assets/content-templates/types-template.eta +13 -7
- package/assets/skills/fimo/SKILL.md +2 -2
- package/assets/skills/fimo-cli/SKILL.md +8 -8
- package/assets/skills/fimo-cli/references/agent-setup.md +1 -1
- package/assets/skills/fimo-cli/references/agents.md +5 -12
- package/assets/skills/fimo-cli/references/assets.md +2 -2
- package/assets/skills/fimo-cli/references/content.md +16 -30
- package/assets/skills/fimo-cli/references/describe.md +1 -1
- package/assets/skills/fimo-cli/references/forms.md +8 -8
- package/assets/skills/fimo-cli/references/init.md +5 -4
- package/assets/skills/fimo-cli/references/labels.md +4 -4
- package/assets/skills/fimo-cli/references/locales.md +3 -3
- package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +1 -1
- package/assets/skills/fimo-code/SKILL.md +40 -50
- package/assets/skills/fimo-code/references/assets.md +6 -4
- package/assets/skills/fimo-code/references/content/clients.md +130 -0
- package/assets/skills/fimo-code/references/content/primitives.md +5 -0
- package/assets/skills/fimo-code/references/content/richtext.md +48 -0
- package/assets/skills/fimo-code/references/content/schema.md +122 -0
- package/assets/skills/fimo-code/references/content.md +12 -597
- package/assets/skills/fimo-code/references/design.md +1 -1
- package/assets/skills/fimo-code/references/frameworks/next/app-router.md +116 -0
- package/assets/skills/fimo-code/references/frameworks/next/locales.md +59 -0
- package/assets/skills/fimo-code/references/frameworks/next/pages-router.md +99 -0
- package/assets/skills/fimo-code/references/frameworks/next.md +116 -0
- package/assets/skills/fimo-code/references/frameworks/react-router.md +295 -0
- package/assets/skills/fimo-code/references/frameworks/vite-react.md +225 -0
- package/assets/skills/fimo-code/references/labels.md +24 -83
- package/assets/skills/fimo-code/references/locales.md +22 -111
- package/assets/skills/fimo-code/references/setup-plain-vite.md +3 -227
- package/assets/skills/fimo-code/references/setup-react-router.md +3 -276
- package/assets/skills/fimo-code/references/ui.md +32 -78
- package/dist/cli/bundle.json +2 -2
- package/dist/cli/index.js +16 -16
- package/dist/scripts/extract-translations.js +8 -1
- package/package.json +1 -1
- package/templates/react-router/AGENTS.md +10 -10
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/react-router.config.ts +1 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Setup Fimo on plain Vite + React
|
|
2
|
+
|
|
3
|
+
Use this recipe for a Vite + React SPA without React Router framework mode.
|
|
4
|
+
This is also the upgrade path for legacy Fimo v3 projects — v3 was plain Vite,
|
|
5
|
+
so this layout is what those projects look like now.
|
|
6
|
+
|
|
7
|
+
## When to use this vs. React Router
|
|
8
|
+
|
|
9
|
+
- **Plain Vite (this recipe):** simplest setup; client-side routing via a
|
|
10
|
+
React Router library router (`BrowserRouter`, no framework-mode SSR/SSG);
|
|
11
|
+
SEO via build-time HTML rewrite. Per-route SEO that crawlers see requires
|
|
12
|
+
prerendering (SSG); without it, the SPA serves one `index.html` for every
|
|
13
|
+
URL and crawlers see only the home SEO.
|
|
14
|
+
- **React Router (`references/frameworks/react-router.md`):** framework-mode SSR + per-route
|
|
15
|
+
SEO via `meta` export; recommended when you want real per-route crawler SEO.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Node 20+
|
|
20
|
+
- A Fimo project created via `fimo init`.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pnpm add fimo react react-dom react-router @tanstack/react-query
|
|
26
|
+
pnpm add -D vite @vitejs/plugin-react @types/react @types/react-dom typescript
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## File: `package.json`
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"name": "__PROJECT_SLUG__",
|
|
34
|
+
"version": "0.0.0",
|
|
35
|
+
"private": true,
|
|
36
|
+
"type": "module",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "vite --host 0.0.0.0 --port 5173",
|
|
39
|
+
"build": "vite build",
|
|
40
|
+
"preview": "vite preview --host 0.0.0.0 --port 4173",
|
|
41
|
+
"typecheck": "tsc --noEmit"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## File: `vite.config.ts`
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { defineConfig } from 'vite';
|
|
50
|
+
|
|
51
|
+
import { fimo } from 'fimo/vite';
|
|
52
|
+
|
|
53
|
+
export default defineConfig(() => ({
|
|
54
|
+
plugins: fimo({ seo: true }),
|
|
55
|
+
}));
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Notes:
|
|
59
|
+
|
|
60
|
+
- `fimo({ seo: true })` opts in to the build-time SEO plugin
|
|
61
|
+
(`transformIndexHtml`-based). This is the right tool for plain Vite — there's
|
|
62
|
+
no framework `meta` export to plug into.
|
|
63
|
+
- Add `fimo({ seo: true, sitemap: true, robots: true })` to also emit
|
|
64
|
+
`sitemap.xml` and `robots.txt` at build.
|
|
65
|
+
|
|
66
|
+
## File: `index.html`
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<!doctype html>
|
|
70
|
+
<html lang="en">
|
|
71
|
+
<head>
|
|
72
|
+
<meta charset="UTF-8" />
|
|
73
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
74
|
+
<title>__PROJECT_SLUG__</title>
|
|
75
|
+
</head>
|
|
76
|
+
<body>
|
|
77
|
+
<div id="root"></div>
|
|
78
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
79
|
+
</body>
|
|
80
|
+
</html>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `seo` plugin rewrites `<title>`, og, twitter, canonical tags from
|
|
84
|
+
`.fimo/config.json#seo` at build time. You can leave a placeholder title in
|
|
85
|
+
`index.html`; it'll be replaced.
|
|
86
|
+
|
|
87
|
+
## File: `src/main.tsx`
|
|
88
|
+
|
|
89
|
+
`FimoProviders` requires a React Router `<Router>` ancestor (it reads
|
|
90
|
+
`useLocation`/`useNavigate`). It reuses a host app's
|
|
91
|
+
`@tanstack/react-query` client when one exists and supplies a stable fallback
|
|
92
|
+
for adopted SPAs. This recipe still mounts an explicit provider so the app owns
|
|
93
|
+
the shared cache and its configuration:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { FimoProviders } from 'fimo/react-router';
|
|
97
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
98
|
+
import React from 'react';
|
|
99
|
+
import { createRoot } from 'react-dom/client';
|
|
100
|
+
import { BrowserRouter } from 'react-router';
|
|
101
|
+
|
|
102
|
+
import { App } from './App';
|
|
103
|
+
|
|
104
|
+
const queryClient = new QueryClient();
|
|
105
|
+
|
|
106
|
+
createRoot(document.getElementById('root')!).render(
|
|
107
|
+
<React.StrictMode>
|
|
108
|
+
<QueryClientProvider client={queryClient}>
|
|
109
|
+
<BrowserRouter>
|
|
110
|
+
<FimoProviders>
|
|
111
|
+
<App />
|
|
112
|
+
</FimoProviders>
|
|
113
|
+
</BrowserRouter>
|
|
114
|
+
</QueryClientProvider>
|
|
115
|
+
</React.StrictMode>
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`react-router` and `@tanstack/react-query` are peer dependencies of `fimo`;
|
|
120
|
+
both are in the install command above. `FimoProviders` is exported only from
|
|
121
|
+
the `fimo/react-router` adapter barrel. Keep the explicit QueryClientProvider
|
|
122
|
+
when the app needs custom query defaults or shared cache access; adopted SPAs
|
|
123
|
+
can omit it.
|
|
124
|
+
|
|
125
|
+
## File: `src/App.tsx`
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { Text, useLabels } from 'fimo/ui';
|
|
129
|
+
|
|
130
|
+
export function App() {
|
|
131
|
+
const { t } = useLabels();
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<main>
|
|
135
|
+
<Text value={t('home.hero.title')} as="h1" />
|
|
136
|
+
</main>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## File: `.fimo/config.json`
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"version": "3",
|
|
146
|
+
"seo": {
|
|
147
|
+
"title": "__PROJECT_SLUG__",
|
|
148
|
+
"siteName": "__PROJECT_SLUG__"
|
|
149
|
+
},
|
|
150
|
+
"routes": {
|
|
151
|
+
"home": {
|
|
152
|
+
"label": "Home",
|
|
153
|
+
"path": "/"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Note: `"version": "3"` marks the legacy plain-Vite (no-RR-framework-mode)
|
|
160
|
+
shape. `fimo()` is version-agnostic — it composes a fixed set of build-time
|
|
161
|
+
plugins from the `seo`/`sitemap`/`robots` options you pass and never reads
|
|
162
|
+
this version field. It also never injects the React Router plugin: in a
|
|
163
|
+
framework-mode project that `reactRouter()` plugin is added by your own
|
|
164
|
+
`vite.config.ts`, not by `fimo/vite`.
|
|
165
|
+
|
|
166
|
+
## File: `.fimo/project.json`
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"projectId": "__PROJECT_ID__",
|
|
171
|
+
"organizationId": "__ORG_ID__",
|
|
172
|
+
"apiUrl": "__API_URL__"
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## File: `tsconfig.json`
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"compilerOptions": {
|
|
181
|
+
"target": "ES2022",
|
|
182
|
+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
183
|
+
"module": "ESNext",
|
|
184
|
+
"moduleResolution": "Bundler",
|
|
185
|
+
"jsx": "react-jsx",
|
|
186
|
+
"strict": true,
|
|
187
|
+
"esModuleInterop": true,
|
|
188
|
+
"skipLibCheck": true,
|
|
189
|
+
"resolveJsonModule": true,
|
|
190
|
+
"isolatedModules": true,
|
|
191
|
+
"types": ["vite/client"]
|
|
192
|
+
},
|
|
193
|
+
"include": ["src", "*.config.ts"]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Verify and iterate
|
|
198
|
+
|
|
199
|
+
Run the app with the project's package-manager `dev` script (`pnpm dev` /
|
|
200
|
+
`npm run dev` / `yarn dev` / `bun dev` — match the lockfile). It serves the site
|
|
201
|
+
on localhost with live reload — this is your iteration loop: edit, see the change,
|
|
202
|
+
repeat with the developer. `fimo deploy` is on-demand (hosted preview / shareable
|
|
203
|
+
link / go live), run only on the developer's signal; the dashboard's preview iframe
|
|
204
|
+
shows the **deployed** app, not the localhost dev server.
|
|
205
|
+
|
|
206
|
+
## Known limitations vs. React Router
|
|
207
|
+
|
|
208
|
+
- **Per-route SEO that crawlers see** requires either (a) using React Router
|
|
209
|
+
framework mode with prerender, (b) a Vite SSG plugin like `vite-plugin-ssg`,
|
|
210
|
+
or (c) accepting that the crawler sees the home SEO for every URL.
|
|
211
|
+
- **Dashboard "live edit SEO" preview** requires a reload on plain Vite —
|
|
212
|
+
there's no React-side head-mutation component yet. The static `seo` plugin
|
|
213
|
+
bakes SEO at build time.
|
|
214
|
+
- **Routing**: `FimoProviders` requires a React Router context, so this recipe
|
|
215
|
+
wires `BrowserRouter` from `react-router`. You can swap it for another
|
|
216
|
+
React Router setup (e.g. a `createBrowserRouter` data router with
|
|
217
|
+
`<RouterProvider>`), but a `<Router>` ancestor is mandatory — the runtime's
|
|
218
|
+
preview bridge and pageview tracker call `useLocation`/`useNavigate`.
|
|
219
|
+
|
|
220
|
+
## What to do next
|
|
221
|
+
|
|
222
|
+
- Add a sitemap: pass `fimo({ seo: true, sitemap: true })` and set
|
|
223
|
+
`.fimo/config.json#seo.url` to your production base URL.
|
|
224
|
+
- Look up exports: see `references/ui.md`.
|
|
225
|
+
- Use schema content: see `references/content.md`.
|
|
@@ -1,102 +1,43 @@
|
|
|
1
|
-
# Labels
|
|
1
|
+
# Labels
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> For locale-aware routing and links (`fimoRoutes`, `FimoLink`, custom framework contract), see **`references/locales.md`**.
|
|
3
|
+
Labels are project-wide, locale-scoped, editor-managed static copy. Use them for headings, navigation, CTAs, placeholders, empty states, alt text, and metadata. Structured editor content belongs in schemas instead.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
For label CLI writes and missing-key validation, load `fimo-cli/references/labels.md`.
|
|
7
6
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
Every piece of **user-facing static text** in the app (page titles, button labels, nav items, form placeholders, empty-state copy, alt text, meta tags) MUST be wrapped in `t('key')`.
|
|
11
|
-
|
|
12
|
-
Raw string literals in JSX are a bug — they won't show up in the Fimo admin as editable content and won't be translatable. **Hard-coded JSX text (`<h1>Welcome</h1>`) skips the entire i18n + inline-editing pipeline and becomes invisible to the editor.**
|
|
13
|
-
|
|
14
|
-
## Setup (already wired)
|
|
15
|
-
|
|
16
|
-
Labels are wired automatically by the `fimo/vite` build plugin. `useLabels()` reads DB-backed label values for the active locale — there's no provider to add or remove. The app entry is `src/root.tsx`, which wraps the app in `<FimoProviders>` from `fimo/react-router`. Pages and components call `useLabels()`.
|
|
17
|
-
|
|
18
|
-
`useTranslations()` is kept as a legacy alias for older projects. Use `useLabels()` in new code.
|
|
19
|
-
|
|
20
|
-
In development, the generated app reloads when DB label values change. In production, the build embeds the DB-backed label bundle.
|
|
21
|
-
|
|
22
|
-
## Locale behavior
|
|
23
|
-
|
|
24
|
-
The default label locale is `i18n.defaultLocale` in `.fimo/config.json`, falling back to `en` when it is not set. `fimo validate`, the generated app runtime, and `fimo labels set` without `--locale` all use that default locale.
|
|
25
|
-
|
|
26
|
-
For non-default locales, create explicit DB values with `fimo labels set --locale <locale>` or `fimo labels set-many --locale <locale>`. Keep the code shape the same for every locale: `t('hero.title')` declares the key, and the DB decides which locale value is shown.
|
|
27
|
-
|
|
28
|
-
`.fimo/config.json` owns the locale list:
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"i18n": {
|
|
33
|
-
"defaultLocale": "en",
|
|
34
|
-
"locales": ["en"]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
- `locales` is the enabled locale list and must include `defaultLocale`. Do not add non-default locales unless the user asks for multiple languages, localized routes, translations, or a specific locale.
|
|
40
|
-
- If multiple locales are configured, create or update label values for every configured locale.
|
|
41
|
-
- Public locale routing is project/framework code. In React Router projects, prefer `fimoRoutes()` plus `FimoLink`; for custom frameworks, pass the active locale to `FimoProviders`. See `references/locales.md`.
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
7
|
+
## Stable contract
|
|
44
8
|
|
|
45
9
|
```tsx
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
export function Hero() {
|
|
49
|
-
const { t } = useLabels();
|
|
50
|
-
return (
|
|
51
|
-
<section>
|
|
52
|
-
<h1>
|
|
53
|
-
<Text value={t('hero.title')} />
|
|
54
|
-
</h1>
|
|
55
|
-
<p>
|
|
56
|
-
<Text value={t('hero.subtitle')} />
|
|
57
|
-
</p>
|
|
58
|
-
<input placeholder={String(t('hero.emailPlaceholder'))} />
|
|
59
|
-
<img alt={String(t('hero.imageAlt'))} src="..." />
|
|
60
|
-
</section>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
10
|
+
const { t } = useLabels();
|
|
11
|
+
return <Text value={t('hero.title')} as="h1" />;
|
|
63
12
|
```
|
|
64
13
|
|
|
65
|
-
|
|
66
|
-
- For HTML attributes (`placeholder`, `alt`, `title`, `aria-label`) cast with `String(t(...))` — the attribute can't accept a `FimoString` object directly.
|
|
67
|
-
- If a key is missing from the DB, it renders empty. Run `fimo validate` and add missing values with `fimo labels set`.
|
|
14
|
+
`t(key)` returns a source-tracked `FimoString`. Render it with `<Text>` when possible so Studio can select it. Convert with `String(t(...))` only for HTML attributes or framework APIs that require a primitive.
|
|
68
15
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
**The first argument to `t()` MUST be a string literal.** Dynamic keys break source tracking and fail the lint step.
|
|
16
|
+
The first argument must be a string literal so extraction and source tracking remain deterministic:
|
|
72
17
|
|
|
73
18
|
```tsx
|
|
74
|
-
|
|
75
|
-
t(
|
|
19
|
+
const items = [
|
|
20
|
+
{ href: '/', label: t('nav.home') },
|
|
21
|
+
{ href: '/about', label: t('nav.about') },
|
|
22
|
+
];
|
|
23
|
+
```
|
|
76
24
|
|
|
77
|
-
|
|
78
|
-
t(`nav.${page}`);
|
|
25
|
+
Do not use variables or interpolated expressions as label keys. Namespace literal keys by page/section/component, such as `hero.title`, `nav.signIn`, or `contact.form.submit`.
|
|
79
26
|
|
|
80
|
-
|
|
81
|
-
const items = [{ label: t('nav.home') }, { label: t('nav.about') }];
|
|
82
|
-
items.map((item) => <li>{item.label}</li>);
|
|
83
|
-
```
|
|
27
|
+
Missing keys render as an empty tracked value. Run `fimo validate`, then create values with `fimo labels set` or `set-many`; do not add fallback copy to code.
|
|
84
28
|
|
|
85
|
-
##
|
|
29
|
+
## Framework adapters
|
|
86
30
|
|
|
87
|
-
|
|
88
|
-
- Namespace by **page/section/component**, not by feature (`contact.form.submitLabel`, not `forms.contactSubmit`).
|
|
89
|
-
- Values live in the DB. Do not put user-visible fallback copy in code.
|
|
31
|
+
The label contract is shared; acquisition differs by runtime:
|
|
90
32
|
|
|
91
|
-
|
|
33
|
+
- Next server: `getLabels({ locale? })` from `fimo/next`.
|
|
34
|
+
- Next client: `useLabels()` from `fimo/next/client`, under `FimoProvider`.
|
|
35
|
+
- React Router/Vite client: `useLabels()` from `fimo/ui`, under the existing framework integration.
|
|
92
36
|
|
|
93
|
-
|
|
94
|
-
- Content-type field values rendered via `fimo/ui` primitives (`<Text>`, `<RichText>`, …) — those carry their own admin-editing metadata.
|
|
95
|
-
- Purely decorative / non-text (icons, logos).
|
|
96
|
-
- Developer-only strings (`console.log`, error messages thrown for debugging).
|
|
37
|
+
Load `references/frameworks/next.md`, `references/frameworks/react-router.md`, or `references/frameworks/vite-react.md` before adding a provider.
|
|
97
38
|
|
|
98
|
-
##
|
|
39
|
+
## Locale behavior
|
|
99
40
|
|
|
100
|
-
|
|
41
|
+
`.fimo/config.json#i18n.defaultLocale` selects the default (fallback `en`). The application resolves the active locale and gives it to the framework adapter. Create explicit label values for each configured locale the product supports.
|
|
101
42
|
|
|
102
|
-
|
|
43
|
+
Do not translate schema content, dynamic form submissions, decorative icons, or developer-only logs through labels.
|
|
@@ -1,129 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Locale ownership
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The application owns locale resolution and routing. Fimo consumes the resolved locale for labels and localized content.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`.fimo/config.json` declares supported/default locales:
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```jsonc
|
|
7
|
+
```json
|
|
10
8
|
{
|
|
11
9
|
"i18n": {
|
|
12
10
|
"defaultLocale": "en",
|
|
13
|
-
"locales": ["en"]
|
|
14
|
-
"routing": {
|
|
15
|
-
"strategy": "path-prefix", // only supported strategy: /es/about for Spanish; default locale can stay /about
|
|
16
|
-
"prefixDefaultLocale": false, // when false, omit /en for the default locale
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
- `defaultLocale` is the source/default locale.
|
|
23
|
-
- `locales` lists enabled locales and must include `defaultLocale`. Do not add non-default locales unless the user asks for multiple languages, localized routes, translations, or a specific locale.
|
|
24
|
-
- `routing.strategy` currently supports `path-prefix`.
|
|
25
|
-
- If multiple locales are configured, create or update labels and localized content for every configured locale. Create schemas with `i18n: { "enabled": false }` only once; those entries are shared across locales.
|
|
26
|
-
|
|
27
|
-
Locale tags should be structurally valid, for example `en`, `es`, `pt-BR`, or `zh-Hans`.
|
|
28
|
-
|
|
29
|
-
## Canonical Routes
|
|
30
|
-
|
|
31
|
-
Keep `.fimo/config.json#routes` canonical and unlocalized:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{
|
|
35
|
-
"routes": {
|
|
36
|
-
"pricing": {
|
|
37
|
-
"label": "Pricing",
|
|
38
|
-
"path": "/pricing"
|
|
39
|
-
}
|
|
11
|
+
"locales": ["en", "fr"]
|
|
40
12
|
}
|
|
41
13
|
}
|
|
42
14
|
```
|
|
43
15
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## React Router Standard Helper
|
|
47
|
-
|
|
48
|
-
For React Router framework mode, write one canonical route list and wrap it:
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
import type { RouteConfig } from '@react-router/dev/routes';
|
|
52
|
-
import { index, route } from '@react-router/dev/routes';
|
|
53
|
-
import { fimoRoutes } from 'fimo/react-router/routes';
|
|
54
|
-
|
|
55
|
-
const routes = [
|
|
56
|
-
index('./pages/Index.tsx', { id: 'home' }),
|
|
57
|
-
route('pricing', './pages/Pricing.tsx', { id: 'pricing' }),
|
|
58
|
-
route('blog/:slug', './pages/BlogPost.tsx', { id: 'blog-post' }),
|
|
59
|
-
] satisfies RouteConfig;
|
|
60
|
-
|
|
61
|
-
export default fimoRoutes(routes) satisfies RouteConfig;
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
`fimoRoutes()` expands concrete locale-prefixed aliases from the configured locale list while reusing the same page modules. For `locales: ["en", "es"]` and `prefixDefaultLocale: false`, it adds routes like `/es`, `/es/pricing`, and `/es/blog/:slug`.
|
|
65
|
-
|
|
66
|
-
This helper exists so the Fimo UI route selector, preview navigation, labels, and content queries all agree on the same URL shape. It is not required if the project deliberately implements a custom routing strategy.
|
|
67
|
-
|
|
68
|
-
Copyable examples live in `assets/react-router-locales/routes.ts` and `assets/react-router-locales/navigation.tsx`.
|
|
69
|
-
|
|
70
|
-
## Existing Custom Locale Systems
|
|
71
|
-
|
|
72
|
-
Before changing a multilingual app, inspect its locale provider, route table, language switcher, redirects, and current public URLs. Preserve that behavior while migrating.
|
|
73
|
-
|
|
74
|
-
For React Router projects, prefer converging on one locale contract owned by `.fimo/config.json`, `fimoRoutes()`, and `FimoLink`. Move existing providers and language-switcher UI into the framework root instead of replacing them or adding a second locale context. Keep route metadata canonical and reuse page modules across locales.
|
|
75
|
-
|
|
76
|
-
If adopting the standard helper would change established public URLs, do not silently rewrite them. Keep the custom router temporarily, pass its resolved locale to `<FimoProviders locale={locale}>`, and ask the user to choose the canonical URLs and redirects before normalizing the route table.
|
|
77
|
-
|
|
78
|
-
## Links
|
|
79
|
-
|
|
80
|
-
Use `FimoLink` for internal links that should preserve or switch locale:
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
import { FimoLink } from 'fimo/react-router';
|
|
84
|
-
|
|
85
|
-
<FimoLink to="/pricing">Pricing</FimoLink>
|
|
86
|
-
<FimoLink to="/pricing" locale="es">Spanish pricing</FimoLink>
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
`FimoLink` localizes absolute internal paths according to `.fimo/config.json#i18n.routing`. It leaves relative, hash, query-only, and external URLs alone. Use React Router's raw `Link` only when you intentionally want to bypass Fimo's locale behavior.
|
|
90
|
-
|
|
91
|
-
Read the active locale with `useLocale()` when component logic needs it:
|
|
16
|
+
Keep one locale unless the product asks for multilingual output. Locale tags should be structurally valid, such as `en`, `pt-BR`, or `zh-Hans`.
|
|
92
17
|
|
|
93
|
-
|
|
94
|
-
import { useLocale } from 'fimo/ui';
|
|
18
|
+
For an already linked project, the same locales must also be enabled in Fimo before writing non-default label or content variants. Local config and `fimo validate` do not currently provision project locales. If the API says a locale is not configured, enable it in Fimo Studio; do not change the application's route strategy to work around it.
|
|
95
19
|
|
|
96
|
-
|
|
97
|
-
```
|
|
20
|
+
## Application responsibilities
|
|
98
21
|
|
|
99
|
-
|
|
22
|
+
- Resolve locale from its router, route parameters, domain, request headers, middleware, cookie, existing i18n library, or user preference.
|
|
23
|
+
- Keep its existing public URLs, redirects, links, `<html lang/dir>`, and SEO behavior.
|
|
24
|
+
- Pass the resolved locale to Fimo's server reads and client provider.
|
|
25
|
+
- Treat any optional Studio route catalog as framework-adapter-specific; load the framework reference before creating or changing one.
|
|
100
26
|
|
|
101
|
-
|
|
102
|
-
const { locale, locales, routePath } = useLocale();
|
|
103
|
-
|
|
104
|
-
locales.map((code) => (
|
|
105
|
-
<FimoLink key={code} to={routePath} locale={code} aria-current={code === locale ? 'true' : undefined}>
|
|
106
|
-
{new Intl.DisplayNames([code], { type: 'language' }).of(code)}
|
|
107
|
-
</FimoLink>
|
|
108
|
-
));
|
|
109
|
-
```
|
|
27
|
+
## Fimo responsibilities
|
|
110
28
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- The framework owns URL matching.
|
|
116
|
-
- The app resolves the active locale from the request, path, domain, middleware, cookie, or user preference.
|
|
117
|
-
- The app passes that locale to Fimo:
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
<FimoProviders locale={locale}>{children}</FimoProviders>
|
|
121
|
-
```
|
|
29
|
+
- Fetch the correct localized content variant and label dictionary after receiving locale.
|
|
30
|
+
- Use `defaultLocale` when no explicit locale is supplied.
|
|
31
|
+
- Preserve one shared entry for schemas with `i18n.enabled: false`.
|
|
32
|
+
- Preserve source metadata for editing.
|
|
122
33
|
|
|
123
|
-
|
|
34
|
+
## Framework handoff
|
|
124
35
|
|
|
125
|
-
|
|
36
|
+
- Next.js → load `references/frameworks/next/locales.md`.
|
|
37
|
+
- React Router standard locale routing → load `references/frameworks/react-router.md`.
|
|
38
|
+
- Vite + React/custom React router → load `references/frameworks/vite-react.md` and pass locale through its existing provider seam.
|
|
126
39
|
|
|
127
|
-
|
|
128
|
-
- Do not duplicate every route in `.fimo/config.json` per locale.
|
|
129
|
-
- Do not use `/es` as a normal canonical route when `es` is an enabled locale.
|
|
40
|
+
Do not replace an existing locale system merely to add Fimo. Do not duplicate localized routes in the Studio catalog. Do not assume Fimo's route helpers are universal framework APIs.
|