@babelize/sdk 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 ADDED
@@ -0,0 +1,333 @@
1
+ # @babelize/sdk
2
+
3
+ **Core runtime for Babelize** — wrap strings, translate, cache, done. No translation keys, no locale files, no configuration.
4
+
5
+ ```ts
6
+ import { babelize } from "@babelize/sdk";
7
+
8
+ babelize.init(process.env.BABELIZE_API_KEY);
9
+
10
+ babelize("Welcome"); // "ようこそ"
11
+ babelize("Hello {name}", { name }); // "こんにちは Mohit"
12
+ babelize.p("{count} item", "{count} items", 5); // "5 items"
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ bun add @babelize/sdk
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Quick Start
26
+
27
+ ```ts
28
+ import { babelize } from "@babelize/sdk";
29
+
30
+ // 1. Initialize
31
+ babelize.init({
32
+ apiKey: process.env.BABELIZE_API_KEY,
33
+ locale: "ja",
34
+ });
35
+
36
+ // 2. Load production lockfile (generated at build time)
37
+ import lockfile from "virtual:babelize-lockfile";
38
+ lockfile && babelize.loadLockfile(lockfile);
39
+
40
+ // 3. Get translations
41
+ babelize("Welcome"); // "バビリゾーンへようこそ"
42
+ babelize("Hello {name}", { name: "World" }); // "こんにちは World"
43
+
44
+ // 4. Switch locale
45
+ babelize.setLocale("fr");
46
+ babelize("Welcome"); // "Bienvenue à Babelize"
47
+
48
+ // 5. Get current locale
49
+ const locale = babelize.getLocale(); // "fr"
50
+ ```
51
+
52
+ ### With React
53
+
54
+ ```tsx
55
+ import { BabelizeProvider, useBabelize, useLocale } from "@babelize/sdk-react";
56
+
57
+ // Wrap your app
58
+ root.render(
59
+ <BabelizeProvider>
60
+ <App />
61
+ </BabelizeProvider>,
62
+ );
63
+ ```
64
+
65
+ ---
66
+
67
+ ## API
68
+
69
+ ### `babelize(str, vars?)`
70
+
71
+ Translate a string into the current locale.
72
+
73
+ ```ts
74
+ babelize("Welcome"); // "ようこそ"
75
+ babelize("Hello {name}", { name: "Mohit" }); // "こんにちは Mohit"
76
+ babelize("You have {count} messages", { count: 5 }); // "5件のメッセージがあります"
77
+ ```
78
+
79
+ - Returns the translated string from cache or lockfile
80
+ - Returns the original string if no translation is found
81
+ - Background API call is queued if the string is not cached
82
+ - `{key}` placeholders are interpolated with `vars`
83
+
84
+ ### `babelize.init(config)`
85
+
86
+ Initialize the SDK. Accepts an API key string or a config object.
87
+
88
+ ```ts
89
+ // With API key string
90
+ babelize.init("blz_sk_...");
91
+
92
+ // With config object
93
+ babelize.init({
94
+ apiKey: "blz_sk_...",
95
+ locale: "ja",
96
+ fallbackLocale: "en",
97
+ apiUrl: "https://api.babelize.co/api",
98
+ });
99
+ ```
100
+
101
+ **Options:**
102
+
103
+ | Option | Type | Default | Description |
104
+ |--------|------|---------|-------------|
105
+ | `apiKey` | `string` | — | Babelize API key (optional in production) |
106
+ | `locale` | `string` | auto-detected | Initial locale |
107
+ | `fallbackLocale` | `string` | `"en"` | Fallback when auto-detection fails |
108
+ | `apiUrl` | `string` | `"https://api.babelize.co/api"` | API endpoint |
109
+
110
+ ### `babelize.setLocale(locale)`
111
+
112
+ Switch the active locale. All subsequent `babelize()` calls use this locale. Triggers re-render in React components.
113
+
114
+ ```ts
115
+ babelize.setLocale("fr");
116
+ babelize.getLocale(); // "fr"
117
+ ```
118
+
119
+ ### `babelize.getLocale()`
120
+
121
+ Returns the currently active locale.
122
+
123
+ ```ts
124
+ const current = babelize.getLocale();
125
+ ```
126
+
127
+ ### `babelize.p(singular, plural, countOrVars)`
128
+
129
+ Simple two-form pluralization. Uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to select the correct form for the current locale.
130
+
131
+ ```ts
132
+ // With count as number
133
+ babelize.p("{count} item", "{count} items", 1); // "1 item" (one)
134
+ babelize.p("{count} item", "{count} items", 5); // "5 items" (other)
135
+
136
+ // With vars object (count is extracted)
137
+ babelize.p("{count} item", "{count} items", { count: items.length });
138
+
139
+ // Polish automatically selects "few" for 2-4, "many" for 5+
140
+ babelize.setLocale("pl");
141
+ babelize.p("{count} element", "{count} elementów", 2); // "2 elementy" (few)
142
+ babelize.p("{count} element", "{count} elementów", 5); // "5 elementów" (many)
143
+ ```
144
+
145
+ ### `babelize.plural(forms, vars?)`
146
+
147
+ Explicit CLDR plural forms for full control. Supports all 6 CLDR forms: `zero`, `one`, `two`, `few`, `many`, `other`.
148
+
149
+ ```ts
150
+ babelize.plural(
151
+ {
152
+ one: "{count} item",
153
+ other: "{count} items",
154
+ },
155
+ { count: 5 },
156
+ );
157
+
158
+ // Arabic has 6 forms
159
+ babelize.setLocale("ar");
160
+ babelize.plural(
161
+ {
162
+ zero: "لا {count} عناصر",
163
+ one: "{count} عنصر",
164
+ two: "{count} عنصرين",
165
+ few: "{count} عناصر",
166
+ many: "عنصر {count}",
167
+ other: "{count} عنصرًا",
168
+ },
169
+ { count: 0 }, // zero
170
+ );
171
+ ```
172
+
173
+ ### `babelize.tag(template)`
174
+
175
+ Translate a template string with segment markers as a single unit. The full sentence is sent to the translator, so word order and grammar are correct across segments.
176
+
177
+ ```ts
178
+ const result = babelize.tag("Welcome to {Babelize}, a platform {for localization}");
179
+ // result.parts = [
180
+ // { type: 'text', value: 'ローカリゼーションのための' },
181
+ // { type: 'segment', value: 'for localization' },
182
+ // { type: 'text', value: 'プラットフォーム、' },
183
+ // { type: 'segment', value: 'Babelize' },
184
+ // { type: 'text', value: 'へようこそ' },
185
+ // ]
186
+
187
+ // Map segments to React elements
188
+ const segments = {
189
+ Babelize: <strong>Babelize</strong>,
190
+ "for localization": <em>for localization</em>,
191
+ };
192
+ const rendered = result.parts.map(p =>
193
+ p.type === 'segment' ? segments[p.value] ?? p.value : p.value,
194
+ );
195
+ ```
196
+
197
+ ### `babelize.loadLockfile(data)`
198
+
199
+ Load a pre-generated lockfile into the translation cache. The lockfile is generated at build time by the Vite plugin.
200
+
201
+ ```ts
202
+ import lockfile from "virtual:babelize-lockfile";
203
+ lockfile && babelize.loadLockfile(lockfile);
204
+ ```
205
+
206
+ ### `babelize.subscribe(cb)`
207
+
208
+ Subscribe to state changes (locale switch, cache update). Returns an unsubscribe function.
209
+
210
+ ```ts
211
+ const unsubscribe = babelize.subscribe(() => {
212
+ console.log("Locale or translation cache updated");
213
+ });
214
+
215
+ // Later
216
+ unsubscribe();
217
+ ```
218
+
219
+ ### `babelize.getVersion()`
220
+
221
+ Returns a number that increments on every state change. Useful with `useSyncExternalStore` for reactivity.
222
+
223
+ ```ts
224
+ const version = babelize.getVersion();
225
+ ```
226
+
227
+ ### `babelize.reset()`
228
+
229
+ Clear all state — cache, locale, subscribers, API client. Useful in testing.
230
+
231
+ ```ts
232
+ babelize.reset();
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Lockfile Format
238
+
239
+ The lockfile is generated at build time by `@babelize/vite` or `@babelize/next`. In production, translations are read from the lockfile — zero API calls.
240
+
241
+ ```json
242
+ {
243
+ "version": 1,
244
+ "meta": {
245
+ "generatedAt": "2026-07-30T12:00:00Z",
246
+ "locales": ["ja", "fr", "de"],
247
+ "totalStrings": 42
248
+ },
249
+ "strings": {
250
+ "Welcome": {
251
+ "ja": "バビリゾーンへようこそ",
252
+ "fr": "Bienvenue à Babelize",
253
+ "de": "Willkommen bei Babelize"
254
+ },
255
+ "Hello {name}": {
256
+ "ja": "こんにちは {name}",
257
+ "fr": "Bonjour {name}",
258
+ "de": "Hallo {name}"
259
+ }
260
+ },
261
+ "plurals": {
262
+ "{count} item": {
263
+ "forms": ["one", "other"],
264
+ "ja": { "other": "{count} 項目" },
265
+ "fr": { "one": "{count} élément", "other": "{count} éléments" },
266
+ "de": { "one": "{count} Gegenstand", "other": "{count} Gegenstände" }
267
+ }
268
+ }
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Cache Strategy
275
+
276
+ ```
277
+ Memory Cache < 0.1ms
278
+ ↓ (miss)
279
+ Lockfile Cache < 1ms
280
+ ↓ (miss)
281
+ Babelize API < 100ms
282
+ ↓ (miss)
283
+ Original string (fallback)
284
+ ```
285
+
286
+ - Memory cache is checked first for every `babelize()` call
287
+ - Lockfile cache is loaded at startup from the build-time lockfile
288
+ - API calls are batched via `queueMicrotask` — all strings from one render cycle are sent as a single request
289
+ - Once a translation is returned by the API, it's cached in memory and never fetched again
290
+ - Production builds read from lockfile only — zero API calls at runtime
291
+
292
+ ---
293
+
294
+ ## TypeScript
295
+
296
+ ```ts
297
+ import type { BabelizeConfig, LockfileData, PluralForms, TagResult, TagPart } from "@babelize/sdk";
298
+
299
+ interface BabelizeConfig {
300
+ apiKey?: string;
301
+ locale?: string;
302
+ fallbackLocale?: string;
303
+ apiUrl?: string;
304
+ storage?: TranslationStorage;
305
+ }
306
+
307
+ interface LockfileData {
308
+ version: number;
309
+ strings: Record<string, Record<string, string>>;
310
+ plurals?: Record<string, {
311
+ forms: string[];
312
+ [locale: string]: Record<string, string> | string[];
313
+ }>;
314
+ }
315
+
316
+ type PluralForms = {
317
+ zero?: string;
318
+ one?: string;
319
+ two?: string;
320
+ few?: string;
321
+ many?: string;
322
+ other: string;
323
+ };
324
+
325
+ interface TagResult {
326
+ parts: TagPart[];
327
+ }
328
+
329
+ interface TagPart {
330
+ type: "text" | "segment";
331
+ value: string;
332
+ }
333
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,451 @@
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
+ babelize: () => babelize
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/storage/JsonStorage.ts
28
+ var JsonStorage = class {
29
+ constructor() {
30
+ this.data = null;
31
+ }
32
+ load(data) {
33
+ this.data = data;
34
+ }
35
+ get(locale, key) {
36
+ if (!this.data) return null;
37
+ const translations = this.data.strings[key];
38
+ if (!translations) return null;
39
+ return translations[locale] ?? null;
40
+ }
41
+ };
42
+
43
+ // src/cache/Cache.ts
44
+ var Cache = class {
45
+ constructor() {
46
+ this.memory = /* @__PURE__ */ new Map();
47
+ this.pluralMemory = /* @__PURE__ */ new Map();
48
+ this.pluralLockfile = /* @__PURE__ */ new Map();
49
+ this.lockfile = new JsonStorage();
50
+ }
51
+ loadLockfile(data) {
52
+ this.lockfile.load(data);
53
+ if (data.plurals) {
54
+ for (const [canonical, localeData] of Object.entries(data.plurals)) {
55
+ for (const [locale, forms] of Object.entries(localeData)) {
56
+ if (locale === "forms") continue;
57
+ const formMap = forms;
58
+ let localePlurals = this.pluralLockfile.get(locale);
59
+ if (!localePlurals) {
60
+ localePlurals = /* @__PURE__ */ new Map();
61
+ this.pluralLockfile.set(locale, localePlurals);
62
+ }
63
+ localePlurals.set(canonical, formMap);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ get(locale, str) {
69
+ const localeCache = this.memory.get(locale);
70
+ if (localeCache?.has(str)) {
71
+ return localeCache.get(str);
72
+ }
73
+ const fromLockfile = this.lockfile.get(locale, str);
74
+ if (fromLockfile !== null) {
75
+ this.setMemory(locale, str, fromLockfile);
76
+ return fromLockfile;
77
+ }
78
+ return null;
79
+ }
80
+ set(locale, str, translation) {
81
+ this.setMemory(locale, str, translation);
82
+ }
83
+ getPlural(locale, key, form) {
84
+ const localePluralMemory = this.pluralMemory.get(locale);
85
+ if (localePluralMemory?.get(key)?.has(form)) {
86
+ return localePluralMemory.get(key).get(form);
87
+ }
88
+ const localePluralLockfile = this.pluralLockfile.get(locale);
89
+ if (localePluralLockfile?.has(key)) {
90
+ const forms = localePluralLockfile.get(key);
91
+ const translation = forms[form] ?? null;
92
+ if (translation !== null) {
93
+ let keyMemory = this.pluralMemory.get(locale);
94
+ if (!keyMemory) {
95
+ keyMemory = /* @__PURE__ */ new Map();
96
+ this.pluralMemory.set(locale, keyMemory);
97
+ }
98
+ let formMemory = keyMemory.get(key);
99
+ if (!formMemory) {
100
+ formMemory = /* @__PURE__ */ new Map();
101
+ keyMemory.set(key, formMemory);
102
+ }
103
+ formMemory.set(form, translation);
104
+ return translation;
105
+ }
106
+ }
107
+ return null;
108
+ }
109
+ setPlural(locale, key, form, translation) {
110
+ let localeMemory = this.pluralMemory.get(locale);
111
+ if (!localeMemory) {
112
+ localeMemory = /* @__PURE__ */ new Map();
113
+ this.pluralMemory.set(locale, localeMemory);
114
+ }
115
+ let keyMemory = localeMemory.get(key);
116
+ if (!keyMemory) {
117
+ keyMemory = /* @__PURE__ */ new Map();
118
+ localeMemory.set(key, keyMemory);
119
+ }
120
+ keyMemory.set(form, translation);
121
+ }
122
+ hasPlural(locale, key, form) {
123
+ if (this.pluralMemory.get(locale)?.get(key)?.has(form)) return true;
124
+ const lockfileForm = this.pluralLockfile.get(locale)?.get(key)?.[form];
125
+ return lockfileForm != null;
126
+ }
127
+ setMemory(locale, str, translation) {
128
+ let localeCache = this.memory.get(locale);
129
+ if (!localeCache) {
130
+ localeCache = /* @__PURE__ */ new Map();
131
+ this.memory.set(locale, localeCache);
132
+ }
133
+ localeCache.set(str, translation);
134
+ }
135
+ has(locale, str) {
136
+ return this.memory.get(locale)?.has(str) ?? this.lockfile.get(locale, str) !== null;
137
+ }
138
+ };
139
+
140
+ // src/api/client.ts
141
+ var DEFAULT_API_URL = "https://api.babelize.co/api";
142
+ var ApiClient = class {
143
+ constructor(apiKey, apiUrl = DEFAULT_API_URL) {
144
+ this.apiKey = apiKey;
145
+ this.apiUrl = apiUrl;
146
+ }
147
+ async translate(locale, strings, plurals) {
148
+ if (strings.length === 0 && !plurals?.length) return { translations: {} };
149
+ const response = await fetch(`${this.apiUrl}/v1/translate`, {
150
+ method: "POST",
151
+ headers: {
152
+ "Content-Type": "application/json",
153
+ Authorization: `Bearer ${this.apiKey}`
154
+ },
155
+ body: JSON.stringify({ locale, strings, plurals })
156
+ });
157
+ if (!response.ok) {
158
+ throw new Error(`Translation API error: ${response.status}`);
159
+ }
160
+ return response.json();
161
+ }
162
+ };
163
+
164
+ // src/batching/Batcher.ts
165
+ var Batcher = class {
166
+ constructor(flushFn) {
167
+ this.pending = /* @__PURE__ */ new Map();
168
+ this.pendingPlurals = /* @__PURE__ */ new Map();
169
+ this.scheduled = false;
170
+ this.flushFn = flushFn;
171
+ }
172
+ add(locale, str) {
173
+ if (!this.pending.has(locale)) {
174
+ this.pending.set(locale, /* @__PURE__ */ new Set());
175
+ }
176
+ this.pending.get(locale).add(str);
177
+ if (!this.scheduled) {
178
+ this.scheduled = true;
179
+ queueMicrotask(() => this.flush());
180
+ }
181
+ }
182
+ addPlural(locale, canonical, forms, templates) {
183
+ if (!this.pendingPlurals.has(locale)) {
184
+ this.pendingPlurals.set(locale, []);
185
+ }
186
+ this.pendingPlurals.get(locale).push({ canonical, forms, templates });
187
+ if (!this.scheduled) {
188
+ this.scheduled = true;
189
+ queueMicrotask(() => this.flush());
190
+ }
191
+ }
192
+ async flush() {
193
+ this.scheduled = false;
194
+ const batch = this.pending;
195
+ const batchPlurals = this.pendingPlurals;
196
+ this.pending = /* @__PURE__ */ new Map();
197
+ this.pendingPlurals = /* @__PURE__ */ new Map();
198
+ const promises = [];
199
+ for (const [locale, strings] of batch) {
200
+ const plurals = batchPlurals.get(locale);
201
+ if (strings.size === 0 && !plurals?.length) continue;
202
+ promises.push(this.flushFn(locale, [...strings], plurals));
203
+ }
204
+ for (const [locale, plurals] of batchPlurals) {
205
+ if (batch.has(locale)) continue;
206
+ if (!plurals.length) continue;
207
+ promises.push(this.flushFn(locale, [], plurals));
208
+ }
209
+ await Promise.all(promises);
210
+ }
211
+ };
212
+
213
+ // src/locale/detect.ts
214
+ function browserDetector() {
215
+ if (typeof navigator === "undefined") return null;
216
+ return navigator.language?.split("-")[0] ?? null;
217
+ }
218
+ function createDetectionChain(detectors) {
219
+ return () => {
220
+ for (const detector of detectors) {
221
+ const result = detector();
222
+ if (result !== null) return result;
223
+ }
224
+ return null;
225
+ };
226
+ }
227
+
228
+ // src/utils/interpolate.ts
229
+ function interpolate(template, vars) {
230
+ if (!vars) return template;
231
+ return template.replace(/\{(\w+)\}/g, (_, key) => {
232
+ const value = vars[key];
233
+ return value !== void 0 ? String(value) : `{${key}}`;
234
+ });
235
+ }
236
+
237
+ // src/Babelize.ts
238
+ var DEFAULT_FALLBACK_LOCALE = "en";
239
+ var DEFAULT_API_URL2 = "https://api.babelize.co/api";
240
+ var Babelize = class {
241
+ constructor() {
242
+ this.initialized = false;
243
+ this.apiKey = "";
244
+ this.apiUrl = DEFAULT_API_URL2;
245
+ this.apiClient = null;
246
+ this.batcher = null;
247
+ this._locale = DEFAULT_FALLBACK_LOCALE;
248
+ this.fallbackLocale = DEFAULT_FALLBACK_LOCALE;
249
+ this.subscribers = /* @__PURE__ */ new Set();
250
+ this._version = 0;
251
+ this.cache = new Cache();
252
+ this.detectLocale = createDetectionChain([browserDetector]);
253
+ this.batcher = new Batcher(async (locale, strings, plurals) => {
254
+ await this.flushBatch(locale, strings, plurals);
255
+ });
256
+ }
257
+ init(config) {
258
+ if (this.initialized) return;
259
+ if (typeof config === "string") {
260
+ config = { apiKey: config };
261
+ }
262
+ const resolved = config ?? {};
263
+ this.apiKey = resolved.apiKey ?? "";
264
+ this.apiUrl = resolved.apiUrl ?? DEFAULT_API_URL2;
265
+ this.fallbackLocale = resolved.fallbackLocale ?? DEFAULT_FALLBACK_LOCALE;
266
+ if (resolved.locale) {
267
+ this._locale = resolved.locale;
268
+ } else {
269
+ const detected = this.detectLocale();
270
+ this._locale = detected ?? this.fallbackLocale;
271
+ }
272
+ if (this.apiKey) {
273
+ this.apiClient = new ApiClient(this.apiKey, this.apiUrl);
274
+ }
275
+ this.initialized = true;
276
+ }
277
+ reset() {
278
+ this.initialized = false;
279
+ this.apiKey = "";
280
+ this.apiClient = null;
281
+ this.cache = new Cache();
282
+ this._locale = DEFAULT_FALLBACK_LOCALE;
283
+ this.fallbackLocale = DEFAULT_FALLBACK_LOCALE;
284
+ this.subscribers.clear();
285
+ }
286
+ loadLockfile(data) {
287
+ this.cache.loadLockfile(data);
288
+ }
289
+ t(str, vars) {
290
+ const cached = this.cache.get(this._locale, str);
291
+ if (cached !== null) {
292
+ return interpolate(cached, vars);
293
+ }
294
+ if (this.apiClient && this.batcher) {
295
+ this.batcher.add(this._locale, str);
296
+ }
297
+ return interpolate(str, vars);
298
+ }
299
+ p(singular, plural, countOrVars) {
300
+ let count;
301
+ let vars;
302
+ if (typeof countOrVars === "number") {
303
+ count = countOrVars;
304
+ vars = { count };
305
+ } else {
306
+ count = countOrVars.count ?? 0;
307
+ vars = countOrVars;
308
+ }
309
+ const rules = new Intl.PluralRules(this._locale);
310
+ const form = rules.select(count);
311
+ const key = singular;
312
+ const cached = this.cache.getPlural(this._locale, key, form);
313
+ if (cached !== null) {
314
+ return interpolate(cached, vars);
315
+ }
316
+ if (this.apiClient && this.batcher) {
317
+ this.batcher.addPlural(this._locale, key, ["one", "other"], [singular, plural]);
318
+ }
319
+ const sourceForm = form === "one" ? singular : plural;
320
+ return interpolate(sourceForm, vars);
321
+ }
322
+ plural(forms, vars) {
323
+ const count = vars?.count ?? 0;
324
+ const rules = new Intl.PluralRules(this._locale);
325
+ const form = rules.select(count);
326
+ const key = forms.other;
327
+ const cached = this.cache.getPlural(this._locale, key, form);
328
+ if (cached !== null) {
329
+ return interpolate(cached, vars);
330
+ }
331
+ if (this.apiClient && this.batcher) {
332
+ const entries = Object.entries(forms);
333
+ const formNames = entries.map(([f]) => f);
334
+ const templates = entries.map(([, t]) => t);
335
+ this.batcher.addPlural(this._locale, key, formNames, templates);
336
+ }
337
+ const sourceForm = forms[form] ?? forms.other;
338
+ return interpolate(sourceForm, vars);
339
+ }
340
+ tag(template) {
341
+ const translated = this.t(template);
342
+ const parts = [];
343
+ const markerRegex = /\{([^}]+)\}/g;
344
+ let lastIndex = 0;
345
+ let match;
346
+ while ((match = markerRegex.exec(translated)) !== null) {
347
+ if (match.index > lastIndex) {
348
+ parts.push({ type: "text", value: translated.slice(lastIndex, match.index) });
349
+ }
350
+ parts.push({ type: "segment", value: match[1] });
351
+ lastIndex = match.index + match[0].length;
352
+ }
353
+ if (lastIndex < translated.length) {
354
+ parts.push({ type: "text", value: translated.slice(lastIndex) });
355
+ }
356
+ return { parts };
357
+ }
358
+ getVersion() {
359
+ return this._version;
360
+ }
361
+ setLocale(locale) {
362
+ if (locale === this._locale) return;
363
+ this._locale = locale;
364
+ this._version++;
365
+ this.subscribers.forEach((cb) => cb());
366
+ }
367
+ getLocale() {
368
+ return this._locale;
369
+ }
370
+ subscribe(cb) {
371
+ this.subscribers.add(cb);
372
+ return () => {
373
+ this.subscribers.delete(cb);
374
+ };
375
+ }
376
+ async flushBatch(locale, strings, plurals) {
377
+ if (!this.apiClient) return;
378
+ if (strings.length === 0 && !plurals?.length) return;
379
+ const uncached = strings.filter((s) => !this.cache.has(locale, s));
380
+ if (uncached.length === 0 && !plurals?.length) return;
381
+ try {
382
+ const { translations, plurals: pluralTranslations } = await this.apiClient.translate(
383
+ locale,
384
+ uncached,
385
+ plurals
386
+ );
387
+ let updated = false;
388
+ for (const [source, translation] of Object.entries(translations)) {
389
+ if (translation && translation !== source) {
390
+ this.cache.set(locale, source, translation);
391
+ updated = true;
392
+ }
393
+ }
394
+ if (pluralTranslations) {
395
+ for (const [canonical, forms] of Object.entries(pluralTranslations)) {
396
+ for (const [form, translation] of Object.entries(forms)) {
397
+ if (translation) {
398
+ this.cache.setPlural(locale, canonical, form, translation);
399
+ updated = true;
400
+ }
401
+ }
402
+ }
403
+ }
404
+ if (updated) {
405
+ this._version++;
406
+ this.subscribers.forEach((cb) => cb());
407
+ }
408
+ } catch {
409
+ }
410
+ }
411
+ };
412
+
413
+ // src/index.ts
414
+ var _babelize = new Babelize();
415
+ function babelize(str, vars) {
416
+ return _babelize.t(str, vars);
417
+ }
418
+ babelize.init = (config) => {
419
+ _babelize.init(config);
420
+ };
421
+ babelize.reset = () => {
422
+ _babelize.reset();
423
+ };
424
+ babelize.setLocale = (locale) => {
425
+ _babelize.setLocale(locale);
426
+ };
427
+ babelize.getLocale = () => {
428
+ return _babelize.getLocale();
429
+ };
430
+ babelize.loadLockfile = (data) => {
431
+ _babelize.loadLockfile(data);
432
+ };
433
+ babelize.subscribe = (cb) => {
434
+ return _babelize.subscribe(cb);
435
+ };
436
+ babelize.getVersion = () => {
437
+ return _babelize.getVersion();
438
+ };
439
+ babelize.tag = (template) => {
440
+ return _babelize.tag(template);
441
+ };
442
+ babelize.p = (singular, plural, countOrVars) => {
443
+ return _babelize.p(singular, plural, countOrVars);
444
+ };
445
+ babelize.plural = (forms, vars) => {
446
+ return _babelize.plural(forms, vars);
447
+ };
448
+ // Annotate the CommonJS export names for ESM import in node:
449
+ 0 && (module.exports = {
450
+ babelize
451
+ });
@@ -0,0 +1,47 @@
1
+ interface BabelizeConfig {
2
+ apiKey?: string;
3
+ locale?: string;
4
+ fallbackLocale?: string;
5
+ apiUrl?: string;
6
+ storage?: TranslationStorage;
7
+ }
8
+ interface TranslationStorage {
9
+ get(locale: string, key: string): string | null;
10
+ }
11
+ type PluralForm = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
12
+ type PluralForms = {
13
+ [form in PluralForm]?: string;
14
+ } & {
15
+ other: string;
16
+ };
17
+ interface LockfileData {
18
+ version: number;
19
+ strings: Record<string, Record<string, string>>;
20
+ plurals?: Record<string, {
21
+ forms: string[];
22
+ [locale: string]: Record<string, string> | string[];
23
+ }>;
24
+ }
25
+ interface TagPart {
26
+ type: 'text' | 'segment';
27
+ value: string;
28
+ }
29
+ interface TagResult {
30
+ parts: TagPart[];
31
+ }
32
+
33
+ declare function babelize(str: string, vars?: Record<string, string | number>): string;
34
+ declare namespace babelize {
35
+ var init: (config?: string | BabelizeConfig) => void;
36
+ var reset: () => void;
37
+ var setLocale: (locale: string) => void;
38
+ var getLocale: () => string;
39
+ var loadLockfile: (data: LockfileData) => void;
40
+ var subscribe: (cb: () => void) => (() => void);
41
+ var getVersion: () => number;
42
+ var tag: (template: string) => TagResult;
43
+ var p: (singular: string, plural: string, countOrVars: number | Record<string, string | number>) => string;
44
+ var plural: (forms: PluralForms, vars?: Record<string, string | number>) => string;
45
+ }
46
+
47
+ export { type BabelizeConfig, type LockfileData, type PluralForm, type PluralForms, type TagPart, type TagResult, babelize };
@@ -0,0 +1,47 @@
1
+ interface BabelizeConfig {
2
+ apiKey?: string;
3
+ locale?: string;
4
+ fallbackLocale?: string;
5
+ apiUrl?: string;
6
+ storage?: TranslationStorage;
7
+ }
8
+ interface TranslationStorage {
9
+ get(locale: string, key: string): string | null;
10
+ }
11
+ type PluralForm = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
12
+ type PluralForms = {
13
+ [form in PluralForm]?: string;
14
+ } & {
15
+ other: string;
16
+ };
17
+ interface LockfileData {
18
+ version: number;
19
+ strings: Record<string, Record<string, string>>;
20
+ plurals?: Record<string, {
21
+ forms: string[];
22
+ [locale: string]: Record<string, string> | string[];
23
+ }>;
24
+ }
25
+ interface TagPart {
26
+ type: 'text' | 'segment';
27
+ value: string;
28
+ }
29
+ interface TagResult {
30
+ parts: TagPart[];
31
+ }
32
+
33
+ declare function babelize(str: string, vars?: Record<string, string | number>): string;
34
+ declare namespace babelize {
35
+ var init: (config?: string | BabelizeConfig) => void;
36
+ var reset: () => void;
37
+ var setLocale: (locale: string) => void;
38
+ var getLocale: () => string;
39
+ var loadLockfile: (data: LockfileData) => void;
40
+ var subscribe: (cb: () => void) => (() => void);
41
+ var getVersion: () => number;
42
+ var tag: (template: string) => TagResult;
43
+ var p: (singular: string, plural: string, countOrVars: number | Record<string, string | number>) => string;
44
+ var plural: (forms: PluralForms, vars?: Record<string, string | number>) => string;
45
+ }
46
+
47
+ export { type BabelizeConfig, type LockfileData, type PluralForm, type PluralForms, type TagPart, type TagResult, babelize };
package/dist/index.js ADDED
@@ -0,0 +1,424 @@
1
+ // src/storage/JsonStorage.ts
2
+ var JsonStorage = class {
3
+ constructor() {
4
+ this.data = null;
5
+ }
6
+ load(data) {
7
+ this.data = data;
8
+ }
9
+ get(locale, key) {
10
+ if (!this.data) return null;
11
+ const translations = this.data.strings[key];
12
+ if (!translations) return null;
13
+ return translations[locale] ?? null;
14
+ }
15
+ };
16
+
17
+ // src/cache/Cache.ts
18
+ var Cache = class {
19
+ constructor() {
20
+ this.memory = /* @__PURE__ */ new Map();
21
+ this.pluralMemory = /* @__PURE__ */ new Map();
22
+ this.pluralLockfile = /* @__PURE__ */ new Map();
23
+ this.lockfile = new JsonStorage();
24
+ }
25
+ loadLockfile(data) {
26
+ this.lockfile.load(data);
27
+ if (data.plurals) {
28
+ for (const [canonical, localeData] of Object.entries(data.plurals)) {
29
+ for (const [locale, forms] of Object.entries(localeData)) {
30
+ if (locale === "forms") continue;
31
+ const formMap = forms;
32
+ let localePlurals = this.pluralLockfile.get(locale);
33
+ if (!localePlurals) {
34
+ localePlurals = /* @__PURE__ */ new Map();
35
+ this.pluralLockfile.set(locale, localePlurals);
36
+ }
37
+ localePlurals.set(canonical, formMap);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ get(locale, str) {
43
+ const localeCache = this.memory.get(locale);
44
+ if (localeCache?.has(str)) {
45
+ return localeCache.get(str);
46
+ }
47
+ const fromLockfile = this.lockfile.get(locale, str);
48
+ if (fromLockfile !== null) {
49
+ this.setMemory(locale, str, fromLockfile);
50
+ return fromLockfile;
51
+ }
52
+ return null;
53
+ }
54
+ set(locale, str, translation) {
55
+ this.setMemory(locale, str, translation);
56
+ }
57
+ getPlural(locale, key, form) {
58
+ const localePluralMemory = this.pluralMemory.get(locale);
59
+ if (localePluralMemory?.get(key)?.has(form)) {
60
+ return localePluralMemory.get(key).get(form);
61
+ }
62
+ const localePluralLockfile = this.pluralLockfile.get(locale);
63
+ if (localePluralLockfile?.has(key)) {
64
+ const forms = localePluralLockfile.get(key);
65
+ const translation = forms[form] ?? null;
66
+ if (translation !== null) {
67
+ let keyMemory = this.pluralMemory.get(locale);
68
+ if (!keyMemory) {
69
+ keyMemory = /* @__PURE__ */ new Map();
70
+ this.pluralMemory.set(locale, keyMemory);
71
+ }
72
+ let formMemory = keyMemory.get(key);
73
+ if (!formMemory) {
74
+ formMemory = /* @__PURE__ */ new Map();
75
+ keyMemory.set(key, formMemory);
76
+ }
77
+ formMemory.set(form, translation);
78
+ return translation;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ setPlural(locale, key, form, translation) {
84
+ let localeMemory = this.pluralMemory.get(locale);
85
+ if (!localeMemory) {
86
+ localeMemory = /* @__PURE__ */ new Map();
87
+ this.pluralMemory.set(locale, localeMemory);
88
+ }
89
+ let keyMemory = localeMemory.get(key);
90
+ if (!keyMemory) {
91
+ keyMemory = /* @__PURE__ */ new Map();
92
+ localeMemory.set(key, keyMemory);
93
+ }
94
+ keyMemory.set(form, translation);
95
+ }
96
+ hasPlural(locale, key, form) {
97
+ if (this.pluralMemory.get(locale)?.get(key)?.has(form)) return true;
98
+ const lockfileForm = this.pluralLockfile.get(locale)?.get(key)?.[form];
99
+ return lockfileForm != null;
100
+ }
101
+ setMemory(locale, str, translation) {
102
+ let localeCache = this.memory.get(locale);
103
+ if (!localeCache) {
104
+ localeCache = /* @__PURE__ */ new Map();
105
+ this.memory.set(locale, localeCache);
106
+ }
107
+ localeCache.set(str, translation);
108
+ }
109
+ has(locale, str) {
110
+ return this.memory.get(locale)?.has(str) ?? this.lockfile.get(locale, str) !== null;
111
+ }
112
+ };
113
+
114
+ // src/api/client.ts
115
+ var DEFAULT_API_URL = "https://api.babelize.co/api";
116
+ var ApiClient = class {
117
+ constructor(apiKey, apiUrl = DEFAULT_API_URL) {
118
+ this.apiKey = apiKey;
119
+ this.apiUrl = apiUrl;
120
+ }
121
+ async translate(locale, strings, plurals) {
122
+ if (strings.length === 0 && !plurals?.length) return { translations: {} };
123
+ const response = await fetch(`${this.apiUrl}/v1/translate`, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ Authorization: `Bearer ${this.apiKey}`
128
+ },
129
+ body: JSON.stringify({ locale, strings, plurals })
130
+ });
131
+ if (!response.ok) {
132
+ throw new Error(`Translation API error: ${response.status}`);
133
+ }
134
+ return response.json();
135
+ }
136
+ };
137
+
138
+ // src/batching/Batcher.ts
139
+ var Batcher = class {
140
+ constructor(flushFn) {
141
+ this.pending = /* @__PURE__ */ new Map();
142
+ this.pendingPlurals = /* @__PURE__ */ new Map();
143
+ this.scheduled = false;
144
+ this.flushFn = flushFn;
145
+ }
146
+ add(locale, str) {
147
+ if (!this.pending.has(locale)) {
148
+ this.pending.set(locale, /* @__PURE__ */ new Set());
149
+ }
150
+ this.pending.get(locale).add(str);
151
+ if (!this.scheduled) {
152
+ this.scheduled = true;
153
+ queueMicrotask(() => this.flush());
154
+ }
155
+ }
156
+ addPlural(locale, canonical, forms, templates) {
157
+ if (!this.pendingPlurals.has(locale)) {
158
+ this.pendingPlurals.set(locale, []);
159
+ }
160
+ this.pendingPlurals.get(locale).push({ canonical, forms, templates });
161
+ if (!this.scheduled) {
162
+ this.scheduled = true;
163
+ queueMicrotask(() => this.flush());
164
+ }
165
+ }
166
+ async flush() {
167
+ this.scheduled = false;
168
+ const batch = this.pending;
169
+ const batchPlurals = this.pendingPlurals;
170
+ this.pending = /* @__PURE__ */ new Map();
171
+ this.pendingPlurals = /* @__PURE__ */ new Map();
172
+ const promises = [];
173
+ for (const [locale, strings] of batch) {
174
+ const plurals = batchPlurals.get(locale);
175
+ if (strings.size === 0 && !plurals?.length) continue;
176
+ promises.push(this.flushFn(locale, [...strings], plurals));
177
+ }
178
+ for (const [locale, plurals] of batchPlurals) {
179
+ if (batch.has(locale)) continue;
180
+ if (!plurals.length) continue;
181
+ promises.push(this.flushFn(locale, [], plurals));
182
+ }
183
+ await Promise.all(promises);
184
+ }
185
+ };
186
+
187
+ // src/locale/detect.ts
188
+ function browserDetector() {
189
+ if (typeof navigator === "undefined") return null;
190
+ return navigator.language?.split("-")[0] ?? null;
191
+ }
192
+ function createDetectionChain(detectors) {
193
+ return () => {
194
+ for (const detector of detectors) {
195
+ const result = detector();
196
+ if (result !== null) return result;
197
+ }
198
+ return null;
199
+ };
200
+ }
201
+
202
+ // src/utils/interpolate.ts
203
+ function interpolate(template, vars) {
204
+ if (!vars) return template;
205
+ return template.replace(/\{(\w+)\}/g, (_, key) => {
206
+ const value = vars[key];
207
+ return value !== void 0 ? String(value) : `{${key}}`;
208
+ });
209
+ }
210
+
211
+ // src/Babelize.ts
212
+ var DEFAULT_FALLBACK_LOCALE = "en";
213
+ var DEFAULT_API_URL2 = "https://api.babelize.co/api";
214
+ var Babelize = class {
215
+ constructor() {
216
+ this.initialized = false;
217
+ this.apiKey = "";
218
+ this.apiUrl = DEFAULT_API_URL2;
219
+ this.apiClient = null;
220
+ this.batcher = null;
221
+ this._locale = DEFAULT_FALLBACK_LOCALE;
222
+ this.fallbackLocale = DEFAULT_FALLBACK_LOCALE;
223
+ this.subscribers = /* @__PURE__ */ new Set();
224
+ this._version = 0;
225
+ this.cache = new Cache();
226
+ this.detectLocale = createDetectionChain([browserDetector]);
227
+ this.batcher = new Batcher(async (locale, strings, plurals) => {
228
+ await this.flushBatch(locale, strings, plurals);
229
+ });
230
+ }
231
+ init(config) {
232
+ if (this.initialized) return;
233
+ if (typeof config === "string") {
234
+ config = { apiKey: config };
235
+ }
236
+ const resolved = config ?? {};
237
+ this.apiKey = resolved.apiKey ?? "";
238
+ this.apiUrl = resolved.apiUrl ?? DEFAULT_API_URL2;
239
+ this.fallbackLocale = resolved.fallbackLocale ?? DEFAULT_FALLBACK_LOCALE;
240
+ if (resolved.locale) {
241
+ this._locale = resolved.locale;
242
+ } else {
243
+ const detected = this.detectLocale();
244
+ this._locale = detected ?? this.fallbackLocale;
245
+ }
246
+ if (this.apiKey) {
247
+ this.apiClient = new ApiClient(this.apiKey, this.apiUrl);
248
+ }
249
+ this.initialized = true;
250
+ }
251
+ reset() {
252
+ this.initialized = false;
253
+ this.apiKey = "";
254
+ this.apiClient = null;
255
+ this.cache = new Cache();
256
+ this._locale = DEFAULT_FALLBACK_LOCALE;
257
+ this.fallbackLocale = DEFAULT_FALLBACK_LOCALE;
258
+ this.subscribers.clear();
259
+ }
260
+ loadLockfile(data) {
261
+ this.cache.loadLockfile(data);
262
+ }
263
+ t(str, vars) {
264
+ const cached = this.cache.get(this._locale, str);
265
+ if (cached !== null) {
266
+ return interpolate(cached, vars);
267
+ }
268
+ if (this.apiClient && this.batcher) {
269
+ this.batcher.add(this._locale, str);
270
+ }
271
+ return interpolate(str, vars);
272
+ }
273
+ p(singular, plural, countOrVars) {
274
+ let count;
275
+ let vars;
276
+ if (typeof countOrVars === "number") {
277
+ count = countOrVars;
278
+ vars = { count };
279
+ } else {
280
+ count = countOrVars.count ?? 0;
281
+ vars = countOrVars;
282
+ }
283
+ const rules = new Intl.PluralRules(this._locale);
284
+ const form = rules.select(count);
285
+ const key = singular;
286
+ const cached = this.cache.getPlural(this._locale, key, form);
287
+ if (cached !== null) {
288
+ return interpolate(cached, vars);
289
+ }
290
+ if (this.apiClient && this.batcher) {
291
+ this.batcher.addPlural(this._locale, key, ["one", "other"], [singular, plural]);
292
+ }
293
+ const sourceForm = form === "one" ? singular : plural;
294
+ return interpolate(sourceForm, vars);
295
+ }
296
+ plural(forms, vars) {
297
+ const count = vars?.count ?? 0;
298
+ const rules = new Intl.PluralRules(this._locale);
299
+ const form = rules.select(count);
300
+ const key = forms.other;
301
+ const cached = this.cache.getPlural(this._locale, key, form);
302
+ if (cached !== null) {
303
+ return interpolate(cached, vars);
304
+ }
305
+ if (this.apiClient && this.batcher) {
306
+ const entries = Object.entries(forms);
307
+ const formNames = entries.map(([f]) => f);
308
+ const templates = entries.map(([, t]) => t);
309
+ this.batcher.addPlural(this._locale, key, formNames, templates);
310
+ }
311
+ const sourceForm = forms[form] ?? forms.other;
312
+ return interpolate(sourceForm, vars);
313
+ }
314
+ tag(template) {
315
+ const translated = this.t(template);
316
+ const parts = [];
317
+ const markerRegex = /\{([^}]+)\}/g;
318
+ let lastIndex = 0;
319
+ let match;
320
+ while ((match = markerRegex.exec(translated)) !== null) {
321
+ if (match.index > lastIndex) {
322
+ parts.push({ type: "text", value: translated.slice(lastIndex, match.index) });
323
+ }
324
+ parts.push({ type: "segment", value: match[1] });
325
+ lastIndex = match.index + match[0].length;
326
+ }
327
+ if (lastIndex < translated.length) {
328
+ parts.push({ type: "text", value: translated.slice(lastIndex) });
329
+ }
330
+ return { parts };
331
+ }
332
+ getVersion() {
333
+ return this._version;
334
+ }
335
+ setLocale(locale) {
336
+ if (locale === this._locale) return;
337
+ this._locale = locale;
338
+ this._version++;
339
+ this.subscribers.forEach((cb) => cb());
340
+ }
341
+ getLocale() {
342
+ return this._locale;
343
+ }
344
+ subscribe(cb) {
345
+ this.subscribers.add(cb);
346
+ return () => {
347
+ this.subscribers.delete(cb);
348
+ };
349
+ }
350
+ async flushBatch(locale, strings, plurals) {
351
+ if (!this.apiClient) return;
352
+ if (strings.length === 0 && !plurals?.length) return;
353
+ const uncached = strings.filter((s) => !this.cache.has(locale, s));
354
+ if (uncached.length === 0 && !plurals?.length) return;
355
+ try {
356
+ const { translations, plurals: pluralTranslations } = await this.apiClient.translate(
357
+ locale,
358
+ uncached,
359
+ plurals
360
+ );
361
+ let updated = false;
362
+ for (const [source, translation] of Object.entries(translations)) {
363
+ if (translation && translation !== source) {
364
+ this.cache.set(locale, source, translation);
365
+ updated = true;
366
+ }
367
+ }
368
+ if (pluralTranslations) {
369
+ for (const [canonical, forms] of Object.entries(pluralTranslations)) {
370
+ for (const [form, translation] of Object.entries(forms)) {
371
+ if (translation) {
372
+ this.cache.setPlural(locale, canonical, form, translation);
373
+ updated = true;
374
+ }
375
+ }
376
+ }
377
+ }
378
+ if (updated) {
379
+ this._version++;
380
+ this.subscribers.forEach((cb) => cb());
381
+ }
382
+ } catch {
383
+ }
384
+ }
385
+ };
386
+
387
+ // src/index.ts
388
+ var _babelize = new Babelize();
389
+ function babelize(str, vars) {
390
+ return _babelize.t(str, vars);
391
+ }
392
+ babelize.init = (config) => {
393
+ _babelize.init(config);
394
+ };
395
+ babelize.reset = () => {
396
+ _babelize.reset();
397
+ };
398
+ babelize.setLocale = (locale) => {
399
+ _babelize.setLocale(locale);
400
+ };
401
+ babelize.getLocale = () => {
402
+ return _babelize.getLocale();
403
+ };
404
+ babelize.loadLockfile = (data) => {
405
+ _babelize.loadLockfile(data);
406
+ };
407
+ babelize.subscribe = (cb) => {
408
+ return _babelize.subscribe(cb);
409
+ };
410
+ babelize.getVersion = () => {
411
+ return _babelize.getVersion();
412
+ };
413
+ babelize.tag = (template) => {
414
+ return _babelize.tag(template);
415
+ };
416
+ babelize.p = (singular, plural, countOrVars) => {
417
+ return _babelize.p(singular, plural, countOrVars);
418
+ };
419
+ babelize.plural = (forms, vars) => {
420
+ return _babelize.plural(forms, vars);
421
+ };
422
+ export {
423
+ babelize
424
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@babelize/sdk",
3
+ "version": "0.0.1",
4
+ "description": "The simplest way to localize your app — wrap strings, Babelize handles the rest",
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",
22
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run",
25
+ "lint": "eslint src --ext .ts"
26
+ },
27
+ "dependencies": {},
28
+ "devDependencies": {
29
+ "tsup": "^8.4.0",
30
+ "typescript": "^5.7.3",
31
+ "vitest": "^3.1.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "keywords": ["babelize", "i18n", "localization", "translation", "l10n"]
37
+ }