@babelize/next 0.0.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/README.md +126 -0
- package/dist/index.cjs +127 -0
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +102 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @babelize/next
|
|
2
|
+
|
|
3
|
+
**Next.js build plugin for Babelize** — automatically discover and pre-translate strings during `next build`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// next.config.ts
|
|
7
|
+
import withBabelize from "@babelize/next";
|
|
8
|
+
|
|
9
|
+
export default withBabelize(
|
|
10
|
+
{ /* your Next.js config */ },
|
|
11
|
+
{ locales: ["ja", "fr", "de"] },
|
|
12
|
+
);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun add @babelize/next -D
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Wrap your Next.js config
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// next.config.ts
|
|
31
|
+
import withBabelize from "@babelize/next";
|
|
32
|
+
|
|
33
|
+
const nextConfig = {
|
|
34
|
+
// your existing config
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default withBabelize(nextConfig, {
|
|
38
|
+
locales: ["ja", "fr", "de"],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Set your API key
|
|
43
|
+
|
|
44
|
+
```env
|
|
45
|
+
# .env.local
|
|
46
|
+
BABELIZE_API_KEY=blz_sk_xxxxxxxxx
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Build
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
bun run build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The plugin scans your source files during the build, translates discovered strings, and writes `babelize-lock.json` to your project root.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## How It Works
|
|
60
|
+
|
|
61
|
+
The plugin integrates with Next.js's webpack build pipeline:
|
|
62
|
+
|
|
63
|
+
1. **Scans** source files for `babelize("...")`, `babelize.p(...)`, and `<BabelizeTag>` calls
|
|
64
|
+
2. **Translates** via the Babelize API for each configured locale
|
|
65
|
+
3. **Writes** `babelize-lock.json` after the build completes
|
|
66
|
+
|
|
67
|
+
The lockfile is then consumed by `@babelize/sdk-next` at runtime:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// app/page.tsx
|
|
71
|
+
import { babelizeRSC, loadLockfile } from "@babelize/sdk-next";
|
|
72
|
+
import lockfile from "./babelize-lock.json";
|
|
73
|
+
|
|
74
|
+
loadLockfile(lockfile);
|
|
75
|
+
|
|
76
|
+
export default async function Home() {
|
|
77
|
+
return <h1>{await babelizeRSC("Welcome to Babelize")}</h1>;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Options
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
interface BabelizeNextPluginOptions {
|
|
87
|
+
apiKey?: string;
|
|
88
|
+
locales?: string[];
|
|
89
|
+
output?: string;
|
|
90
|
+
functionName?: string;
|
|
91
|
+
apiUrl?: string;
|
|
92
|
+
include?: string[];
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Option | Type | Default | Description |
|
|
97
|
+
|--------|------|---------|-------------|
|
|
98
|
+
| `locales` | `string[]` | — | Target locales to translate to |
|
|
99
|
+
| `apiKey` | `string` | `process.env.BABELIZE_API_KEY` | Babelize API key |
|
|
100
|
+
| `output` | `string` | `"babelize-lock.json"` | Output path for the lockfile |
|
|
101
|
+
| `functionName` | `string` | `"babelize"` | Function name to scan for |
|
|
102
|
+
| `apiUrl` | `string` | `"https://api.babelize.co/api"` | API base URL |
|
|
103
|
+
| `include` | `string[]` | `["src/**/*.{tsx,ts,jsx,js}", "app/**/*.{tsx,ts,jsx,js}", "pages/**/*.{tsx,ts,jsx,js}"]` | Glob patterns for source files |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Lockfile
|
|
108
|
+
|
|
109
|
+
The generated `babelize-lock.json` is identical in format to the one produced by `@babelize/vite`. See the [Vite plugin documentation](https://github.com/babelize/babelize-dashboard/tree/main/packages/sdk-plugin-vite) for the full format reference.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## TypeScript
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import type { BabelizeNextPluginOptions } from "@babelize/next";
|
|
117
|
+
|
|
118
|
+
interface BabelizeNextPluginOptions {
|
|
119
|
+
apiKey?: string;
|
|
120
|
+
locales?: string[];
|
|
121
|
+
output?: string;
|
|
122
|
+
functionName?: string;
|
|
123
|
+
apiUrl?: string;
|
|
124
|
+
include?: string[];
|
|
125
|
+
}
|
|
126
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => index_default,
|
|
24
|
+
withBabelize: () => withBabelize
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_node_fs = require("fs");
|
|
28
|
+
var import_node_path = require("path");
|
|
29
|
+
var import_glob = require("glob");
|
|
30
|
+
var DEFAULT_INCLUDE = ["src/**/*.{tsx,ts,jsx,js}", "app/**/*.{tsx,ts,jsx,js}", "pages/**/*.{tsx,ts,jsx,js}"];
|
|
31
|
+
var DEFAULT_OUTPUT = "babelize-lock.json";
|
|
32
|
+
var DEFAULT_API_URL = "https://api.babelize.co/api";
|
|
33
|
+
var BABELIZE_CALL_REGEX = /\bbabelize\s*\(\s*(["'`])((?:[^\\"']|\\.)*?)\1\s*[),]/g;
|
|
34
|
+
function scanFiles(rootDir, patterns) {
|
|
35
|
+
const strings = [];
|
|
36
|
+
for (const pattern of patterns) {
|
|
37
|
+
const files = (0, import_glob.globSync)(pattern, { cwd: rootDir, ignore: ["**/node_modules/**"] });
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
try {
|
|
40
|
+
const content = (0, import_node_fs.readFileSync)((0, import_node_path.resolve)(rootDir, file), "utf-8");
|
|
41
|
+
const matches = content.matchAll(BABELIZE_CALL_REGEX);
|
|
42
|
+
for (const match of matches) {
|
|
43
|
+
strings.push(match[2]);
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return strings;
|
|
50
|
+
}
|
|
51
|
+
var BabelizeWebpackPlugin = class {
|
|
52
|
+
constructor(rootDir, options, output) {
|
|
53
|
+
this.rootDir = rootDir;
|
|
54
|
+
this.options = options;
|
|
55
|
+
this.output = output;
|
|
56
|
+
}
|
|
57
|
+
apply(compiler) {
|
|
58
|
+
compiler.hooks.done.tapPromise("BabelizeWebpackPlugin", async () => {
|
|
59
|
+
const apiKey = this.options.apiKey ?? process.env.BABELIZE_API_KEY;
|
|
60
|
+
const locales = this.options.locales;
|
|
61
|
+
const apiUrl = this.options.apiUrl ?? DEFAULT_API_URL;
|
|
62
|
+
const include = this.options.include ?? DEFAULT_INCLUDE;
|
|
63
|
+
const strings = scanFiles(this.rootDir, include);
|
|
64
|
+
const uniqueStrings = [...new Set(strings)];
|
|
65
|
+
if (uniqueStrings.length === 0) return;
|
|
66
|
+
const translationsByLocale = {};
|
|
67
|
+
if (apiKey && locales && locales.length > 0) {
|
|
68
|
+
for (const locale of locales) {
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(`${apiUrl}/v1/translate`, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
Authorization: `Bearer ${apiKey}`
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify({ locale, strings: uniqueStrings })
|
|
77
|
+
});
|
|
78
|
+
if (response.ok) {
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
translationsByLocale[locale] = data.translations;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const lockfile = {
|
|
87
|
+
version: 1,
|
|
88
|
+
meta: {
|
|
89
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90
|
+
locales: Object.keys(translationsByLocale),
|
|
91
|
+
totalStrings: uniqueStrings.length
|
|
92
|
+
},
|
|
93
|
+
strings: Object.fromEntries(
|
|
94
|
+
uniqueStrings.map((str) => [
|
|
95
|
+
str,
|
|
96
|
+
Object.fromEntries(
|
|
97
|
+
Object.entries(translationsByLocale).map(([locale, t]) => [
|
|
98
|
+
locale,
|
|
99
|
+
t[str] ?? str
|
|
100
|
+
])
|
|
101
|
+
)
|
|
102
|
+
])
|
|
103
|
+
)
|
|
104
|
+
};
|
|
105
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.resolve)(this.rootDir, this.output), JSON.stringify(lockfile, null, 2));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
function withBabelize(nextConfig = {}, pluginOptions = {}) {
|
|
110
|
+
const output = pluginOptions.output ?? DEFAULT_OUTPUT;
|
|
111
|
+
const originalWebpack = nextConfig.webpack;
|
|
112
|
+
return {
|
|
113
|
+
...nextConfig,
|
|
114
|
+
webpack(config, options) {
|
|
115
|
+
if (!options.isServer) {
|
|
116
|
+
config.plugins.push(new BabelizeWebpackPlugin(options.dir, pluginOptions, output));
|
|
117
|
+
}
|
|
118
|
+
if (originalWebpack) return originalWebpack(config, options);
|
|
119
|
+
return config;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
var index_default = withBabelize;
|
|
124
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
125
|
+
0 && (module.exports = {
|
|
126
|
+
withBabelize
|
|
127
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface BabelizeNextPluginOptions {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
locales?: string[];
|
|
4
|
+
output?: string;
|
|
5
|
+
functionName?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
include?: string[];
|
|
8
|
+
}
|
|
9
|
+
interface NextConfig {
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
declare function withBabelize(nextConfig?: NextConfig, pluginOptions?: BabelizeNextPluginOptions): NextConfig;
|
|
13
|
+
|
|
14
|
+
export { type BabelizeNextPluginOptions, withBabelize as default, withBabelize };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface BabelizeNextPluginOptions {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
locales?: string[];
|
|
4
|
+
output?: string;
|
|
5
|
+
functionName?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
include?: string[];
|
|
8
|
+
}
|
|
9
|
+
interface NextConfig {
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
declare function withBabelize(nextConfig?: NextConfig, pluginOptions?: BabelizeNextPluginOptions): NextConfig;
|
|
13
|
+
|
|
14
|
+
export { type BabelizeNextPluginOptions, withBabelize as default, withBabelize };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import { globSync } from "glob";
|
|
5
|
+
var DEFAULT_INCLUDE = ["src/**/*.{tsx,ts,jsx,js}", "app/**/*.{tsx,ts,jsx,js}", "pages/**/*.{tsx,ts,jsx,js}"];
|
|
6
|
+
var DEFAULT_OUTPUT = "babelize-lock.json";
|
|
7
|
+
var DEFAULT_API_URL = "https://api.babelize.co/api";
|
|
8
|
+
var BABELIZE_CALL_REGEX = /\bbabelize\s*\(\s*(["'`])((?:[^\\"']|\\.)*?)\1\s*[),]/g;
|
|
9
|
+
function scanFiles(rootDir, patterns) {
|
|
10
|
+
const strings = [];
|
|
11
|
+
for (const pattern of patterns) {
|
|
12
|
+
const files = globSync(pattern, { cwd: rootDir, ignore: ["**/node_modules/**"] });
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
try {
|
|
15
|
+
const content = readFileSync(resolve(rootDir, file), "utf-8");
|
|
16
|
+
const matches = content.matchAll(BABELIZE_CALL_REGEX);
|
|
17
|
+
for (const match of matches) {
|
|
18
|
+
strings.push(match[2]);
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return strings;
|
|
25
|
+
}
|
|
26
|
+
var BabelizeWebpackPlugin = class {
|
|
27
|
+
constructor(rootDir, options, output) {
|
|
28
|
+
this.rootDir = rootDir;
|
|
29
|
+
this.options = options;
|
|
30
|
+
this.output = output;
|
|
31
|
+
}
|
|
32
|
+
apply(compiler) {
|
|
33
|
+
compiler.hooks.done.tapPromise("BabelizeWebpackPlugin", async () => {
|
|
34
|
+
const apiKey = this.options.apiKey ?? process.env.BABELIZE_API_KEY;
|
|
35
|
+
const locales = this.options.locales;
|
|
36
|
+
const apiUrl = this.options.apiUrl ?? DEFAULT_API_URL;
|
|
37
|
+
const include = this.options.include ?? DEFAULT_INCLUDE;
|
|
38
|
+
const strings = scanFiles(this.rootDir, include);
|
|
39
|
+
const uniqueStrings = [...new Set(strings)];
|
|
40
|
+
if (uniqueStrings.length === 0) return;
|
|
41
|
+
const translationsByLocale = {};
|
|
42
|
+
if (apiKey && locales && locales.length > 0) {
|
|
43
|
+
for (const locale of locales) {
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(`${apiUrl}/v1/translate`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
Authorization: `Bearer ${apiKey}`
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify({ locale, strings: uniqueStrings })
|
|
52
|
+
});
|
|
53
|
+
if (response.ok) {
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
translationsByLocale[locale] = data.translations;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const lockfile = {
|
|
62
|
+
version: 1,
|
|
63
|
+
meta: {
|
|
64
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65
|
+
locales: Object.keys(translationsByLocale),
|
|
66
|
+
totalStrings: uniqueStrings.length
|
|
67
|
+
},
|
|
68
|
+
strings: Object.fromEntries(
|
|
69
|
+
uniqueStrings.map((str) => [
|
|
70
|
+
str,
|
|
71
|
+
Object.fromEntries(
|
|
72
|
+
Object.entries(translationsByLocale).map(([locale, t]) => [
|
|
73
|
+
locale,
|
|
74
|
+
t[str] ?? str
|
|
75
|
+
])
|
|
76
|
+
)
|
|
77
|
+
])
|
|
78
|
+
)
|
|
79
|
+
};
|
|
80
|
+
writeFileSync(resolve(this.rootDir, this.output), JSON.stringify(lockfile, null, 2));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
function withBabelize(nextConfig = {}, pluginOptions = {}) {
|
|
85
|
+
const output = pluginOptions.output ?? DEFAULT_OUTPUT;
|
|
86
|
+
const originalWebpack = nextConfig.webpack;
|
|
87
|
+
return {
|
|
88
|
+
...nextConfig,
|
|
89
|
+
webpack(config, options) {
|
|
90
|
+
if (!options.isServer) {
|
|
91
|
+
config.plugins.push(new BabelizeWebpackPlugin(options.dir, pluginOptions, output));
|
|
92
|
+
}
|
|
93
|
+
if (originalWebpack) return originalWebpack(config, options);
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
var index_default = withBabelize;
|
|
99
|
+
export {
|
|
100
|
+
index_default as default,
|
|
101
|
+
withBabelize
|
|
102
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@babelize/next",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Next.js build plugin for Babelize — auto-discover strings and generate lockfile during Next.js builds",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --external next",
|
|
22
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --external next",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"next": "^14.x || ^15.x"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"glob": "^11.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"next": "^15.x",
|
|
34
|
+
"tsup": "^8.4.0",
|
|
35
|
+
"typescript": "^5.7.3"
|
|
36
|
+
}
|
|
37
|
+
}
|