livelocale 0.12.1-next.17 → 1.0.0
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/README.md +55 -974
- package/dist/client/index.cjs +220 -14
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +93 -6
- package/dist/client/index.d.ts +93 -6
- package/dist/client/index.js +220 -14
- package/dist/client/index.js.map +1 -1
- package/dist/codemod/index.cjs +583 -441
- package/dist/codemod/index.cjs.map +1 -1
- package/dist/codemod/index.js +583 -441
- package/dist/codemod/index.js.map +1 -1
- package/dist/react/index.cjs +229 -18
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +229 -18
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +73 -8
- package/dist/server/index.d.ts +73 -8
- package/dist/server/index.js.map +1 -1
- package/dist/studio/assets/index-CwvUCfuq.js +14 -0
- package/dist/studio/assets/index-DrqnTE2K.css +1 -0
- package/dist/studio/index.html +2 -2
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.d.cts +5 -19
- package/dist/vite/index.d.ts +5 -19
- package/dist/vite/index.js.map +1 -1
- package/dist/{wrap-2qVxLN9Q.d.cts → wrap-BbTu7D0Q.d.cts} +24 -5
- package/dist/{wrap-2qVxLN9Q.d.ts → wrap-BbTu7D0Q.d.ts} +24 -5
- package/package.json +15 -8
- package/dist/studio/assets/index-CcIiR_d-.js +0 -14
- package/dist/studio/assets/index-KRNgb_rB.css +0 -1
package/README.md
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
<img src="brand/logo.svg" alt="livelocale" height="64">
|
|
6
6
|
</picture>
|
|
7
7
|
</h1>
|
|
8
|
-
<p><strong>
|
|
9
|
-
<p>
|
|
8
|
+
<p><strong>The editing and review layer on top of next-intl or react-i18next.</strong></p>
|
|
9
|
+
<p>Your content people fix text on the real site, and you get each fix as an edit to your JSON locale files in git, or as a pull request. For Next.js and React + Vite: no account, no server, no browser extension.</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://github.com/Araby99/livelocale/actions/workflows/ci.yml"><img src="https://github.com/Araby99/livelocale/actions/workflows/ci.yml/badge.svg" alt="CI Status" /></a>
|
|
12
|
+
<a href="https://github.com/Araby99/livelocale/actions/workflows/ci.yml?query=branch%3Amain"><img src="https://github.com/Araby99/livelocale/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI Status" /></a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/livelocale"><img src="https://img.shields.io/npm/v/livelocale.svg?style=flat-square" alt="NPM Version" /></a>
|
|
14
14
|
<a href="https://www.npmjs.com/package/livelocale"><img src="https://img.shields.io/npm/dm/livelocale.svg?style=flat-square" alt="NPM Downloads" /></a>
|
|
15
15
|
<a href="https://github.com/Araby99/livelocale/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/livelocale.svg?style=flat-square" alt="License" /></a>
|
|
@@ -18,53 +18,36 @@
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**It is not an i18n library.** next-intl or react-i18next keeps doing routing, locale detection,
|
|
22
|
+
date and number formatting and message rendering; livelocale only edits and reviews the text.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
- **Freelancing?** Your client fixes their own copy through a
|
|
25
|
+
[share link](https://livelocale.js.org/docs/guides/share-links.html), and you review a pull
|
|
26
|
+
request instead of collecting screenshots.
|
|
27
|
+
- **Only want CI?** [`npx livelocale check --hardcoded`](https://livelocale.js.org/docs/guides/ci-check.html)
|
|
28
|
+
works without the Studio. Translating on your own? The CI check alone catches missing keys and
|
|
29
|
+
hard-coded text.
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
Run your app beside the livelocale Studio, hold Alt and click a line of text, and fix it. The save
|
|
32
|
+
goes straight into your locale JSON file as an ordinary edit you commit. In development every
|
|
33
|
+
translated string carries an invisible watermark, so the Studio knows which key you clicked; in
|
|
34
|
+
production nothing is added. Share links let someone without a checkout fix text on your deployed
|
|
35
|
+
site and send the change back as a pull request.
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## ✨ Features
|
|
33
|
-
|
|
34
|
-
- 🎯 **In-Context Visual Editing:** Hover over any translated text to highlight it; click to instantly edit in the visual panel.
|
|
35
|
-
- 🔴 **Missing Key Indicators:** Identifies translation keys that are missing in the active locale but exist in the codebase or other translation files, highlighting them with red indicators.
|
|
36
|
-
- 🔄 **Smart Fallback Placeholders:** Automatically renders translation placeholders from alternative languages for missing keys in development, keeping them visible and fully click-to-edit inline.
|
|
37
|
-
- 📁 **Namespaced Folder Support:** Handles nested multi-file folder layouts (e.g., `locales/en/auth.json`) natively, merging files during load and separating them during mutation.
|
|
38
|
-
- 🧼 **Input & Form Sanitization:** Patches controlled inputs and intercepts clipboard actions to strip watermarks in development automatically.
|
|
39
|
-
- ⚡ **Atomic File Operations:** Prevents Next.js hot module replacement (HMR) reading corrupt half-written files via atomic temp-to-final writes.
|
|
40
|
-
- 🔒 **Secure Local Boundaries:** Restricts directory traversal, verifies origins, and supports deep schemas up to 30 levels of recursion.
|
|
41
|
-
- 🤖 **CLI Migration Tool:** Automatically parses and wraps standard `react-i18next` hooks with a single command.
|
|
42
|
-
- 🔢 **Plural forms editor:** ICU `plural` / `select` messages are edited one branch at a time, with the categories your locale needs (Arabic gets all six) pre-filled from CLDR rules.
|
|
43
|
-
- 🧭 **Namespace-aware key scanning:** `useTranslations('home')`, `getTranslations`, `useTranslation('auth')`, and the wrap helpers are understood, so the Locale Directory shows `home.title`, not a false "missing" `title`.
|
|
44
|
-
- 🧩 **Placeholder safety:** Saves that drop or invent a `{placeholder}`, `{{interpolation}}`, `%s`, or `<tag>` are held back with a clear warning, and broken ICU plural/select syntax is rejected, unless you choose Save anyway.
|
|
45
|
-
- 🌍 **Preview mode for non-developers:** Run a token-protected Studio against a preview deployment; every save becomes a commit on a GitHub branch and one click opens the pull request.
|
|
46
|
-
- 🧾 **Review, commit, undo:** The Studio's Changes panel shows git status and a diff of your locale files, commits them with a message, discards them, and undoes any edit from the session.
|
|
47
|
-
- 🩺 **Doctor:** `npx livelocale doctor` finds the usual integration mistakes before you open the Studio.
|
|
48
|
-
- ✅ **Translation check for CI:** `npx livelocale check` fails the build on missing keys, placeholder drift between locales, and broken ICU syntax, and lists keys nothing uses, and, with `--hardcoded`, text that never went through translation.
|
|
49
|
-
- 🛑 **Safety switches:** `LIVELOCALE=off` kill switch, and automatic shut-off on hosting platforms so a staging build can never leak watermarks.
|
|
50
|
-
- 🧭 **App Router and Pages Router:** One handler works with both `route.ts` and `pages/api` signatures.
|
|
51
|
-
- 🌐 **Works beyond localhost:3000:** Point the Studio at any port or hostname with `--app` or `?app=`, and allow Codespaces or custom origins with one env var.
|
|
52
|
-
|
|
53
|
-
---
|
|
37
|
+
**Docs: [livelocale.js.org/docs](https://livelocale.js.org/docs/)**
|
|
54
38
|
|
|
55
|
-
##
|
|
56
|
-
|
|
57
|
-
### 1. Install Dependency
|
|
39
|
+
## Install
|
|
58
40
|
|
|
59
41
|
```bash
|
|
60
42
|
npm install livelocale
|
|
61
43
|
```
|
|
62
44
|
|
|
63
|
-
|
|
45
|
+
It is a dependency, not a dev dependency: your app imports `livelocale/react` and
|
|
46
|
+
`livelocale/server` at run time. livelocale needs Node.js 18 or later.
|
|
64
47
|
|
|
65
|
-
|
|
48
|
+
## 30 seconds with Next.js
|
|
66
49
|
|
|
67
|
-
|
|
50
|
+
Render the provider once in your root layout:
|
|
68
51
|
|
|
69
52
|
```tsx
|
|
70
53
|
// app/layout.tsx
|
|
@@ -82,969 +65,67 @@ export default function RootLayout({ children }) {
|
|
|
82
65
|
}
|
|
83
66
|
```
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
### 3. Initialize Server Mutation Handler
|
|
88
|
-
|
|
89
|
-
Initialize the server-side API endpoints automatically by running:
|
|
68
|
+
Let `init` write the API route the Studio saves through, then load your strings with
|
|
69
|
+
`createTranslations`:
|
|
90
70
|
|
|
91
71
|
```bash
|
|
92
72
|
npx livelocale init
|
|
93
73
|
```
|
|
94
74
|
|
|
95
|
-
_The CLI will check for TypeScript/JavaScript and write the appropriate mutation file (`/app/api/livelocale/route.ts` or `/pages/api/livelocale.ts`) directly into your project._
|
|
96
|
-
|
|
97
|
-
> [!NOTE]
|
|
98
|
-
> For manual setup, create `app/api/livelocale/route.ts` with:
|
|
99
|
-
>
|
|
100
|
-
> ```typescript
|
|
101
|
-
> import { createLiveLocaleHandler } from 'livelocale/server';
|
|
102
|
-
> import * as path from 'path';
|
|
103
|
-
>
|
|
104
|
-
> const handler = createLiveLocaleHandler({
|
|
105
|
-
> localesPath: path.resolve('./locales'),
|
|
106
|
-
> });
|
|
107
|
-
>
|
|
108
|
-
> export const GET = handler;
|
|
109
|
-
> export const POST = handler;
|
|
110
|
-
> export const OPTIONS = handler;
|
|
111
|
-
> ```
|
|
112
|
-
>
|
|
113
|
-
> For the **Pages Router**, create `pages/api/livelocale.ts` instead and default-export the
|
|
114
|
-
> same handler. It accepts the `(req, res)` signature automatically:
|
|
115
|
-
>
|
|
116
|
-
> ```typescript
|
|
117
|
-
> import { createLiveLocaleHandler } from 'livelocale/server';
|
|
118
|
-
> import * as path from 'path';
|
|
119
|
-
>
|
|
120
|
-
> export default createLiveLocaleHandler({ localesPath: path.resolve('./locales') });
|
|
121
|
-
> ```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
### 4. Enable Key Watermarking
|
|
126
|
-
|
|
127
|
-
Wrap translation lookups to inject dev-only watermarks.
|
|
128
|
-
|
|
129
|
-
#### Option A: Server Components (Direct Loader)
|
|
130
|
-
|
|
131
|
-
Use `createTranslations` to load flat JSON files or directory-based namespaces:
|
|
132
|
-
|
|
133
75
|
```tsx
|
|
76
|
+
// app/page.tsx
|
|
134
77
|
import { createTranslations } from 'livelocale/server';
|
|
135
78
|
|
|
136
79
|
export default async function Page({ searchParams }) {
|
|
137
80
|
const { locale = 'en' } = await searchParams;
|
|
138
81
|
const t = createTranslations(locale, {
|
|
139
|
-
supportedLocales: ['en', 'ar'
|
|
82
|
+
supportedLocales: ['en', 'ar'],
|
|
140
83
|
localesDir: './locales',
|
|
141
84
|
});
|
|
142
85
|
|
|
143
|
-
return <h1>{t.home?.
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
#### Option B: Client-side Hooks (react-i18next or next-intl)
|
|
148
|
-
|
|
149
|
-
Wrap hook return values with `wrapTranslationEngine`:
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
'use client';
|
|
153
|
-
import { useTranslation } from 'react-i18next';
|
|
154
|
-
import { wrapTranslationEngine } from 'livelocale/client';
|
|
155
|
-
|
|
156
|
-
export default function Page() {
|
|
157
|
-
const { t: rawT } = useTranslation('auth');
|
|
158
|
-
const t = wrapTranslationEngine(rawT, { keyPrefix: 'auth' });
|
|
159
|
-
|
|
160
|
-
return <h1>{t('login.title')}</h1>;
|
|
86
|
+
return <h1>{t.home?.title}</h1>;
|
|
161
87
|
}
|
|
162
88
|
```
|
|
163
89
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
### 5. Launch the Visual Studio
|
|
167
|
-
|
|
168
|
-
Boot the editing studio next to your local development server:
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
npx livelocale studio --port 3010 --app http://localhost:3000
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Open `http://localhost:3010` to view the Studio, loaded with your app. `--app` defaults to
|
|
175
|
-
`http://localhost:3000`; you can also switch apps without restarting by opening
|
|
176
|
-
`http://localhost:3010/?app=http://localhost:3001`.
|
|
177
|
-
|
|
178
|
-
The preview bar's address field shows the page you are on. Type a path and press Enter to open it
|
|
179
|
-
in the current language. Suggestions list your recent pages and, on a share link, every page of the
|
|
180
|
-
site. Reload reloads the page you are on.
|
|
181
|
-
|
|
182
|
-
The Studio has the new LiveLocale look, in light and dark. It speaks English and Arabic; the share
|
|
183
|
-
link follows the browser language, and the switch is in the header. Share links also follow your
|
|
184
|
-
system's light or dark setting; the dev Studio starts dark until you pick System, Light or Dark.
|
|
185
|
-
Its fonts ship inside the package, so the Studio loads nothing from Google. Every control works by
|
|
186
|
-
keyboard, with a visible focus ring.
|
|
187
|
-
|
|
188
|
-
The look is the brand's "hunk" palette: amber fills for the patch, the primary button and the
|
|
189
|
-
edited row, with Martian Mono and IBM Plex type. The bundled fonts (Martian Mono, IBM Plex Sans,
|
|
190
|
-
IBM Plex Mono and IBM Plex Sans Arabic) are under the SIL Open Font License 1.1; each family's
|
|
191
|
-
licence ships in `dist/studio/fonts-licenses/`.
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
### 6. Check your setup
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
npx livelocale doctor --app http://localhost:3000
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
The doctor verifies the route handler, the provider mount, middleware matchers, frame-blocking
|
|
202
|
-
headers, and locale JSON, then probes the running app. It exits non-zero on blocking problems, so
|
|
203
|
-
it can also run in CI.
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## 🛠️ CLI Reference
|
|
208
|
-
|
|
209
|
-
### `init`
|
|
210
|
-
|
|
211
|
-
Generates API route handlers based on your router structure (App vs Pages Router) and configuration language (TypeScript vs JavaScript).
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
npx livelocale init [--dir <path>]
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### `init --share`
|
|
218
|
-
|
|
219
|
-
Sets up the share link for your stack and, on a GitHub remote, writes
|
|
220
|
-
`.github/workflows/livelocale.yml`, which turns change links into pull requests. It prints every
|
|
221
|
-
file it creates, updates, leaves unchanged or skips, then what is left for you to do.
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
npx livelocale init --share [--locales <path>] [--source <locale>] [--branches <a,b>] [--overwrite] [--dir <path>]
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
| Stack | Build change |
|
|
228
|
-
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
229
|
-
| CRA / webpack / Rsbuild | `"build": "react-scripts build && livelocale pack build --locales <path> --source <locale>"` on CRA; webpack and Rsbuild pack `dist` instead of `build`. This is an explicit `&&`, not a `postbuild` hook, because Yarn Berry and some pnpm versions skip pre/post scripts, and hosts that call the bundler directly skip them too ([yarn](https://v2.yarnpkg.com/advanced/lifecycle-scripts), [pnpm](https://pnpm.io/cli/run)). |
|
|
230
|
-
| Next.js `output: 'export'` | `"build:preview": "livelocale build --preview -- next build"`, deployed to a staging location, because the HTML is pre-rendered and must be watermarked at build time. |
|
|
231
|
-
| Next.js on Vercel/Node | For the Preview environment, use the build command `livelocale build --preview -- next build`. It packs into a gitignored `public/livelocale/`. |
|
|
232
|
-
| Custom CI | `LIVELOCALE=preview <your build>` (only for SSR/SSG), then `npx livelocale pack <outDir>`. |
|
|
233
|
-
|
|
234
|
-
For Vite it adds `share: true` to your `liveLocaleVite({ ... })` call. The scripts it writes also
|
|
235
|
-
pass `--locales` and `--source`; webpack and Rsbuild pack `dist`.
|
|
236
|
-
|
|
237
|
-
- `--locales` — locales directory relative to the project (default: the first of `locales`,
|
|
238
|
-
`src/locales`, `public/locales`, `src/i18n/locales` and `messages` that holds locale JSON).
|
|
239
|
-
- `--source` — source locale (default `en`).
|
|
240
|
-
- `--branches` — branches a change link may target (default: the repository's default branch, at
|
|
241
|
-
most 10). Merge the workflow into each of them too: a pull request into a branch runs that
|
|
242
|
-
branch's copy of the workflow.
|
|
243
|
-
- `--overwrite` — replace an existing workflow file whose content differs (it is skipped otherwise).
|
|
244
|
-
- On another git host no workflow is written; use `npx livelocale apply "<link>" --pr`.
|
|
245
|
-
|
|
246
|
-
Commit and push the result: the workflow must be on the default branch.
|
|
247
|
-
|
|
248
|
-
### `studio`
|
|
249
|
-
|
|
250
|
-
Spins up a lightweight, CORS-enabled HTTP server serving the Visual Studio assets statically.
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
npx livelocale studio [--port <number>] [--app <origin>]
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
- `--port` — port for the Studio itself (default `3010`).
|
|
257
|
-
- `--app` — origin of the app being edited (default `http://localhost:3000`).
|
|
258
|
-
|
|
259
|
-
### `doctor`
|
|
260
|
-
|
|
261
|
-
Inspects the project for the integration problems the Studio cannot recover from, and optionally
|
|
262
|
-
probes the running app.
|
|
263
|
-
|
|
264
|
-
```bash
|
|
265
|
-
npx livelocale doctor [--dir <path>] [--locales <path>] [--app <url>] [--share] [--token <token>]
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
- `--locales` — locales directory relative to the project (default `locales`).
|
|
269
|
-
- `--app` — also check that the mutation API answers and that pages are embeddable. A path is
|
|
270
|
-
the app base: `--app https://user.github.io/repo/` probes a GitHub Pages project site (a
|
|
271
|
-
trailing `index.html` is dropped).
|
|
272
|
-
- `--share` — run the share link probes even when `<base>livelocale/manifest.json` does not
|
|
273
|
-
answer. They also run on their own whenever the manifest answers with JSON. They check the
|
|
274
|
-
Studio page, the manifest, `flag.js` and its tag first in `<head>`, frame headers
|
|
275
|
-
(`X-Frame-Options: DENY` or `frame-ancestors 'none'` breaks the Studio; `SAMEORIGIN` is fine),
|
|
276
|
-
`script-src`, and service workers, then print `🔗 Share link: <base>livelocale/index.html`.
|
|
277
|
-
When a Workbox service worker (vite-plugin-pwa, workbox-webpack-plugin) would answer the Studio
|
|
278
|
-
with your app shell, add this line to its Workbox options:
|
|
279
|
-
`navigateFallbackDenylist: [/^\/livelocale\//]`.
|
|
280
|
-
- `--token` — access token for a preview deployment (defaults to `LIVELOCALE_TOKEN`); with it the
|
|
281
|
-
doctor also reports the server mode and repository access problems.
|
|
282
|
-
|
|
283
|
-
### `check`
|
|
284
|
-
|
|
285
|
-
A read-only consistency check of your translations, built for CI. It runs in any `NODE_ENV`.
|
|
90
|
+
Start the app, and the Studio in a second terminal:
|
|
286
91
|
|
|
287
92
|
```bash
|
|
288
|
-
npx
|
|
93
|
+
npx next dev
|
|
94
|
+
npx livelocale studio
|
|
289
95
|
```
|
|
290
96
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
| Locale file is not valid JSON | problem |
|
|
294
|
-
| Broken ICU syntax (unbalanced braces, `plural` without `other`) | problem |
|
|
295
|
-
| Key used in code but missing from a locale | problem |
|
|
296
|
-
| Key in the source locale but missing from another locale | problem |
|
|
297
|
-
| Placeholders differ from the source locale (`{name}`, `{{count}}`, `%s`, `<b>`) | problem |
|
|
298
|
-
| Empty value | warning |
|
|
299
|
-
| Key never referenced in code | warning |
|
|
300
|
-
|
|
301
|
-
- `--source` — locale the others are compared against (default `en`, else the first locale).
|
|
302
|
-
- `--no-unused` — skip the unused-key report. Keys built at run time (``t(`status.${s}`)``) cannot be
|
|
303
|
-
detected, so review this list before deleting anything.
|
|
304
|
-
- `--strict` — treat warnings as problems.
|
|
305
|
-
- `--json` — print the full report for other tools.
|
|
306
|
-
|
|
307
|
-
The command exits with code 1 when there are problems. Code references resolve namespaces the same
|
|
308
|
-
way the Studio does, an object access such as `t.home` covers every key under `home`, and i18next
|
|
309
|
-
plural families (`items_one`, `items_other`, …) count as one key, so Arabic's six forms against
|
|
310
|
-
English's two are not reported.
|
|
311
|
-
|
|
312
|
-
```yaml
|
|
313
|
-
# .github/workflows/i18n.yml
|
|
314
|
-
- run: npx livelocale check
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### Catch untranslated text before launch
|
|
318
|
-
|
|
319
|
-
`--hardcoded` adds three findings to `check`:
|
|
320
|
-
|
|
321
|
-
| Finding | Level |
|
|
322
|
-
| ----------------------------------------------------------------------------------------- | ------- |
|
|
323
|
-
| `hardcoded`: JSX text or a user-facing attribute that never went through translation | problem |
|
|
324
|
-
| `untranslated`: a translation identical to the source locale | warning |
|
|
325
|
-
| `wrong-script`: a translation mostly in Latin letters for a non-Latin locale (`ar`, `ja`) | warning |
|
|
326
|
-
|
|
327
|
-
Hardcoded text always fails the run. The other two are warnings and fail only with `--strict`.
|
|
328
|
-
Text is read from JSX children and from the attributes a user reads or hears (`alt`, `title`,
|
|
329
|
-
`placeholder`, `aria-label`, `label` and the other `aria-` text attributes); each finding prints
|
|
330
|
-
its file, line and column, and the source text shortened to 60 characters. Translation values are
|
|
331
|
-
never printed.
|
|
332
|
-
|
|
333
|
-
```bash
|
|
334
|
-
npx livelocale check --hardcoded
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
```yaml
|
|
338
|
-
# .github/workflows/i18n.yml
|
|
339
|
-
- run: npx livelocale check --hardcoded
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Without `--hardcoded`, `check` behaves as before, so an upgrade does not break an existing pipeline.
|
|
343
|
-
|
|
344
|
-
**Opting out.** Put `// livelocale-ignore-next-line` (or `{/* livelocale-ignore-next-line */}`
|
|
345
|
-
inside JSX) either on the line above the text or on the line above the element that holds it,
|
|
346
|
-
which is where Prettier leaves it for multi-line JSX:
|
|
347
|
-
|
|
348
|
-
```tsx
|
|
349
|
-
// livelocale-ignore-next-line
|
|
350
|
-
const label = <span>LiveLocale</span>;
|
|
351
|
-
|
|
352
|
-
<footer>
|
|
353
|
-
{/* livelocale-ignore-next-line */}
|
|
354
|
-
<img
|
|
355
|
-
src="/brand/livelocale-logo.svg"
|
|
356
|
-
alt="LiveLocale logo"
|
|
357
|
-
width={120}
|
|
358
|
-
height={32}
|
|
359
|
-
className="logo"
|
|
360
|
-
/>
|
|
361
|
-
</footer>;
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
A `// livelocale-ignore-file` comment at the top of a file skips the whole file. Brand names and
|
|
365
|
-
other words that are never translated go in a `livelocale.allow` file at the project root, one per
|
|
366
|
-
line; a `key:` line exempts a translation key from `untranslated` and `wrong-script`, for a value
|
|
367
|
-
that is the same in every language:
|
|
368
|
-
|
|
369
|
-
```text
|
|
370
|
-
# livelocale.allow
|
|
371
|
-
LiveLocale
|
|
372
|
-
GitHub
|
|
373
|
-
key:checkout.brand
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
`--allow <word>` adds a word or phrase for one run, and can be repeated. Allowed words are matched
|
|
377
|
-
as whole words, case-sensitively.
|
|
378
|
-
|
|
379
|
-
**What is skipped.** Test and story files (`*.test.*`, `*.spec.*`, `*.stories.*`, `*.cy.*`,
|
|
380
|
-
`*.e2e.*`), the folders `__tests__`, `__mocks__`, `__fixtures__`, `fixtures`, `.storybook`, `e2e`
|
|
381
|
-
and `cypress`, `.d.ts` files, folders the key scanner ignores (`node_modules`, build output) and
|
|
382
|
-
paths in `.gitignore`. Code-like strings are not reported: URLs, emails, paths, ids, `kebab-case`,
|
|
383
|
-
`snake_case`, `camelCase` and `CONSTANT` tokens, dotted keys, and shell commands. Elements such as
|
|
384
|
-
`<code>`, `<pre>`, `<kbd>`, `<script>`, `<style>` and `<Trans>` are not looked into.
|
|
385
|
-
|
|
386
|
-
**Limits.** Only JSX is checked: Vue, Svelte, Angular and HTML templates are not, and neither are
|
|
387
|
-
object literals such as page metadata. And text built at run time is not seen: a template string
|
|
388
|
-
with substitutions, a concatenation or a value from an API.
|
|
389
|
-
|
|
390
|
-
**Requirement.** The JSX scan uses your project's own `typescript`; without it the other checks
|
|
391
|
-
still run and the command exits with code 2. Install it with:
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
npm install -D typescript
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
### `pack`
|
|
398
|
-
|
|
399
|
-
Turns a finished static build into a share link: it writes the Studio, `manifest.json`, the locale
|
|
400
|
-
files and `flag.js` into `<outDir>/livelocale/` and links `flag.js` from every HTML page. Run it
|
|
401
|
-
after your build, for example `"build": "vite build && livelocale pack dist"`.
|
|
402
|
-
|
|
403
|
-
```bash
|
|
404
|
-
npx livelocale pack <outDir> [--dir <path>] [--locales <path>] [--source <locale>] [--base <path>] [--expose-repo] [--no-inject]
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
- `<outDir>` — the built site folder, relative to `--dir` (for example `dist`, `build` or `out`).
|
|
408
|
-
- `--locales` — locales directory relative to the project (default `locales`).
|
|
409
|
-
- `--source` — source locale (default `en`, else the first locale).
|
|
410
|
-
- `--base` — site path the app is served from, such as `/repo/` (default: detected from the
|
|
411
|
-
project, else `/`). A relative base (`./`) prints a warning; pass an absolute one if the app has
|
|
412
|
-
client-side routes.
|
|
413
|
-
- `--expose-repo` — put the GitHub `owner/repo` into `livelocale/manifest.json` (off by default).
|
|
414
|
-
- `--no-inject` — do not add the `flag.js` script to HTML files. Next.js outputs are never injected.
|
|
415
|
-
|
|
416
|
-
The share link is `<your site><base>livelocale/index.html`. Nothing is written outside
|
|
417
|
-
`<outDir>/livelocale/` except that one script tag, and an existing `livelocale/` folder is replaced
|
|
418
|
-
only when pack wrote it.
|
|
419
|
-
|
|
420
|
-
`manifest.json` now lists the site's HTML pages (`pages`, at most 2,000) for the Studio's
|
|
421
|
-
suggestions.
|
|
422
|
-
|
|
423
|
-
### `build`
|
|
424
|
-
|
|
425
|
-
Runs your build command and packs its output in one step. The environment variables work the same
|
|
426
|
-
on Windows, macOS and Linux, so no `cross-env` is needed.
|
|
427
|
-
|
|
428
|
-
```bash
|
|
429
|
-
npx livelocale build [--preview] [--out <dir>] [pack options] -- <your build command>
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
- `--preview` — run the command with `LIVELOCALE=preview` and `NEXT_PUBLIC_LIVELOCALE=preview`, so
|
|
433
|
-
server-rendered and pre-rendered pages carry watermarks.
|
|
434
|
-
- `--out` — the build output folder (default: the first of `dist/`, `build/` or `out/` that holds an
|
|
435
|
-
`index.html` written by this build). Next.js projects always pack into `public/livelocale/`
|
|
436
|
-
before the build runs, so Next copies it into the output.
|
|
437
|
-
- `--dir`, `--locales`, `--source`, `--base` and `--expose-repo` work as they do for `pack`.
|
|
438
|
-
|
|
439
|
-
| Stack | Build change |
|
|
440
|
-
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
441
|
-
| Next.js `output: 'export'` | `"build:preview": "livelocale build --preview -- next build"`, deployed to a staging location, because the HTML is pre-rendered and must be watermarked at build time. |
|
|
442
|
-
| Next.js on Vercel/Node | For the Preview environment, use the build command `livelocale build --preview -- next build`. It packs into a gitignored `public/livelocale/`. |
|
|
443
|
-
| Custom CI | `LIVELOCALE=preview <your build>` (only for SSR/SSG), then `npx livelocale pack <outDir>`. |
|
|
444
|
-
|
|
445
|
-
A static export stops before building when `app/api/livelocale/route.*` exports POST, because export
|
|
446
|
-
mode only allows GET route handlers; the share link does not need that route.
|
|
447
|
-
|
|
448
|
-
### `apply`
|
|
449
|
-
|
|
450
|
-
Applies a LiveLocale change link (the `lc1.` link a translator sends from the share link) to your
|
|
451
|
-
locale files. Every edit is checked again against your own files and source locale before anything
|
|
452
|
-
is written.
|
|
453
|
-
|
|
454
|
-
```bash
|
|
455
|
-
npx livelocale apply "<link>" [--pr] [--dir <path>] [--locales <path>] [--source <locale>] [--force] [--dry-run]
|
|
456
|
-
npx livelocale apply changes.txt
|
|
457
|
-
npx livelocale apply - < changes.txt
|
|
458
|
-
npx livelocale apply --clipboard
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
- The input is the change link itself (or the Studio link that carries it), a file that holds it
|
|
462
|
-
(the change file Send offers, or a saved email or issue), `-` to read standard input, or
|
|
463
|
-
`--clipboard` to read the clipboard. Pass the link or `--clipboard`, not both.
|
|
464
|
-
- cmd.exe cuts command lines at 8,191 characters, so on Windows pass a long link as a file or with
|
|
465
|
-
`--clipboard`.
|
|
466
|
-
- `--pr` — commit the changes on a new `livelocale/apply-<time>` branch, push it and open a pull
|
|
467
|
-
request.
|
|
468
|
-
- `--locales` and `--source` work as they do for `check` (default `locales`, and `en`, else the
|
|
469
|
-
first locale).
|
|
470
|
-
- `--dry-run` — plan the edits and print the summary without writing any file.
|
|
471
|
-
- `--force` — also apply edits the translator saved with "Save anyway" (for example a dropped
|
|
472
|
-
placeholder). Without it they are reported as `needs --force` and skipped.
|
|
473
|
-
|
|
474
|
-
The summary lists each edit's locale, key and status (applied, already done, conflict, needs
|
|
475
|
-
--force or rejected) and never prints translation values. The exit code is 0 when every edit was
|
|
476
|
-
applied or already done, and 1 when any edit is a conflict, needs `--force` or was rejected, or
|
|
477
|
-
when the link cannot be read.
|
|
478
|
-
|
|
479
|
-
#### `apply --github-event`
|
|
480
|
-
|
|
481
|
-
```bash
|
|
482
|
-
npx livelocale apply --github-event --locales <path> --source <locale> [--branches <a,b>] [--dir <path>]
|
|
483
|
-
```
|
|
97
|
+
Open `http://localhost:3010`, hold Alt (Option on a Mac), click a line of text and press
|
|
98
|
+
**Save Changes**. `git diff locales/` shows the fix.
|
|
484
99
|
|
|
485
|
-
|
|
486
|
-
the issue, comment, dispatch or pull request event from `$GITHUB_EVENT_PATH` (never from the
|
|
487
|
-
command line) and needs `GITHUB_TOKEN`.
|
|
100
|
+
## Supported versions
|
|
488
101
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
branch is never force-pushed. When the repository does not let GitHub Actions open pull
|
|
497
|
-
requests, the comment carries a one-click compare link instead.
|
|
498
|
-
- `--locales` and `--dir` are relative to the repository root. `--branches` lists the branches a
|
|
499
|
-
link may target (default: the repository's default branch); a link made from any other branch
|
|
500
|
-
targets the default branch, and the comment says so.
|
|
501
|
-
- `--force` is not accepted; a maintainer comments `/livelocale apply --force` instead.
|
|
502
|
-
- When the pull request is merged or closed, it closes the issue.
|
|
102
|
+
| Needs | Tested in CI |
|
|
103
|
+
| ----------------------------------------- | ------------------------------------------ |
|
|
104
|
+
| Node.js 18 or later | Node.js 20 and 24; the built package on 18 |
|
|
105
|
+
| Next.js 13 or later | Next.js 16 |
|
|
106
|
+
| React 18 or later | React 18 |
|
|
107
|
+
| Vite 5 or later | Vite 8 |
|
|
108
|
+
| TypeScript 4.7 to 6 (`check --hardcoded`) | TypeScript 5 |
|
|
503
109
|
|
|
504
|
-
|
|
110
|
+
From 1.0 on, livelocale follows semantic versioning, and share links people already sent keep
|
|
111
|
+
working in newer versions. [Versioning and stability](https://livelocale.js.org/docs/reference/stability.html)
|
|
112
|
+
lists what that covers and what is internal.
|
|
505
113
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
```bash
|
|
509
|
-
npx livelocale migrate [--dir <path>] [--exclude <dirs>] [--dry-run]
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
`migrate` needs the `typescript` package to parse your source. It is an optional dependency so
|
|
513
|
-
JavaScript-only projects are not forced to install it; `init` and `studio` never need it.
|
|
514
|
-
|
|
515
|
-
---
|
|
516
|
-
|
|
517
|
-
## ⚙️ Non-Next.js Integration (React + Vite SPA)
|
|
518
|
-
|
|
519
|
-
If you are using a standard React Client-Side SPA (like Vite), you don't have Next.js API endpoints. You can use the built-in Vite plugin inside `vite.config.ts` to automatically capture the mutation endpoints:
|
|
520
|
-
|
|
521
|
-
```typescript
|
|
522
|
-
import { defineConfig } from 'vite';
|
|
523
|
-
import react from '@vitejs/plugin-react';
|
|
524
|
-
import { liveLocaleVite } from 'livelocale/vite';
|
|
525
|
-
|
|
526
|
-
export default defineConfig({
|
|
527
|
-
plugins: [
|
|
528
|
-
react(),
|
|
529
|
-
liveLocaleVite({
|
|
530
|
-
localesPath: './locales', // path to your locales directory
|
|
531
|
-
}),
|
|
532
|
-
],
|
|
533
|
-
});
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
### Share link from `vite build`
|
|
537
|
-
|
|
538
|
-
Set `share: true` and `vite build` also writes a share link: the Studio, `manifest.json`, the
|
|
539
|
-
locale files and `flag.js` go into `<outDir>/livelocale/`, and `index.html` loads
|
|
540
|
-
`<base>livelocale/flag.js` first in `<head>`. Deploy the output anywhere static and send
|
|
541
|
-
`<origin><base>livelocale/index.html`. Without `share` the build output is unchanged.
|
|
542
|
-
|
|
543
|
-
```typescript
|
|
544
|
-
liveLocaleVite({
|
|
545
|
-
localesPath: './locales',
|
|
546
|
-
share: true, // write <outDir>/livelocale/ on vite build (default: false)
|
|
547
|
-
exposeRepo: false, // put the GitHub owner/repo into the manifest (default: false)
|
|
548
|
-
sourceLocale: 'en', // default: en when present, else the first locale
|
|
549
|
-
shareBase: '/app/', // the site path the HTML is served from (default: Vite base)
|
|
550
|
-
});
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
| Option | Default | What it does |
|
|
554
|
-
| -------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
555
|
-
| `share` | `false` | Write the share Studio into `<outDir>/livelocale/` on `vite build`. SSR and library builds are skipped. |
|
|
556
|
-
| `exposeRepo` | `false` | Put the GitHub `owner/repo` into the published manifest and the change link. Leave it off for a private repository. |
|
|
557
|
-
| `sourceLocale` | `en` when present, else the first locale | The locale placeholders are checked against. |
|
|
558
|
-
| `shareBase` | Vite `base` | The path the HTML is served from. Set it when `base` is a full URL (a CDN), which the build otherwise rejects with `BAD_BASE`, or when the HTML is served from a path other than `base`. |
|
|
559
|
-
|
|
560
|
-
A relative `base` (`./`) links `flag.js` per HTML file and prints a warning: pages served by a
|
|
561
|
-
client-side route fallback cannot load it, so prefer an absolute `base` for apps with such routes.
|
|
562
|
-
|
|
563
|
-
---
|
|
564
|
-
|
|
565
|
-
## 🧾 Reviewing and committing from the Studio
|
|
566
|
-
|
|
567
|
-
Every save writes to disk immediately. The **Changes** button in the Studio header opens a panel
|
|
568
|
-
that keeps those writes reviewable:
|
|
569
|
-
|
|
570
|
-
- **Locale files** lists what git sees as modified, added, or deleted under your locales directory,
|
|
571
|
-
with the current branch and an on-demand diff.
|
|
572
|
-
- **Commit** stages and commits _only_ the locales directory with the message you type. Other
|
|
573
|
-
work in your tree is never touched. Push when you are ready.
|
|
574
|
-
- **Discard** reverts the locales directory to the last commit (tracked files restored, new locale
|
|
575
|
-
files removed) after a confirmation.
|
|
576
|
-
- **This session** lists each edit you saved with its previous and new value. **Undo** writes the
|
|
577
|
-
previous value back through the normal save path.
|
|
578
|
-
|
|
579
|
-
Git integration needs the `git` command on PATH and the locales directory inside a repository. It
|
|
580
|
-
is on by default and can be turned off per handler:
|
|
581
|
-
|
|
582
|
-
```typescript
|
|
583
|
-
createLiveLocaleHandler({ localesPath: path.resolve('./locales'), git: false });
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
Rewrites keep your files diff-friendly: the existing indentation unit (tabs, 2 or 4 spaces) and
|
|
587
|
-
trailing newline are preserved.
|
|
588
|
-
|
|
589
|
-
## 🔢 Editing plural forms
|
|
590
|
-
|
|
591
|
-
When a message is a single ICU `plural`, `select`, or `selectordinal` argument, optionally with text
|
|
592
|
-
around it, the editor replaces the textarea with one field per branch:
|
|
593
|
-
|
|
594
|
-
- Fields are the union of the branches already in the message (including explicit cases such as
|
|
595
|
-
`=0`) and the CLDR categories of the active locale, from `Intl.PluralRules`. English shows
|
|
596
|
-
`one` and `other`; Arabic shows `zero`, `one`, `two`, `few`, `many`, `other`; Japanese shows
|
|
597
|
-
`other` only. Required categories that are still empty are flagged.
|
|
598
|
-
- `#` stands for the number. Text before and after the argument is editable separately.
|
|
599
|
-
- Empty branches are dropped from the saved message, except `other`, which ICU requires.
|
|
600
|
-
- **Edit raw** switches back to the plain textarea at any time.
|
|
601
|
-
|
|
602
|
-
## 🧩 Placeholder safety
|
|
603
|
-
|
|
604
|
-
Most production incidents caused by copy edits come from a placeholder that went missing. The
|
|
605
|
-
Studio and the API guard against that:
|
|
606
|
-
|
|
607
|
-
- The editor shows the placeholders found in the reference text as chips. A chip turns red when
|
|
608
|
-
your translation no longer contains it and amber when you introduce one the reference lacks.
|
|
609
|
-
- On save, the server compares the placeholder sets of the reference and the new value. A
|
|
610
|
-
mismatch answers `422 PLACEHOLDER_MISMATCH` and the Studio shows the reason with **Save
|
|
611
|
-
anyway** and **Keep editing**.
|
|
612
|
-
- Structurally broken ICU messages (unbalanced braces, a `plural`/`select` without an `other`
|
|
613
|
-
branch) answer `422 MESSAGE_SYNTAX` with the same override.
|
|
614
|
-
|
|
615
|
-
Recognised syntaxes: ICU/next-intl/react-intl `{name}`, `{count, plural, …}` (with apostrophe
|
|
616
|
-
quoting), i18next `{{name}}` and `$t(key)`, printf `%s` / `%1$s`, and rich-text tags such as
|
|
617
|
-
`<b>` or `<0>`. The reference is the source-language value for other locales, and the previous
|
|
618
|
-
value when you edit the source language itself.
|
|
619
|
-
|
|
620
|
-
For API clients: send `reference` with the POST to enable the check, and `force: true` to bypass
|
|
621
|
-
it. Without `reference` only the syntax check runs.
|
|
622
|
-
|
|
623
|
-
## 🔗 Share link on any host
|
|
624
|
-
|
|
625
|
-
Translators fix text on your live site with no account, and every fix becomes a pull request with
|
|
626
|
-
one click.
|
|
627
|
-
|
|
628
|
-
A share link needs no server, no account and no credential anywhere. The Studio ships inside your
|
|
629
|
-
build at `<base>livelocale/`, so any host that serves the site serves the Studio too: Apache,
|
|
630
|
-
Nginx, S3, GitHub Pages, Netlify, Cloudflare Pages or Vercel. The translator's fixes stay in their
|
|
631
|
-
own browser until they send you a change link. On GitHub you turn that link into a pull request
|
|
632
|
-
with one click; on any other git host, with one command.
|
|
633
|
-
|
|
634
|
-
### Setup
|
|
635
|
-
|
|
636
|
-
1. Run `npx livelocale init --share` in your project. It sets up the build for your stack (see the
|
|
637
|
-
table below) and, on a GitHub remote, writes `.github/workflows/livelocale.yml`. See
|
|
638
|
-
[`init --share`](#init---share) for its options.
|
|
639
|
-
2. Commit and push the result. The workflow must be on the default branch, because GitHub runs
|
|
640
|
-
issue and comment workflows only from there.
|
|
641
|
-
3. On GitHub, tick the one setting below.
|
|
642
|
-
4. Deploy as usual, then check the deployment and print the share link (next step).
|
|
643
|
-
|
|
644
|
-
On a GitHub remote, `init --share` writes this workflow (`X.Y.Z` is the livelocale version you
|
|
645
|
-
installed, `locales`, `en` and `main` come from your project):
|
|
646
|
-
|
|
647
|
-
````yaml
|
|
648
|
-
# Written by `npx livelocale init --share`. Turns LiveLocale change links into pull requests.
|
|
649
|
-
# livelocale reads the link from the event file; it never reaches a shell command.
|
|
650
|
-
name: LiveLocale
|
|
651
|
-
on:
|
|
652
|
-
issues: { types: [opened] }
|
|
653
|
-
issue_comment: { types: [created] }
|
|
654
|
-
pull_request: { types: [closed] }
|
|
655
|
-
workflow_dispatch: { inputs: { link: { description: 'LiveLocale change link', required: true } } }
|
|
656
|
-
permissions: { contents: write, pull-requests: write, issues: write }
|
|
657
|
-
concurrency: { group: 'livelocale-${{ github.event.issue.number || github.run_id }}', queue: max }
|
|
658
|
-
jobs:
|
|
659
|
-
apply:
|
|
660
|
-
if: >-
|
|
661
|
-
github.event_name == 'workflow_dispatch' ||
|
|
662
|
-
(github.event_name == 'issues' && contains(github.event.issue.body, '```livelocale')) ||
|
|
663
|
-
(github.event_name == 'issue_comment' && !github.event.issue.pull_request &&
|
|
664
|
-
startsWith(github.event.comment.body, '/livelocale')) ||
|
|
665
|
-
(github.event_name == 'pull_request' && startsWith(github.event.pull_request.head.ref, 'livelocale/issue-') &&
|
|
666
|
-
github.event.pull_request.base.ref != github.event.repository.default_branch)
|
|
667
|
-
runs-on: ubuntu-latest
|
|
668
|
-
steps:
|
|
669
|
-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
670
|
-
with: { persist-credentials: false }
|
|
671
|
-
- run: npx --yes livelocale@X.Y.Z apply --github-event --locales locales --source en --branches main
|
|
672
|
-
env: { GITHUB_TOKEN: '${{ github.token }}' }
|
|
673
|
-
````
|
|
674
|
-
|
|
675
|
-
It reads the change link only from the event file, never from a shell command, and it has no
|
|
676
|
-
`pull_request_target` trigger.
|
|
677
|
-
|
|
678
|
-
To set up the build by hand instead:
|
|
679
|
-
|
|
680
|
-
| Stack | Build change |
|
|
681
|
-
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
682
|
-
| Vite | `share: true` in `liveLocaleVite(...)` (see [Share link from `vite build`](#share-link-from-vite-build)). |
|
|
683
|
-
| CRA | `"build": "<bundler> && livelocale pack build --locales <path> --source <locale>"`, for example `"build": "react-scripts build && livelocale pack build --locales locales --source en"`. |
|
|
684
|
-
| webpack and Rsbuild | The same, packing `dist`: `"build": "<bundler> && livelocale pack dist --locales <path> --source <locale>"`. |
|
|
685
|
-
| Next.js `output: 'export'` | `livelocale build --preview -- next build`, deployed to a staging location, because the HTML is pre-rendered and must be watermarked. |
|
|
686
|
-
| Next.js on Vercel/Node | `livelocale build --preview -- next build` as the Preview environment's build command. It packs into a gitignored `public/livelocale/`. |
|
|
687
|
-
| Custom CI | `LIVELOCALE=preview <build>` (only for SSR/SSG), then `npx livelocale pack <outDir>`. |
|
|
688
|
-
|
|
689
|
-
For CRA, webpack and Rsbuild, use an explicit `&&`, not a `postbuild` script: Yarn Berry and some
|
|
690
|
-
pnpm versions skip pre/post scripts, and hosts that call the bundler directly skip them too.
|
|
691
|
-
|
|
692
|
-
Deploy the output as usual, then check it and print the share link:
|
|
693
|
-
|
|
694
|
-
```bash
|
|
695
|
-
npx livelocale doctor --app <url>
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
The doctor checks the Studio page, the manifest, `flag.js`, frame headers and service workers, then
|
|
699
|
-
prints `🔗 Share link: <base>livelocale/index.html`. Pin that link for your translators.
|
|
700
|
-
|
|
701
|
-
#### The one-checkbox setting
|
|
702
|
-
|
|
703
|
-
In the repository's **Settings → Actions → General → Workflow permissions**, tick
|
|
704
|
-
**Allow GitHub Actions to create and approve pull requests**. Without it the workflow still
|
|
705
|
-
commits the branch, but GitHub refuses to let it open the pull request; the issue comment then
|
|
706
|
-
carries a one-click compare link that opens the pull request form already filled in, and you
|
|
707
|
-
press **Create pull request** there.
|
|
708
|
-
|
|
709
|
-
### Translator steps
|
|
710
|
-
|
|
711
|
-
1. Open the share link in a desktop browser. There is no sign-up, no password and nothing to
|
|
712
|
-
install. If the site has its own login, sign in as usual.
|
|
713
|
-
2. On the first visit, read the three tips and, optionally, type your name so you are credited in
|
|
714
|
-
the pull request.
|
|
715
|
-
3. Check the language shown at the top. It is detected from the page text; change it if it is
|
|
716
|
-
wrong.
|
|
717
|
-
4. Hold **Alt** (Option on Mac) and click the wrong text. The editor shows the source text, the
|
|
718
|
-
current translation, the placeholders that must stay, and one field per plural form.
|
|
719
|
-
5. Type the fix and press **Save** (Ctrl+Enter). When a placeholder is missing, a message explains
|
|
720
|
-
it, and **Save anyway** stays available; it marks the edit for the developer.
|
|
721
|
-
6. Keep browsing and fixing. Changes survive reloads and closing the tab. A counter shows how many
|
|
722
|
-
are not sent yet ("5 changes not sent"), and **Changes** lists each one with Undo.
|
|
723
|
-
7. Click **Send to developer** and add an optional note. Then choose **Copy link** and paste it in
|
|
724
|
-
any chat or email, or **Download file** for a large batch.
|
|
725
|
-
8. Sent changes stay in the list, marked "Sent".
|
|
726
|
-
|
|
727
|
-
### Developer
|
|
728
|
-
|
|
729
|
-
Open the change link. The Studio opens in **Review mode**, with the proposed edits shown read-only
|
|
730
|
-
on the real page and a before/after table with the check results. Then turn it into a pull request.
|
|
731
|
-
|
|
732
|
-
**On GitHub, with the workflow:**
|
|
733
|
-
|
|
734
|
-
- Press **Create pull request** in Review mode. It opens a GitHub issue already filled in with the
|
|
735
|
-
change link; press **Submit new issue**. About a minute later the workflow has committed the
|
|
736
|
-
edits on a `livelocale/issue-<n>` branch, opened the pull request and commented its link on the
|
|
737
|
-
issue. Without `exposeRepo: true`, Review mode asks for `owner/repo` the first time and remembers
|
|
738
|
-
it in that browser.
|
|
739
|
-
- With issues turned off, or for a link you would rather not file: **Actions → LiveLocale → Run
|
|
740
|
-
workflow**, paste the link and run it. The result is on the run's summary page.
|
|
741
|
-
- **The trust gate.** The workflow writes only for a maintainer: the issue author or the
|
|
742
|
-
commenter must be an owner, member or collaborator, or have write access. In an organization
|
|
743
|
-
that includes any member or invited collaborator, even one with read-only access: they can turn a
|
|
744
|
-
link into a pull request, but only people with write access can merge it. For anyone else it
|
|
745
|
-
replies "Waiting for a maintainer: comment `/livelocale apply`", and nothing is written until a
|
|
746
|
-
maintainer posts that comment on the issue. Edits the translator saved with "Save anyway" are
|
|
747
|
-
skipped as `needs --force`; comment `/livelocale apply --force` to include them.
|
|
748
|
-
- **Base branches.** By default every pull request targets the default branch. To let links made
|
|
749
|
-
from other builds (for example a `staging` deployment) target their own branch, list the
|
|
750
|
-
branches with `npx livelocale init --share --branches main,staging`. A link from any other
|
|
751
|
-
branch targets the default branch, and the comment says so. A pull request into the default
|
|
752
|
-
branch says `Closes #n`, so merging it closes the issue; into another branch it says `Refs #n`,
|
|
753
|
-
because GitHub only closes issues from the default branch, and LiveLocale closes the issue
|
|
754
|
-
itself when that pull request is merged or closed. Also merge `.github/workflows/livelocale.yml`
|
|
755
|
-
into each extra branch: pull requests into it run workflows from that branch, so LiveLocale cannot
|
|
756
|
-
close their issues otherwise.
|
|
757
|
-
|
|
758
|
-
**On any git host, from your machine:**
|
|
759
|
-
|
|
760
|
-
```bash
|
|
761
|
-
npx livelocale apply "<link or file>" --pr
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
This works with your own `git` login: it commits only the locale files on a new
|
|
765
|
-
`livelocale/apply-<time>` branch and pushes it. On GitHub it opens the pull request with `gh`;
|
|
766
|
-
elsewhere it prints the branch to open a pull or merge request from. Every edit is checked again
|
|
767
|
-
against your own files first. For a long link, use `npx livelocale apply --clipboard`, or pass `-`
|
|
768
|
-
and pipe the link in (`npx livelocale apply - < changes.txt`). See [`apply`](#apply) for every
|
|
769
|
-
option.
|
|
770
|
-
|
|
771
|
-
#### Run CI on LiveLocale PRs without the approval click
|
|
772
|
-
|
|
773
|
-
Pull requests opened with the workflow's `GITHUB_TOKEN` start their CI in an approval-required
|
|
774
|
-
state: someone has to press **Approve workflows to run** on each one. Pull requests opened with a
|
|
775
|
-
GitHub App token start CI as usual. To use one:
|
|
776
|
-
|
|
777
|
-
1. Create a GitHub App with **Contents**, **Pull requests** and **Issues** set to read and write,
|
|
778
|
-
and install it on the repository.
|
|
779
|
-
2. Store its Client ID (on the App's settings page) as the repository variable
|
|
780
|
-
`LIVELOCALE_APP_CLIENT_ID` and its private key as the secret `LIVELOCALE_APP_PRIVATE_KEY`.
|
|
781
|
-
3. In `.github/workflows/livelocale.yml`, add this step before the `apply` step and give its token
|
|
782
|
-
to the `apply` step as `GITHUB_TOKEN`:
|
|
783
|
-
|
|
784
|
-
```yaml
|
|
785
|
-
- id: app-token
|
|
786
|
-
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
|
|
787
|
-
with:
|
|
788
|
-
{
|
|
789
|
-
client-id: '${{ vars.LIVELOCALE_APP_CLIENT_ID }}',
|
|
790
|
-
private-key: '${{ secrets.LIVELOCALE_APP_PRIVATE_KEY }}',
|
|
791
|
-
}
|
|
792
|
-
- run: npx --yes livelocale@X.Y.Z apply --github-event --locales locales --source en --branches main
|
|
793
|
-
env: { GITHUB_TOKEN: '${{ steps.app-token.outputs.token }}' }
|
|
794
|
-
```
|
|
795
|
-
|
|
796
|
-
`init --share` leaves an edited workflow alone unless you pass `--overwrite`.
|
|
797
|
-
|
|
798
|
-
### Translators with GitHub access
|
|
799
|
-
|
|
800
|
-
**Open on GitHub** in the Send dialog lets a translator file the issue themselves, so the pull
|
|
801
|
-
request opens with no developer click when the translator is a collaborator (otherwise a
|
|
802
|
-
maintainer comments `/livelocale apply`). It is available only with `exposeRepo: true`, because it
|
|
803
|
-
needs the repository's `owner/repo` in the public manifest. Turn it on for a private repository
|
|
804
|
-
whose translators already have GitHub access; with the default, translators use **Copy link** or
|
|
805
|
-
**Download file**, and the developer path is unchanged.
|
|
806
|
-
|
|
807
|
-
### What "Sent" means
|
|
808
|
-
|
|
809
|
-
In this mode "Sent" means the translator handed the link over; it is not a pull request until a
|
|
810
|
-
developer applies it.
|
|
811
|
-
|
|
812
|
-
### Hosting notes
|
|
813
|
-
|
|
814
|
-
- **S3 and CloudFront:** the link must end in `index.html`, because an S3 REST origin does not
|
|
815
|
-
resolve folder indexes.
|
|
816
|
-
- **GitHub Pages:** the folder is `livelocale/`, with no underscore for Jekyll to drop, and a
|
|
817
|
-
project site's `/repo/` base path is handled.
|
|
818
|
-
- **Netlify and Cloudflare Pages:** `_headers` must not send DENY (`X-Frame-Options: DENY` or
|
|
819
|
-
`frame-ancestors 'none'`); SAMEORIGIN is fine. Every per-deploy URL is a separate origin with its
|
|
820
|
-
own drafts, so share the branch alias (for example
|
|
821
|
-
`https://staging--site.netlify.app/livelocale/index.html`), not a per-deploy URL.
|
|
822
|
-
- **Basic auth and host password protection** carry over, because the Studio is on the same site.
|
|
823
|
-
- **Vercel:** Deployment Protection must be off for the deployment you share, or the translator
|
|
824
|
-
must have access to it. `doctor --app` reports the login wall.
|
|
825
|
-
- **PWA:** a Workbox service worker (vite-plugin-pwa, workbox-webpack-plugin) can answer the Studio
|
|
826
|
-
with your app shell. Add `navigateFallbackDenylist: [/^\/livelocale\//]` to its Workbox options.
|
|
827
|
-
- **Relative bases** (`base: './'`, CRA `homepage: '.'`): pages served by a client-side route
|
|
828
|
-
fallback cannot load `flag.js`, so pack prints a warning recommending an absolute base
|
|
829
|
-
(`--base /path/`) when the app has such routes.
|
|
830
|
-
|
|
831
|
-
### Security
|
|
832
|
-
|
|
833
|
-
- **No credential anywhere.** The share link, the build, the host and the browser hold no token.
|
|
834
|
-
Write access stays with the developer's own git login.
|
|
835
|
-
- **A leaked link only proposes text.** A developer still has to apply it, and a person has to
|
|
836
|
-
merge the pull request. `apply` re-validates every edit against your own source locale.
|
|
837
|
-
- **`exposeRepo: false` by default.** The GitHub `owner/repo` is published only with
|
|
838
|
-
`exposeRepo: true` (or `pack --expose-repo`).
|
|
839
|
-
- **The Studio is `noindex`**, so search engines do not list it.
|
|
840
|
-
- **`manifest.json` is public.** `<base>livelocale/manifest.json` makes every string and key name
|
|
841
|
-
public, including keys the app does not show.
|
|
842
|
-
- **The same-origin risk is accepted.** The Studio frames your app on the same origin, so app
|
|
843
|
-
scripts can read and alter drafts. This is accepted because no secret is stored, and you review
|
|
844
|
-
every edit before it is merged.
|
|
845
|
-
|
|
846
|
-
### Honest risks
|
|
847
|
-
|
|
848
|
-
- **Issues in public repositories are public.** A change link filed as an issue, with its text,
|
|
849
|
-
the translator's name and note, is visible to everyone.
|
|
850
|
-
- **Bot pull requests wait for a click.** CI on a pull request the workflow opens waits until
|
|
851
|
-
someone presses **Approve workflows to run**, unless you use the
|
|
852
|
-
[App token](#run-ci-on-livelocale-prs-without-the-approval-click).
|
|
853
|
-
- **Vercel may refuse the commit.** Vercel can block deployments of commits whose author is not a
|
|
854
|
-
member of the Vercel team. The commit author is the maintainer who triggered the run, with a
|
|
855
|
-
`Translated-by:` trailer for the translator.
|
|
856
|
-
- **One-click automation is GitHub-only.** Other git hosts use
|
|
857
|
-
`npx livelocale apply "<link>" --pr`, which works anywhere git works.
|
|
858
|
-
|
|
859
|
-
### Limits
|
|
860
|
-
|
|
861
|
-
- **Desktop only.** Alt+Click needs a keyboard; phones and tablets can only view.
|
|
862
|
-
- **Drafts live in one browser.** Clearing site data, a private window, or Safari's 7-day cap on
|
|
863
|
-
script-writable storage can lose unsent work. Send often, or use **Download backup** in Changes.
|
|
864
|
-
- **One app per repository.**
|
|
865
|
-
|
|
866
|
-
## 🌍 Preview mode: editing without a checkout
|
|
867
|
-
|
|
868
|
-
Development mode needs a developer's machine. Preview mode lets a product manager, translator, or
|
|
869
|
-
reviewer edit copy against a **deployed preview** of the app, with changes going to a git branch
|
|
870
|
-
and a pull request instead of a local file. Nothing is written to the server's filesystem.
|
|
871
|
-
|
|
872
|
-
### How it fits together
|
|
873
|
-
|
|
874
|
-
1. The app is deployed from a branch (say `i18n/preview-edits`) with preview mode on.
|
|
875
|
-
2. The Studio, hosted anywhere or run locally with `--app <preview-url>`, embeds that deployment.
|
|
876
|
-
3. Each save calls the app's mutation API, which commits the change to `i18n/preview-edits`
|
|
877
|
-
through the GitHub API. The live preview in the iframe updates immediately; the deployment
|
|
878
|
-
itself updates when your platform rebuilds the branch.
|
|
879
|
-
4. **Open pull request** in the Changes panel proposes the branch to `main`. Reviewers merge.
|
|
880
|
-
|
|
881
|
-
### Server setup
|
|
882
|
-
|
|
883
|
-
```typescript
|
|
884
|
-
// app/api/livelocale/route.ts
|
|
885
|
-
import { createLiveLocaleHandler } from 'livelocale/server';
|
|
886
|
-
|
|
887
|
-
const handler = createLiveLocaleHandler({
|
|
888
|
-
localesPath: 'locales', // repository-relative in GitHub mode
|
|
889
|
-
allowedOrigins: ['https://i18n-studio.example.com'], // where the Studio is served from
|
|
890
|
-
github: {
|
|
891
|
-
repo: 'acme/site',
|
|
892
|
-
branch: 'i18n/preview-edits',
|
|
893
|
-
baseBranch: 'main',
|
|
894
|
-
token: process.env.GITHUB_TOKEN!, // contents: write, pull requests: write
|
|
895
|
-
},
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
export const GET = handler;
|
|
899
|
-
export const POST = handler;
|
|
900
|
-
export const OPTIONS = handler;
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
Environment on the preview deployment:
|
|
904
|
-
|
|
905
|
-
```bash
|
|
906
|
-
LIVELOCALE=preview # server: watermark and serve the API in a production build
|
|
907
|
-
NEXT_PUBLIC_LIVELOCALE=preview # client bundle: run the scanner and clipboard guards
|
|
908
|
-
LIVELOCALE_TOKEN=<long random> # required; the Studio asks for it once per browser session
|
|
909
|
-
GITHUB_TOKEN=<fine-grained PAT> # Contents and Pull requests: "Read and write" on the repository
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
The app must also let the Studio embed it. Next.js evaluates `headers()` in `next.config` at
|
|
913
|
-
**build** time, so a `frame-ancestors` value that lists the Studio origin has to be present when the
|
|
914
|
-
preview is built, not only at run time.
|
|
915
|
-
|
|
916
|
-
For a Vite app, the Vite plugin mirrors the server's `LIVELOCALE` flag into the page, so
|
|
917
|
-
`LIVELOCALE=preview` alone is enough; no `NEXT_PUBLIC_` variable is needed there. Preview mode
|
|
918
|
-
runs on the Vite dev server only. For a static `vite build` served by Apache, Nginx or a CDN, use
|
|
919
|
-
the share link instead; see [Share link on any host](#-share-link-on-any-host).
|
|
920
|
-
|
|
921
|
-
Then open the Studio with `?app=https://<preview-url>` (or `studio --app`). It shows a
|
|
922
|
-
**PREVIEW** badge, asks for the access token, and the Changes panel switches to branch and
|
|
923
|
-
pull-request actions. Code-reference scanning is off in this mode (there is no checkout), so the
|
|
924
|
-
Locale Directory lists keys from the locale files only.
|
|
925
|
-
|
|
926
|
-
Check a preview deployment before handing it to anyone:
|
|
927
|
-
|
|
928
|
-
```bash
|
|
929
|
-
npx livelocale doctor --app https://<preview-url> --token <LIVELOCALE_TOKEN>
|
|
930
|
-
```
|
|
931
|
-
|
|
932
|
-
The server verifies its GitHub token when the Studio connects: a token that cannot write contents
|
|
933
|
-
or open pull requests, a missing base branch, or an unknown repository is reported in a banner
|
|
934
|
-
in the Studio and by `doctor`, before anyone loses an edit to a failed save.
|
|
935
|
-
|
|
936
|
-
Behaviour worth knowing:
|
|
937
|
-
|
|
938
|
-
- **Edits stay visible.** The deployment keeps serving the text it was built with until the
|
|
939
|
-
branch is rebuilt, so the Studio re-applies this session's saved values on reload and navigation.
|
|
940
|
-
- **Concurrent saves are safe.** Saves to one file are queued, and a save that loses a race with
|
|
941
|
-
another server instance or another Studio re-reads the file and retries.
|
|
942
|
-
- **Discard closes the pull request.** Resetting the branch to the base leaves the pull request
|
|
943
|
-
with no changes, and GitHub closes it.
|
|
944
|
-
- **Every save is a commit, and most hosts build every commit.** To save build minutes, skip builds
|
|
945
|
-
for commits that only touch locale files; the Studio already shows saved edits. On Vercel, set
|
|
946
|
-
_Project Settings → Git → Ignored Build Step_ to
|
|
947
|
-
`git diff --quiet HEAD^ HEAD -- . ':(exclude)locales'`.
|
|
948
|
-
- **Hosting login walls block the Studio.** Vercel's Deployment Protection (on by default for
|
|
949
|
-
previews) sends the iframe and the API calls to a Vercel login page. Turn off Vercel
|
|
950
|
-
Authentication for the preview deployments you edit; `doctor --app` reports it.
|
|
951
|
-
|
|
952
|
-
### What preview mode does not do
|
|
953
|
-
|
|
954
|
-
- It never writes to the deployment's filesystem; the GitHub branch is the only store.
|
|
955
|
-
- It does not replace authentication for the app itself. The token only guards the mutation API.
|
|
956
|
-
- It is not a production feature. Do not set `LIVELOCALE=preview` on a production deployment.
|
|
957
|
-
|
|
958
|
-
## 🛑 Safety switches
|
|
959
|
-
|
|
960
|
-
Everything runs only when `NODE_ENV=development`, or when preview mode is switched on explicitly.
|
|
961
|
-
Two more guards exist for teams:
|
|
962
|
-
|
|
963
|
-
- **Kill switch.** `LIVELOCALE=off` disables watermarking, the mutation API, and the client scanner.
|
|
964
|
-
Client bundles read `NEXT_PUBLIC_LIVELOCALE=off` instead, because Next.js only inlines public
|
|
965
|
-
variables.
|
|
966
|
-
- **Deployed-platform guard.** If the server detects Vercel (preview or production), Netlify,
|
|
967
|
-
Kubernetes, Render, Fly.io, Railway, or Heroku while `NODE_ENV` is still `development`, the tool
|
|
968
|
-
switches itself off and logs one warning. Local `vercel dev` and `netlify dev` are unaffected.
|
|
969
|
-
Set `LIVELOCALE=force` to override on purpose.
|
|
970
|
-
|
|
971
|
-
## 🪄 When the watermark bites
|
|
972
|
-
|
|
973
|
-
In development every translated string starts with invisible characters. Most code never notices,
|
|
974
|
-
but anything that treats the string as data will:
|
|
975
|
-
|
|
976
|
-
| Symptom | Cause | Fix |
|
|
977
|
-
| --------------------------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------- |
|
|
978
|
-
| `t('yes') === 'Yes'` is false | equality on a watermarked string | `unwrap(t('yes')) === 'Yes'` |
|
|
979
|
-
| `maxLength` or `.length` checks are off by a few | invisible prefix counts as characters | `unwrap(value).length` |
|
|
980
|
-
| `<title>`, `aria-label`, `alt`, `placeholder` show odd spacing in dev tools | attributes do not strip the prefix | pass `unwrap(...)` to attributes, or leave it: it is dev-only |
|
|
981
|
-
| Screenshot or snapshot tests differ between dev and prod | watermarks only exist in development | run those tests with `LIVELOCALE=off` |
|
|
982
|
-
| A value saved from a form contains zero-width characters | input sanitizers were bypassed (custom components) | call `sanitizeZeroWidth` before submitting |
|
|
983
|
-
|
|
984
|
-
`unwrap` and `sanitizeZeroWidth` are exported from both `livelocale/client` and
|
|
985
|
-
`livelocale/server`. `unwrap` is deep: it cleans strings inside arrays and objects and never
|
|
986
|
-
mutates its input.
|
|
987
|
-
|
|
988
|
-
## 🏢 Running in team and enterprise setups
|
|
989
|
-
|
|
990
|
-
The defaults assume `localhost:3000` for the app and `localhost:3010` for the Studio. Anything else
|
|
991
|
-
needs one of the knobs below.
|
|
992
|
-
|
|
993
|
-
**App on another port or hostname.** Start the Studio with `--app https://app.dev.local:3000`, or
|
|
994
|
-
open the Studio with `?app=<origin>`.
|
|
995
|
-
|
|
996
|
-
**Studio not on localhost (GitHub Codespaces, Gitpod, a shared dev box).** The mutation API only
|
|
997
|
-
accepts writes from localhost origins unless told otherwise. Either pass `allowedOrigins` to
|
|
998
|
-
`createLiveLocaleHandler`, or set an environment variable in the app's dev process:
|
|
999
|
-
|
|
1000
|
-
```bash
|
|
1001
|
-
LIVELOCALE_ALLOWED_ORIGINS="https://abc-3010.app.github.dev,https://studio.dev.local"
|
|
1002
|
-
```
|
|
1003
|
-
|
|
1004
|
-
**Content-Security-Policy blocks the iframe.** If your app sends `frame-ancestors 'none'` or
|
|
1005
|
-
`X-Frame-Options: DENY` in development, the Studio cannot embed it. Relax the header only when
|
|
1006
|
-
`NODE_ENV === 'development'`, for example by allowing `frame-ancestors 'self' http://localhost:3010`.
|
|
1007
|
-
|
|
1008
|
-
**Auth middleware redirects the API.** Exclude `/api/livelocale` from your middleware matcher so the
|
|
1009
|
-
Studio can reach the mutation route without a session:
|
|
1010
|
-
|
|
1011
|
-
```typescript
|
|
1012
|
-
export const config = { matcher: ['/((?!api/livelocale).*)'] };
|
|
1013
|
-
```
|
|
1014
|
-
|
|
1015
|
-
**Not sure which of these applies?** Run `npx livelocale doctor --app <origin>`; it names
|
|
1016
|
-
the exact file and header involved.
|
|
1017
|
-
|
|
1018
|
-
**Namespaced hooks.** The key scanner reads the namespace from `useTranslations('ns')`,
|
|
1019
|
-
`getTranslations('ns')`, `useTranslation('ns')`, `wrapNextIntl(…, 'ns')`, and
|
|
1020
|
-
`wrapTranslationEngine(…, { keyPrefix: 'ns' })` in each file, and prefixes the keys it finds. It
|
|
1021
|
-
does not follow translator functions passed between files or chosen at run time.
|
|
1022
|
-
|
|
1023
|
-
**Monorepos.** `localesPath` is resolved relative to the process working directory of the app, so
|
|
1024
|
-
point it at the package that owns the locale files, for example `path.resolve('./src/locales')`.
|
|
1025
|
-
|
|
1026
|
-
---
|
|
114
|
+
## Docs
|
|
1027
115
|
|
|
1028
|
-
|
|
116
|
+
- [Get started](https://livelocale.js.org/docs/): Next.js, Vite + React, or any static host.
|
|
117
|
+
- [Next.js, step by step](https://livelocale.js.org/docs/start/nextjs.html): what each step should show.
|
|
118
|
+
- [Share links](https://livelocale.js.org/docs/guides/share-links.html): fixes from the deployed site, no account and no install.
|
|
119
|
+
- [Catch untranslated text in CI](https://livelocale.js.org/docs/guides/ci-check.html): missing keys, placeholder drift and hardcoded text.
|
|
120
|
+
- [CLI reference](https://livelocale.js.org/docs/reference/cli/index.html): every command and flag.
|
|
121
|
+
- [Versioning and stability](https://livelocale.js.org/docs/reference/stability.html): what semver covers, and the versions CI tests.
|
|
1029
122
|
|
|
1030
|
-
|
|
1031
|
-
aliases for the old names:
|
|
123
|
+
## Contributing
|
|
1032
124
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
`i18nLensVite` → `liveLocaleVite`, `getLensMode` / `isLensEnabled` → `getLiveLocaleMode` /
|
|
1037
|
-
`isLiveLocaleEnabled`; types `I18nLensHandler` → `LiveLocaleHandler` and `LensMode` →
|
|
1038
|
-
`LiveLocaleMode` (`ClientInitializer` is gone).
|
|
1039
|
-
3. Move the route file: `app/api/i18n-lens/mutate/route.ts` → `app/api/livelocale/route.ts`
|
|
1040
|
-
(Pages Router: `pages/api/i18n-lens/mutate.ts` → `pages/api/livelocale.ts`), or run
|
|
1041
|
-
`npx livelocale init` again.
|
|
1042
|
-
4. Rename environment variables: `I18N_LENS` → `LIVELOCALE`, `NEXT_PUBLIC_I18N_LENS` →
|
|
1043
|
-
`NEXT_PUBLIC_LIVELOCALE`, `I18N_LENS_TOKEN` → `LIVELOCALE_TOKEN`, `I18N_LENS_ALLOWED_ORIGINS` →
|
|
1044
|
-
`LIVELOCALE_ALLOWED_ORIGINS`. Old names are ignored, so a forgotten kill switch stops working:
|
|
1045
|
-
check your hosting settings.
|
|
1046
|
-
5. The CLI is `npx livelocale` (`doctor`, `check`, `studio`, `init`, `migrate`).
|
|
125
|
+
- [Contributing guide](CONTRIBUTING.md): setup, checks and changesets.
|
|
126
|
+
- [Security policy](SECURITY.md): report a vulnerability privately.
|
|
127
|
+
- [Code of Conduct](CODE_OF_CONDUCT.md): how we work together.
|
|
1047
128
|
|
|
1048
|
-
##
|
|
129
|
+
## License
|
|
1049
130
|
|
|
1050
131
|
MIT License. See [LICENSE](LICENSE) for more details.
|