livelocale 0.12.0 → 0.12.1-next.17
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 +21 -16
- package/dist/codemod/index.cjs +27 -10
- package/dist/codemod/index.cjs.map +1 -1
- package/dist/codemod/index.js +27 -10
- package/dist/codemod/index.js.map +1 -1
- package/dist/server/index.cjs +21 -6
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +21 -6
- package/dist/server/index.js.map +1 -1
- package/dist/studio/assets/index-CcIiR_d-.js +14 -0
- package/dist/studio/assets/index-KRNgb_rB.css +1 -0
- package/dist/studio/index.html +2 -2
- package/dist/vite/index.cjs +18 -5
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +18 -5
- package/dist/vite/index.js.map +1 -1
- package/package.json +21 -16
- package/dist/studio/assets/index-C6OyWSoQ.js +0 -14
- package/dist/studio/assets/index-ClWjP6QY.css +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
# LiveLocale
|
|
2
|
-
|
|
3
1
|
<div align="center">
|
|
2
|
+
<h1>
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="brand/logo-dark.svg">
|
|
5
|
+
<img src="brand/logo.svg" alt="livelocale" height="64">
|
|
6
|
+
</picture>
|
|
7
|
+
</h1>
|
|
4
8
|
<p><strong>Click any text in your app. Fix the translation. Ship it as a pull request.</strong></p>
|
|
5
9
|
<p>Keep next-intl or react-i18next. LiveLocale adds in-context editing for Next.js and React + Vite: no account, no server, no browser extension. Your translations stay as JSON in git.</p>
|
|
6
|
-
|
|
10
|
+
|
|
7
11
|
<p align="center">
|
|
8
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>
|
|
9
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>
|
|
@@ -220,12 +224,12 @@ file it creates, updates, leaves unchanged or skips, then what is left for you t
|
|
|
220
224
|
npx livelocale init --share [--locales <path>] [--source <locale>] [--branches <a,b>] [--overwrite] [--dir <path>]
|
|
221
225
|
```
|
|
222
226
|
|
|
223
|
-
| Stack | Build change
|
|
224
|
-
| -------------------------- |
|
|
225
|
-
| CRA / webpack / Rsbuild | `"build": "react-scripts build && livelocale pack 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)). |
|
|
226
|
-
| 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.
|
|
227
|
-
| 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/`.
|
|
228
|
-
| Custom CI | `LIVELOCALE=preview <your build>` (only for SSR/SSG), then `npx livelocale pack <outDir>`.
|
|
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>`. |
|
|
229
233
|
|
|
230
234
|
For Vite it adds `share: true` to your `liveLocaleVite({ ... })` call. The scripts it writes also
|
|
231
235
|
pass `--locales` and `--source`; webpack and Rsbuild pack `dist`.
|
|
@@ -673,13 +677,14 @@ It reads the change link only from the event file, never from a shell command, a
|
|
|
673
677
|
|
|
674
678
|
To set up the build by hand instead:
|
|
675
679
|
|
|
676
|
-
| Stack | Build change
|
|
677
|
-
| -------------------------- |
|
|
678
|
-
| Vite | `share: true` in `liveLocaleVite(...)` (see [Share link from `vite build`](#share-link-from-vite-build)).
|
|
679
|
-
| CRA
|
|
680
|
-
|
|
|
681
|
-
| Next.js
|
|
682
|
-
|
|
|
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>`. |
|
|
683
688
|
|
|
684
689
|
For CRA, webpack and Rsbuild, use an explicit `&&`, not a `postbuild` script: Yarn Berry and some
|
|
685
690
|
pnpm versions skip pre/post scripts, and hosts that call the bundler directly skip them too.
|
package/dist/codemod/index.cjs
CHANGED
|
@@ -405,7 +405,8 @@ function createStudioServer(options) {
|
|
|
405
405
|
res.end("Bad Request");
|
|
406
406
|
return;
|
|
407
407
|
}
|
|
408
|
-
|
|
408
|
+
const relativePath = path2.relative(studioDir, filePath);
|
|
409
|
+
if (relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
|
|
409
410
|
res.statusCode = 403;
|
|
410
411
|
res.end("Forbidden");
|
|
411
412
|
return;
|
|
@@ -1577,7 +1578,7 @@ function parseIcu(message) {
|
|
|
1577
1578
|
parseMessage(0);
|
|
1578
1579
|
return { args, error };
|
|
1579
1580
|
}
|
|
1580
|
-
var I18NEXT_RE = /\{\{
|
|
1581
|
+
var I18NEXT_RE = /\{\{([^{}]*)\}\}/g;
|
|
1581
1582
|
var NESTING_RE = /\$t\([^)]*\)/g;
|
|
1582
1583
|
var PRINTF_RE = /%(?:\d+\$)?[-+ 0#]*\d*(?:\.\d+)?[sdifgxXoc]/g;
|
|
1583
1584
|
var TAG_RE = /<\/?([A-Za-z][\w-]*|\d+)\b[^<>]*>/g;
|
|
@@ -3041,7 +3042,12 @@ function escapeAttribute(value) {
|
|
|
3041
3042
|
}
|
|
3042
3043
|
function injectFlagScript(html, src) {
|
|
3043
3044
|
const tag = `<script src="${escapeAttribute(src)}"></script>`;
|
|
3044
|
-
|
|
3045
|
+
let cleaned = html;
|
|
3046
|
+
let previous;
|
|
3047
|
+
do {
|
|
3048
|
+
previous = cleaned;
|
|
3049
|
+
cleaned = cleaned.replace(EXISTING_FLAG_TAG, "");
|
|
3050
|
+
} while (cleaned !== previous);
|
|
3045
3051
|
for (const pattern of [HEAD_OPEN2, HTML_OPEN2, DOCTYPE2]) {
|
|
3046
3052
|
const match = pattern.exec(cleaned);
|
|
3047
3053
|
if (match) {
|
|
@@ -3057,8 +3063,8 @@ var NO_REPO_WARNING = "exposeRepo is on but no GitHub repository was found; the
|
|
|
3057
3063
|
function isPlainObject2(value) {
|
|
3058
3064
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3059
3065
|
}
|
|
3060
|
-
function toPosix(
|
|
3061
|
-
return
|
|
3066
|
+
function toPosix(relative11) {
|
|
3067
|
+
return relative11.split(path11.sep).join("/") || ".";
|
|
3062
3068
|
}
|
|
3063
3069
|
function isInside(parent, child) {
|
|
3064
3070
|
const rel = path11.relative(parent, child);
|
|
@@ -3555,11 +3561,14 @@ function setNestedProperty(obj, keyPath, value) {
|
|
|
3555
3561
|
let current = obj;
|
|
3556
3562
|
for (let i = 0; i < keys.length; i++) {
|
|
3557
3563
|
const key = keys[i];
|
|
3564
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
3565
|
+
throw new Error(`[livelocale] Forbidden key segment in path: "${keyPath}".`);
|
|
3566
|
+
}
|
|
3558
3567
|
if (i === keys.length - 1) {
|
|
3559
3568
|
current[key] = value;
|
|
3560
3569
|
return;
|
|
3561
3570
|
}
|
|
3562
|
-
if (!(key
|
|
3571
|
+
if (!Object.prototype.hasOwnProperty.call(current, key) || current[key] === null || typeof current[key] !== "object" || Array.isArray(current[key])) {
|
|
3563
3572
|
current[key] = {};
|
|
3564
3573
|
}
|
|
3565
3574
|
current = current[key];
|
|
@@ -3832,6 +3841,13 @@ var nodeInflate = async (bytes, maxOutputLength) => {
|
|
|
3832
3841
|
return result.buffer;
|
|
3833
3842
|
};
|
|
3834
3843
|
|
|
3844
|
+
// src/core/strings.ts
|
|
3845
|
+
function trimTrailingSlashes(value) {
|
|
3846
|
+
let end = value.length;
|
|
3847
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
|
|
3848
|
+
return value.slice(0, end);
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3835
3851
|
// src/server/github-rest.ts
|
|
3836
3852
|
var GitHubApiError = class extends Error {
|
|
3837
3853
|
constructor(message, status) {
|
|
@@ -3864,7 +3880,7 @@ function encodeRefPath(ref) {
|
|
|
3864
3880
|
}
|
|
3865
3881
|
function createGitHubRest(config) {
|
|
3866
3882
|
const repo = config.repo;
|
|
3867
|
-
const apiBase = (config.apiBase ?? "https://api.github.com")
|
|
3883
|
+
const apiBase = trimTrailingSlashes(config.apiBase ?? "https://api.github.com");
|
|
3868
3884
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
3869
3885
|
const owner = repo.split("/")[0] ?? "";
|
|
3870
3886
|
if (!/^[\w.-]+\/[\w.-]+$/.test(repo)) {
|
|
@@ -4293,6 +4309,7 @@ var BIDI_CONTROL_RE = /[\u202a-\u202e\u2066-\u2069]/g;
|
|
|
4293
4309
|
var NEWLINE_RE = /\r\n|\r|\n/g;
|
|
4294
4310
|
var SPAN_CONTROL_RE = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
4295
4311
|
var CONTROL_RE = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
4312
|
+
var BACKSLASH_BEFORE_PIPE_RE = /\\(?=\|)/g;
|
|
4296
4313
|
function cut(text, max) {
|
|
4297
4314
|
if (text.length <= max) return text;
|
|
4298
4315
|
let out = text.slice(0, max);
|
|
@@ -4322,7 +4339,7 @@ function codePointLabel(ch) {
|
|
|
4322
4339
|
}
|
|
4323
4340
|
function codeSpan(text, maxLength = DEFAULT_SPAN_LENGTH) {
|
|
4324
4341
|
const truncated = text.length > maxLength ? `${cut(text, maxLength)}\u2026` : text;
|
|
4325
|
-
const content = truncated.replace(NEWLINE_RE, "\u23CE").replace(BIDI_CONTROL_RE, codePointLabel).replace(SPAN_CONTROL_RE, codePointLabel).replace(/\|/g, "\\|");
|
|
4342
|
+
const content = truncated.replace(NEWLINE_RE, "\u23CE").replace(BIDI_CONTROL_RE, codePointLabel).replace(SPAN_CONTROL_RE, codePointLabel).replace(BACKSLASH_BEFORE_PIPE_RE, codePointLabel).replace(/\|/g, "\\|");
|
|
4326
4343
|
if (content === "") return "` `";
|
|
4327
4344
|
const fence = "`".repeat(longestBacktickRun(content) + 1);
|
|
4328
4345
|
const pad = /^[` ]|[` ]$/.test(content) ? " " : "";
|
|
@@ -5962,8 +5979,8 @@ function planGitignore(projectDir) {
|
|
|
5962
5979
|
if (parseGitignore(current).ignores("public/livelocale", true)) {
|
|
5963
5980
|
return { path: ".gitignore", action: "unchanged" };
|
|
5964
5981
|
}
|
|
5965
|
-
const
|
|
5966
|
-
return { path: ".gitignore", action: "update", content: `${current}${
|
|
5982
|
+
const sep9 = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
5983
|
+
return { path: ".gitignore", action: "update", content: `${current}${sep9}${SHARE_GITIGNORE}
|
|
5967
5984
|
` };
|
|
5968
5985
|
}
|
|
5969
5986
|
function planBuild(c) {
|