@shiftescape/astro-i18n-toolkit 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alvin James Bellero
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @shiftescape/astro-i18n-toolkit
2
+
3
+ A dev toolbar integration for Astro that adds a live i18n debugger — translation coverage map across all locales and a one-click locale switcher. Library-agnostic: works with Astro's built-in i18n, astro-i18next, Paraglide, or any manual JSON/YAML setup.
4
+
5
+ [![version](https://img.shields.io/npm/v/@shiftescape/astro-i18n-toolkit.svg?style=flat-square)](http://npm.im/@shiftescape/astro-i18n-toolkit) [![downloads](https://img.shields.io/npm/dm/@shiftescape/astro-i18n-toolkit.svg?style=flat-square)](https://npm-stat.com/charts.html?package=@shiftescape/astro-i18n-toolkit&from=2016-11-24) [![MIT License](https://img.shields.io/npm/l/@shiftescape/astro-i18n-toolkit.svg?style=flat-square)](http://opensource.org/licenses/MIT)
6
+
7
+ ## Features
8
+
9
+ - 🗺 **Coverage map** — scans all locale files and shows every key as green (complete), amber (fallback), or red (missing), per locale
10
+ - 📊 **Per-locale summary** — coverage % badge per locale at a glance
11
+ - 🔍 **Searchable** — filter keys by name or reference value in real time
12
+ - 🌐 **Locale switcher** — one-click locale switching via injected middleware, no URL editing required
13
+ - ⚙️ **Library-agnostic** — reads JSON or YAML locale files directly; works alongside any i18n library
14
+ - 🔒 **Dev-only** — completely stripped in `astro build`, zero production footprint
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install -D @shiftescape/astro-i18n-toolkit
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```js
25
+ // astro.config.mjs
26
+ import { defineConfig } from "astro/config";
27
+ import i18nToolkit from "@shiftescape/astro-i18n-toolkit";
28
+
29
+ export default defineConfig({
30
+ i18n: {
31
+ defaultLocale: "en",
32
+ locales: ["en", "fr", "de"],
33
+ },
34
+ integrations: [i18nToolkit()],
35
+ });
36
+ ```
37
+
38
+ Your locale files should live at `src/locales/` by default:
39
+
40
+ ```
41
+ src/locales/
42
+ en.json ← reference locale
43
+ fr.json
44
+ de.json
45
+ ```
46
+
47
+ Open your site with `astro dev` and click the globe icon in the dev toolbar.
48
+
49
+ ### With options
50
+
51
+ ```js
52
+ i18nToolkit({
53
+ localesDir: "./src/i18n", // path to locale files
54
+ defaultLocale: "en", // reference locale for coverage diff
55
+ format: "json", // 'json' | 'yaml'
56
+ });
57
+ ```
58
+
59
+ ## Options
60
+
61
+ | Option | Type | Default | Description |
62
+ | --------------- | ------------------ | ----------------- | --------------------------------------------------- |
63
+ | `localesDir` | `string` | `'./src/locales'` | Path to locale files, relative to project root |
64
+ | `defaultLocale` | `string` | `'en'` | Reference locale — all others compared against this |
65
+ | `format` | `'json' \| 'yaml'` | `'json'` | File format of translation files |
66
+
67
+ ## License
68
+
69
+ MIT © [Alvin James Bellero](https://github.com/shiftEscape)
@@ -0,0 +1,5 @@
1
+ import type { AstroIntegration } from 'astro';
2
+ import type { I18nToolkitOptions } from './types.js';
3
+ export default function i18nToolkit(options?: I18nToolkitOptions): AstroIntegration;
4
+ export type { I18nToolkitOptions };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAe,MAAM,OAAO,CAAA;AAS1D,OAAO,KAAK,EACV,kBAAkB,EAInB,MAAM,YAAY,CAAA;AAEnB,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CA8GlB;AAED,YAAY,EAAE,kBAAkB,EAAE,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { join, isAbsolute } from 'node:path';
3
+ import { scanLocalesDir, computeCoverage, buildLocaleUrl, } from './utils.js';
4
+ export default function i18nToolkit(options = {}) {
5
+ const { localesDir = './src/locales', format = 'json', } = options;
6
+ let projectRoot = process.cwd();
7
+ let resolvedLocales = [];
8
+ let resolvedDefaultLocale = options.defaultLocale ?? 'en';
9
+ let prefixDefault = false;
10
+ return {
11
+ name: 'astro-i18n-toolkit',
12
+ hooks: {
13
+ 'astro:config:setup'({ addDevToolbarApp, addMiddleware, command, config, logger }) {
14
+ if (command !== 'dev')
15
+ return;
16
+ projectRoot = fileURLToPath(config.root);
17
+ // Read Astro's i18n config if available
18
+ const astroI18n = config.i18n;
19
+ if (astroI18n?.locales) {
20
+ resolvedLocales = astroI18n.locales;
21
+ }
22
+ if (astroI18n?.defaultLocale) {
23
+ resolvedDefaultLocale = options.defaultLocale ?? astroI18n.defaultLocale;
24
+ }
25
+ if (astroI18n?.routing?.prefixDefaultLocale) {
26
+ prefixDefault = astroI18n.routing.prefixDefaultLocale;
27
+ }
28
+ logger.info('i18n Toolkit active — visible in dev toolbar.');
29
+ // Register toolbar app
30
+ addDevToolbarApp({
31
+ id: 'astro-i18n-toolkit',
32
+ name: 'i18n Toolkit',
33
+ icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>`,
34
+ entrypoint: fileURLToPath(new URL('./toolbar-app.js', import.meta.url)),
35
+ });
36
+ // Inject locale-switcher middleware (Tier 2)
37
+ addMiddleware({
38
+ entrypoint: new URL('./middleware.js', import.meta.url),
39
+ order: 'pre',
40
+ });
41
+ },
42
+ 'astro:server:setup'({ toolbar }) {
43
+ // Client ready — send config
44
+ toolbar.on('astro-i18n-toolkit:ready', () => {
45
+ // If no locales from Astro config, fall back to locale file names
46
+ if (resolvedLocales.length === 0) {
47
+ const absDir = isAbsolute(localesDir) ? localesDir : join(projectRoot, localesDir);
48
+ const localeMap = scanLocalesDir(absDir, format);
49
+ resolvedLocales = [...localeMap.keys()];
50
+ }
51
+ const configPayload = {
52
+ locales: resolvedLocales,
53
+ defaultLocale: resolvedDefaultLocale,
54
+ currentLocale: resolvedDefaultLocale, // server doesn't know the current URL
55
+ };
56
+ toolbar.send('astro-i18n-toolkit:config', configPayload);
57
+ });
58
+ // Client requests coverage scan
59
+ toolbar.on('astro-i18n-toolkit:request-coverage', (data) => {
60
+ const absDir = isAbsolute(localesDir) ? localesDir : join(projectRoot, localesDir);
61
+ const localeMap = scanLocalesDir(absDir, format);
62
+ // Ensure default locale is present
63
+ if (!localeMap.has(resolvedDefaultLocale) && localeMap.size > 0) {
64
+ const firstLocale = [...localeMap.keys()][0];
65
+ resolvedDefaultLocale = firstLocale;
66
+ }
67
+ const { keys, summary } = computeCoverage(localeMap, resolvedDefaultLocale);
68
+ const payload = {
69
+ locales: [...localeMap.keys()],
70
+ defaultLocale: resolvedDefaultLocale,
71
+ keys,
72
+ summary,
73
+ totalKeys: keys.length,
74
+ scannedAt: Date.now(),
75
+ };
76
+ toolbar.send('astro-i18n-toolkit:coverage', payload);
77
+ });
78
+ // Client requests locale switch — compute redirect URL and send back
79
+ toolbar.on('astro-i18n-toolkit:switch-locale', ({ locale, currentUrl }) => {
80
+ const targetUrl = buildLocaleUrl(currentUrl, locale, resolvedLocales, resolvedDefaultLocale, prefixDefault);
81
+ // Send redirect instruction: "locale::target-path"
82
+ toolbar.send('astro-i18n-toolkit:redirect', {
83
+ redirectUrl: `${locale}::${targetUrl}`,
84
+ });
85
+ });
86
+ },
87
+ },
88
+ };
89
+ }
90
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { MiddlewareHandler } from 'astro';
2
+ export declare const onRequest: MiddlewareHandler;
3
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAQ9C,eAAO,MAAM,SAAS,EAAE,iBA6BvB,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { LOCALE_COOKIE } from './types.js';
2
+ // This middleware runs with order: 'pre' — before Astro's own i18n middleware.
3
+ // When the toolbar sets the __astro_i18n_locale cookie, we redirect to the
4
+ // locale-prefixed URL so the page re-renders in the selected locale.
5
+ // In production (command !== 'dev') this middleware is never registered.
6
+ export const onRequest = async (context, next) => {
7
+ const { request, cookies, redirect } = context;
8
+ const url = new URL(request.url);
9
+ // Only act if a locale override cookie is present
10
+ const requestedLocale = cookies.get(LOCALE_COOKIE)?.value;
11
+ if (!requestedLocale)
12
+ return next();
13
+ // Skip internal Astro routes
14
+ if (url.pathname.startsWith('/_astro') || url.pathname.startsWith('/_server')) {
15
+ return next();
16
+ }
17
+ // The toolbar client sends the target URL directly — just redirect there
18
+ // The redirect target is set as the cookie value in toolbar-app.ts
19
+ // Format: "locale::target-path" e.g. "fr::/fr/about"
20
+ if (requestedLocale.includes('::')) {
21
+ const [, targetPath] = requestedLocale.split('::');
22
+ // Clear the cookie so we don't loop
23
+ const response = redirect(targetPath || '/', 302);
24
+ response.headers.append('Set-Cookie', `${LOCALE_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`);
25
+ return response;
26
+ }
27
+ return next();
28
+ };
29
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,+EAA+E;AAC/E,2EAA2E;AAC3E,qEAAqE;AACrE,yEAAyE;AAEzE,MAAM,CAAC,MAAM,SAAS,GAAsB,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAClE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAEhC,kDAAkD;IAClD,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,KAAK,CAAA;IACzD,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,EAAE,CAAA;IAEnC,6BAA6B;IAC7B,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9E,OAAO,IAAI,EAAE,CAAA;IACf,CAAC;IAED,yEAAyE;IACzE,mEAAmE;IACnE,sDAAsD;IACtD,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAElD,oCAAoC;QACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,GAAG,CAAC,CAAA;QACjD,QAAQ,CAAC,OAAO,CAAC,MAAM,CACrB,YAAY,EACZ,GAAG,aAAa,oCAAoC,CACrD,CAAA;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,OAAO,IAAI,EAAE,CAAA;AACf,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("astro").DevToolbarApp;
2
+ export default _default;
3
+ //# sourceMappingURL=toolbar-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-app.d.ts","sourceRoot":"","sources":["../src/toolbar-app.ts"],"names":[],"mappings":";AAUA,wBA6eG"}
@@ -0,0 +1,419 @@
1
+ import { defineToolbarApp } from "astro/toolbar";
2
+ import { LOCALE_COOKIE } from "./types.js";
3
+ export default defineToolbarApp({
4
+ init(canvas, app, server) {
5
+ // -----------------------------------------------------------------------
6
+ // State
7
+ // -----------------------------------------------------------------------
8
+ let config = null;
9
+ let coverage = null;
10
+ let activeTab = "coverage";
11
+ let searchQuery = "";
12
+ let activeFilter = "all";
13
+ let panelBuilt = false;
14
+ let listWrap = null;
15
+ let summaryBar = null;
16
+ let localeButtons = null;
17
+ // -----------------------------------------------------------------------
18
+ // Bootstrap
19
+ // -----------------------------------------------------------------------
20
+ server.send("astro-i18n-toolkit:ready", {});
21
+ server.on("astro-i18n-toolkit:config", (data) => {
22
+ config = data;
23
+ });
24
+ server.on("astro-i18n-toolkit:coverage", (data) => {
25
+ coverage = data;
26
+ if (panelBuilt) {
27
+ updateSummaryBar();
28
+ refreshList();
29
+ updateLocaleButtons();
30
+ }
31
+ else
32
+ buildPanel();
33
+ });
34
+ app.onToggled(({ state }) => {
35
+ if (state) {
36
+ server.send("astro-i18n-toolkit:request-coverage", {});
37
+ if (!panelBuilt && coverage)
38
+ buildPanel();
39
+ }
40
+ });
41
+ // -----------------------------------------------------------------------
42
+ // Build panel once
43
+ // -----------------------------------------------------------------------
44
+ function buildPanel() {
45
+ canvas.innerHTML = "";
46
+ panelBuilt = false;
47
+ const root = document.createElement("div");
48
+ root.style.cssText = `
49
+ position:fixed;bottom:72px;left:50%;transform:translateX(-50%);
50
+ width:660px;max-width:calc(100vw - 32px);max-height:74vh;
51
+ background:#0d1117;border:1px solid #21262d;border-radius:12px;
52
+ font-family:ui-sans-serif,system-ui,sans-serif;font-size:13px;
53
+ color:#e2e8f0;box-shadow:0 20px 60px rgba(0,0,0,.6);
54
+ display:flex;flex-direction:column;overflow:hidden;z-index:9999;
55
+ `;
56
+ // ── Header ──
57
+ const header = document.createElement("div");
58
+ header.style.cssText = `
59
+ padding:12px 16px 0;border-bottom:1px solid #21262d;flex-shrink:0;
60
+ `;
61
+ const titleRow = document.createElement("div");
62
+ titleRow.style.cssText =
63
+ "display:flex;align-items:center;gap:10px;padding-bottom:10px";
64
+ const title = document.createElement("span");
65
+ title.style.cssText =
66
+ "font-size:14px;font-weight:600;color:#e2e8f0;display:flex;align-items:center;gap:8px";
67
+ title.innerHTML = "i18n Toolkit";
68
+ summaryBar = document.createElement("div");
69
+ summaryBar.style.cssText =
70
+ "display:flex;gap:12px;font-size:11px;margin-left:auto";
71
+ titleRow.appendChild(title);
72
+ titleRow.appendChild(summaryBar);
73
+ header.appendChild(titleRow);
74
+ // Tabs
75
+ const tabs = document.createElement("div");
76
+ tabs.style.cssText = "display:flex;gap:0";
77
+ tabs.appendChild(makeTab("Coverage map", activeTab === "coverage", () => switchTab("coverage", tabs, contentArea)));
78
+ tabs.appendChild(makeTab("Locale switcher", activeTab === "switcher", () => switchTab("switcher", tabs, contentArea)));
79
+ header.appendChild(tabs);
80
+ // Content area
81
+ const contentArea = document.createElement("div");
82
+ contentArea.style.cssText =
83
+ "display:flex;flex-direction:column;flex:1;overflow:hidden";
84
+ root.appendChild(header);
85
+ root.appendChild(contentArea);
86
+ canvas.appendChild(root);
87
+ panelBuilt = true;
88
+ renderTabContent(contentArea);
89
+ updateSummaryBar();
90
+ }
91
+ // -----------------------------------------------------------------------
92
+ // Tab switching
93
+ // -----------------------------------------------------------------------
94
+ function switchTab(tab, tabsEl, contentEl) {
95
+ activeTab = tab;
96
+ Array.from(tabsEl.children).forEach((t, i) => {
97
+ const labels = ["coverage", "switcher"];
98
+ styleTab(t, labels[i] === tab);
99
+ });
100
+ contentEl.innerHTML = "";
101
+ renderTabContent(contentEl);
102
+ }
103
+ function renderTabContent(container) {
104
+ if (activeTab === "coverage")
105
+ renderCoverageTab(container);
106
+ else
107
+ renderSwitcherTab(container);
108
+ }
109
+ // -----------------------------------------------------------------------
110
+ // Coverage tab
111
+ // -----------------------------------------------------------------------
112
+ function renderCoverageTab(container) {
113
+ // Filter + search bar
114
+ const controls = document.createElement("div");
115
+ controls.style.cssText =
116
+ "padding:8px 16px;border-bottom:1px solid #21262d;display:flex;gap:6px;align-items:center;flex-shrink:0";
117
+ const filterBtns = [
118
+ ["all", "all"],
119
+ ["missing", "missing"],
120
+ ["fallback", "fallback"],
121
+ ["complete", "complete"],
122
+ ];
123
+ filterBtns.forEach(([label, val]) => {
124
+ const btn = makeFilterBtn(label, activeFilter === val);
125
+ btn.onclick = () => {
126
+ activeFilter = val;
127
+ controls
128
+ .querySelectorAll("button")
129
+ .forEach((b, i) => styleFilterBtn(b, filterBtns[i][1] === val));
130
+ refreshList();
131
+ };
132
+ controls.appendChild(btn);
133
+ });
134
+ const spacer = document.createElement("div");
135
+ spacer.style.cssText = "flex:1";
136
+ controls.appendChild(spacer);
137
+ const searchInput = document.createElement("input");
138
+ searchInput.type = "text";
139
+ searchInput.placeholder = "Search keys…";
140
+ searchInput.value = searchQuery;
141
+ searchInput.style.cssText = `
142
+ background:#010409;border:1px solid #21262d;border-radius:6px;
143
+ color:#e2e8f0;font-size:11px;font-family:ui-monospace,monospace;
144
+ padding:5px 9px;outline:none;width:160px;
145
+ `;
146
+ searchInput.oninput = (e) => {
147
+ searchQuery = e.target.value;
148
+ refreshList();
149
+ };
150
+ controls.appendChild(searchInput);
151
+ // List
152
+ listWrap = document.createElement("div");
153
+ listWrap.style.cssText = "overflow-y:auto;flex:1;padding:4px 0";
154
+ container.appendChild(controls);
155
+ container.appendChild(listWrap);
156
+ refreshList();
157
+ }
158
+ // -----------------------------------------------------------------------
159
+ // Switcher tab
160
+ // -----------------------------------------------------------------------
161
+ function renderSwitcherTab(container) {
162
+ const wrap = document.createElement("div");
163
+ wrap.style.cssText = "padding:24px 20px;flex:1";
164
+ const intro = document.createElement("p");
165
+ intro.style.cssText =
166
+ "font-size:13px;color:#8b949e;margin-bottom:20px;line-height:1.6";
167
+ intro.textContent =
168
+ "Switch the active locale for this page without manually changing the URL. The page will reload in the selected locale.";
169
+ wrap.appendChild(intro);
170
+ const currentRow = document.createElement("div");
171
+ currentRow.style.cssText =
172
+ "display:flex;align-items:center;gap:10px;margin-bottom:20px;font-size:12px;color:#8b949e";
173
+ currentRow.innerHTML = `
174
+ <span>Current locale:</span>
175
+ <span style="font-family:ui-monospace,monospace;font-size:12px;color:#58a6ff;
176
+ background:#0d1b2d;border:0.5px solid #1a3a5c;padding:2px 10px;border-radius:10px">
177
+ ${config?.currentLocale ?? "—"}
178
+ </span>
179
+ `;
180
+ wrap.appendChild(currentRow);
181
+ const label = document.createElement("div");
182
+ label.style.cssText =
183
+ "font-size:11px;font-weight:500;color:#484f58;text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px";
184
+ label.textContent = "Switch to";
185
+ wrap.appendChild(label);
186
+ localeButtons = document.createElement("div");
187
+ localeButtons.style.cssText = "display:flex;gap:8px;flex-wrap:wrap";
188
+ wrap.appendChild(localeButtons);
189
+ updateLocaleButtons();
190
+ container.appendChild(wrap);
191
+ }
192
+ function updateLocaleButtons() {
193
+ if (!localeButtons || !config)
194
+ return;
195
+ localeButtons.innerHTML = "";
196
+ config.locales.forEach((locale) => {
197
+ const isCurrent = locale === config.currentLocale;
198
+ const btn = document.createElement("button");
199
+ btn.style.cssText = `
200
+ font-size:13px;font-weight:500;padding:8px 20px;
201
+ border-radius:8px;cursor:${isCurrent ? "default" : "pointer"};
202
+ border:1px solid ${isCurrent ? "#388bfd" : "#30363d"};
203
+ background:${isCurrent ? "#0d1b2d" : "#161b22"};
204
+ color:${isCurrent ? "#58a6ff" : "#c9d1d9"};
205
+ font-family:ui-sans-serif,system-ui,sans-serif;
206
+ transition:all .15s;
207
+ `;
208
+ btn.textContent = locale + (isCurrent ? " ✓" : "");
209
+ btn.title = isCurrent ? "Current locale" : `Switch to ${locale}`;
210
+ if (!isCurrent) {
211
+ btn.onmouseenter = () => {
212
+ btn.style.borderColor = "#58a6ff";
213
+ btn.style.color = "#58a6ff";
214
+ };
215
+ btn.onmouseleave = () => {
216
+ btn.style.borderColor = "#30363d";
217
+ btn.style.color = "#c9d1d9";
218
+ };
219
+ btn.onclick = () => switchLocale(locale);
220
+ }
221
+ localeButtons.appendChild(btn);
222
+ });
223
+ }
224
+ function switchLocale(locale) {
225
+ const currentPath = window.location.pathname;
226
+ // Send to server — server computes the redirected URL and responds
227
+ const payload = { locale, currentUrl: currentPath };
228
+ server.send("astro-i18n-toolkit:switch-locale", payload);
229
+ }
230
+ server.on("astro-i18n-toolkit:redirect", ({ redirectUrl }) => {
231
+ // Set cookie — middleware will pick it up on the redirected request
232
+ document.cookie = `${LOCALE_COOKIE}=${encodeURIComponent(redirectUrl)}; path=/; max-age=5; SameSite=Lax`;
233
+ // Navigate — middleware reads cookie and redirects properly
234
+ window.location.href = redirectUrl;
235
+ });
236
+ // -----------------------------------------------------------------------
237
+ // Update summary bar in header
238
+ // -----------------------------------------------------------------------
239
+ function updateSummaryBar() {
240
+ if (!summaryBar || !coverage)
241
+ return;
242
+ const missing = coverage.keys.filter((k) => k.status === "missing").length;
243
+ const fallback = coverage.keys.filter((k) => k.status === "fallback").length;
244
+ const total = coverage.totalKeys;
245
+ summaryBar.innerHTML = `
246
+ <span style="color:#f85149">✗ ${missing} missing</span>
247
+ <span style="color:#d29922">~ ${fallback} fallback</span>
248
+ <span style="color:#8b949e">${total} keys</span>
249
+ `;
250
+ }
251
+ // -----------------------------------------------------------------------
252
+ // Refresh the coverage list (scroll preserved)
253
+ // -----------------------------------------------------------------------
254
+ function refreshList() {
255
+ if (!listWrap || !coverage)
256
+ return;
257
+ const saved = listWrap.scrollTop;
258
+ listWrap.innerHTML = "";
259
+ let filtered = coverage.keys.filter((k) => {
260
+ const q = searchQuery.toLowerCase();
261
+ const matchesSearch = !q ||
262
+ k.key.toLowerCase().includes(q) ||
263
+ k.referenceValue.toLowerCase().includes(q);
264
+ const matchesFilter = activeFilter === "all" || k.status === activeFilter;
265
+ return matchesSearch && matchesFilter;
266
+ });
267
+ if (filtered.length === 0) {
268
+ const empty = document.createElement("div");
269
+ empty.style.cssText =
270
+ "text-align:center;color:#484f58;padding:28px;font-size:12px";
271
+ empty.textContent = searchQuery
272
+ ? "No keys match your search."
273
+ : "No locale files found. Check your localesDir config.";
274
+ listWrap.appendChild(empty);
275
+ listWrap.scrollTop = saved;
276
+ return;
277
+ }
278
+ // Group header: per-locale summary pills at top
279
+ if (coverage.summary.length > 0 &&
280
+ activeFilter === "all" &&
281
+ !searchQuery) {
282
+ const summaryRow = document.createElement("div");
283
+ summaryRow.style.cssText =
284
+ "display:flex;gap:8px;flex-wrap:wrap;padding:8px 16px 4px";
285
+ coverage.summary.forEach((s) => {
286
+ const pill = document.createElement("div");
287
+ const pct = s.percent;
288
+ const color = pct === 100 ? "#3fb950" : pct >= 80 ? "#d29922" : "#f85149";
289
+ pill.style.cssText = `
290
+ font-size:11px;padding:4px 10px;border-radius:8px;
291
+ background:#161b22;border:0.5px solid #30363d;
292
+ display:flex;align-items:center;gap:6px;
293
+ `;
294
+ pill.innerHTML = `
295
+ <span style="font-family:ui-monospace,monospace;color:#e2e8f0;font-weight:500">${s.locale}</span>
296
+ <span style="color:${color};font-weight:600">${pct}%</span>
297
+ <span style="color:#484f58">${s.complete}/${s.total}</span>
298
+ `;
299
+ summaryRow.appendChild(pill);
300
+ });
301
+ listWrap.appendChild(summaryRow);
302
+ }
303
+ // Key rows
304
+ filtered.forEach((key) => listWrap.appendChild(makeKeyRow(key)));
305
+ listWrap.scrollTop = saved;
306
+ }
307
+ // -----------------------------------------------------------------------
308
+ // Build a single key row
309
+ // -----------------------------------------------------------------------
310
+ function makeKeyRow(k) {
311
+ const row = document.createElement("div");
312
+ row.style.cssText = `
313
+ display:flex;align-items:flex-start;gap:9px;
314
+ padding:6px 16px;transition:background .1s;
315
+ border-left:2px solid ${statusColor(k.status)};
316
+ `;
317
+ row.onmouseenter = () => (row.style.background = "#111822");
318
+ row.onmouseleave = () => (row.style.background = "transparent");
319
+ const dot = document.createElement("span");
320
+ dot.style.cssText = `width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-top:4px;background:${statusColor(k.status)}`;
321
+ const main = document.createElement("div");
322
+ main.style.cssText = "flex:1;min-width:0";
323
+ const keyEl = document.createElement("div");
324
+ keyEl.style.cssText =
325
+ "font-family:ui-monospace,monospace;font-size:12px;color:#e2e8f0;margin-bottom:2px";
326
+ keyEl.textContent = k.key;
327
+ const meta = document.createElement("div");
328
+ meta.style.cssText =
329
+ "display:flex;gap:6px;align-items:center;flex-wrap:wrap";
330
+ if (k.referenceValue) {
331
+ const val = document.createElement("span");
332
+ val.style.cssText =
333
+ "font-size:11px;color:#6e7681;font-style:italic;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
334
+ val.textContent = `"${k.referenceValue}"`;
335
+ meta.appendChild(val);
336
+ }
337
+ if (k.missingIn.length > 0) {
338
+ k.missingIn.forEach((locale) => {
339
+ const badge = document.createElement("span");
340
+ badge.style.cssText = `
341
+ font-size:10px;font-weight:500;padding:1px 6px;border-radius:10px;
342
+ background:#2d1316;color:#f85149;border:0.5px solid #f8514933;
343
+ font-family:ui-monospace,monospace;
344
+ `;
345
+ badge.textContent = `missing: ${locale}`;
346
+ meta.appendChild(badge);
347
+ });
348
+ }
349
+ main.appendChild(keyEl);
350
+ main.appendChild(meta);
351
+ const statusBadge = makeStatusBadge(k.status);
352
+ row.appendChild(dot);
353
+ row.appendChild(main);
354
+ row.appendChild(statusBadge);
355
+ const divider = document.createElement("div");
356
+ divider.style.cssText = "height:0.5px;background:#161b22;margin:0 16px";
357
+ const wrapper = document.createElement("div");
358
+ wrapper.appendChild(row);
359
+ wrapper.appendChild(divider);
360
+ return wrapper;
361
+ }
362
+ // -----------------------------------------------------------------------
363
+ // Helpers
364
+ // -----------------------------------------------------------------------
365
+ function makeTab(label, active, onClick) {
366
+ const tab = document.createElement("button");
367
+ styleTab(tab, active);
368
+ tab.textContent = label;
369
+ tab.onclick = onClick;
370
+ return tab;
371
+ }
372
+ function styleTab(el, active) {
373
+ el.style.cssText = `
374
+ font-size:12px;padding:8px 14px;cursor:pointer;
375
+ border:none;border-bottom:2px solid ${active ? "#58a6ff" : "transparent"};
376
+ background:transparent;color:${active ? "#e2e8f0" : "#8b949e"};
377
+ font-family:ui-sans-serif,system-ui,sans-serif;transition:color .15s;
378
+ `;
379
+ }
380
+ function makeFilterBtn(label, active) {
381
+ const btn = document.createElement("button");
382
+ styleFilterBtn(btn, active);
383
+ btn.textContent = label;
384
+ return btn;
385
+ }
386
+ function styleFilterBtn(btn, active) {
387
+ btn.style.cssText = `
388
+ font-size:11px;padding:3px 9px;border-radius:100px;
389
+ border:0.5px solid ${active ? "#30363d" : "#21262d"};
390
+ background:${active ? "#21262d" : "transparent"};
391
+ color:${active ? "#e2e8f0" : "#8b949e"};cursor:pointer;
392
+ font-family:ui-sans-serif,system-ui,sans-serif;
393
+ `;
394
+ }
395
+ function makeStatusBadge(status) {
396
+ const colors = {
397
+ complete: ["#0d2e0d", "#3fb950"],
398
+ fallback: ["#1a1a0d", "#d29922"],
399
+ missing: ["#2d1316", "#f85149"],
400
+ };
401
+ const [bg, fg] = colors[status] || colors.missing;
402
+ const b = document.createElement("span");
403
+ b.style.cssText = `
404
+ font-size:10px;font-weight:500;padding:1px 7px;border-radius:10px;
405
+ background:${bg};color:${fg};border:0.5px solid ${fg}33;flex-shrink:0;
406
+ `;
407
+ b.textContent = status;
408
+ return b;
409
+ }
410
+ function statusColor(status) {
411
+ return status === "complete"
412
+ ? "#3fb950"
413
+ : status === "fallback"
414
+ ? "#d29922"
415
+ : "#f85149";
416
+ }
417
+ },
418
+ });
419
+ //# sourceMappingURL=toolbar-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-app.js","sourceRoot":"","sources":["../src/toolbar-app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ export interface I18nToolkitOptions {
2
+ /**
3
+ * Path to locale files directory, relative to project root.
4
+ * e.g. './src/locales' or './public/locales'
5
+ * @default './src/locales'
6
+ */
7
+ localesDir?: string;
8
+ /**
9
+ * The reference locale — all other locales are compared against this.
10
+ * Falls back to config.i18n.defaultLocale if not set.
11
+ * @default 'en'
12
+ */
13
+ defaultLocale?: string;
14
+ /**
15
+ * File format of translation files.
16
+ * @default 'json'
17
+ */
18
+ format?: 'json' | 'yaml';
19
+ }
20
+ export type KeyStatus = 'complete' | 'fallback' | 'missing';
21
+ export interface KeyCoverage {
22
+ key: string;
23
+ status: KeyStatus;
24
+ /** Which locales are missing this key */
25
+ missingIn: string[];
26
+ /** Reference locale value (truncated for display) */
27
+ referenceValue: string;
28
+ }
29
+ export interface LocaleCoverage {
30
+ locale: string;
31
+ total: number;
32
+ complete: number;
33
+ fallback: number;
34
+ missing: number;
35
+ /** 0–100 */
36
+ percent: number;
37
+ }
38
+ export interface CoveragePayload {
39
+ locales: string[];
40
+ defaultLocale: string;
41
+ keys: KeyCoverage[];
42
+ summary: LocaleCoverage[];
43
+ totalKeys: number;
44
+ scannedAt: number;
45
+ }
46
+ export interface LocaleSwitchPayload {
47
+ locale: string;
48
+ currentUrl: string;
49
+ }
50
+ export interface ToolkitConfigPayload {
51
+ locales: string[];
52
+ defaultLocale: string;
53
+ currentLocale: string;
54
+ }
55
+ export declare const LOCALE_COOKIE = "__astro_i18n_locale";
56
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACzB;AAMD,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;AAE3D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,SAAS,CAAA;IACjB,yCAAyC;IACzC,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAGD,eAAO,MAAM,aAAa,wBAAwB,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Public option types
3
+ // ---------------------------------------------------------------------------
4
+ // Cookie name used by the Tier 2 locale switcher middleware
5
+ export const LOCALE_COOKIE = '__astro_i18n_locale';
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAqE9E,4DAA4D;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { KeyCoverage, LocaleCoverage } from './types.js';
2
+ export declare function flattenKeys(obj: Record<string, unknown>, prefix?: string): Record<string, string>;
3
+ export declare function parseLocaleFile(filePath: string, format: 'json' | 'yaml'): Record<string, string>;
4
+ export declare function parseYamlSimple(raw: string): Record<string, unknown>;
5
+ export declare function scanLocalesDir(dir: string, format: 'json' | 'yaml'): Map<string, Record<string, string>>;
6
+ export declare function computeCoverage(localeMap: Map<string, Record<string, string>>, defaultLocale: string): {
7
+ keys: KeyCoverage[];
8
+ summary: LocaleCoverage[];
9
+ };
10
+ export declare function deriveLocaleFromUrl(pathname: string, locales: string[], defaultLocale: string): string;
11
+ export declare function buildLocaleUrl(pathname: string, targetLocale: string, locales: string[], defaultLocale: string, prefixDefault: boolean): string;
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAa,MAAM,YAAY,CAAA;AAMxE,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,SAAK,GACV,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWxB;AAKD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB;AAMD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+BpE;AAKD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAqBrC;AAKD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC9C,aAAa,EAAE,MAAM,GACpB;IAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,CAuEpD;AAMD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,CAOR;AAKD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EAAE,EACjB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,OAAO,GACrB,MAAM,CAcR"}
package/dist/utils.js ADDED
@@ -0,0 +1,192 @@
1
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
2
+ import { join, extname, basename } from 'node:path';
3
+ // ---------------------------------------------------------------------------
4
+ // Flatten a nested object into dot-notation keys
5
+ // { nav: { home: 'Home' } } → { 'nav.home': 'Home' }
6
+ // ---------------------------------------------------------------------------
7
+ export function flattenKeys(obj, prefix = '') {
8
+ const result = {};
9
+ for (const [k, v] of Object.entries(obj)) {
10
+ const key = prefix ? `${prefix}.${k}` : k;
11
+ if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
12
+ Object.assign(result, flattenKeys(v, key));
13
+ }
14
+ else {
15
+ result[key] = String(v ?? '');
16
+ }
17
+ }
18
+ return result;
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // Parse a single locale file (JSON or YAML)
22
+ // ---------------------------------------------------------------------------
23
+ export function parseLocaleFile(filePath, format) {
24
+ try {
25
+ const raw = readFileSync(filePath, 'utf-8');
26
+ if (format === 'json') {
27
+ return flattenKeys(JSON.parse(raw));
28
+ }
29
+ // YAML — minimal parser for simple key: value structures
30
+ // For real projects, this handles 80% of cases without a dep
31
+ return flattenKeys(parseYamlSimple(raw));
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // Very simple YAML parser — handles flat and one-level-nested key: value
39
+ // Sufficient for i18n files, avoids pulling in js-yaml as a dependency
40
+ // ---------------------------------------------------------------------------
41
+ export function parseYamlSimple(raw) {
42
+ const result = {};
43
+ let currentKey = '';
44
+ for (const line of raw.split('\n')) {
45
+ if (!line.trim() || line.trim().startsWith('#'))
46
+ continue;
47
+ // Detect indented (nested) line
48
+ const isNested = line.startsWith(' ') || line.startsWith('\t');
49
+ const colonIdx = line.indexOf(':');
50
+ if (colonIdx === -1)
51
+ continue;
52
+ const key = line.slice(isNested ? line.search(/\S/) : 0, colonIdx).trim();
53
+ const value = line.slice(colonIdx + 1).trim();
54
+ if (!isNested) {
55
+ currentKey = key;
56
+ if (value) {
57
+ // Strip surrounding quotes
58
+ result[key] = value.replace(/^["']|["']$/g, '');
59
+ }
60
+ else {
61
+ result[key] = {};
62
+ }
63
+ }
64
+ else if (currentKey) {
65
+ if (typeof result[currentKey] !== 'object')
66
+ result[currentKey] = {};
67
+ result[currentKey][key] =
68
+ value.replace(/^["']|["']$/g, '');
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Scan a locales directory and return a map of locale → flat keys
75
+ // ---------------------------------------------------------------------------
76
+ export function scanLocalesDir(dir, format) {
77
+ const result = new Map();
78
+ if (!existsSync(dir))
79
+ return result;
80
+ const ext = format === 'json' ? '.json' : '.yaml';
81
+ const altExt = format === 'yaml' ? '.yml' : null;
82
+ try {
83
+ const files = readdirSync(dir);
84
+ for (const file of files) {
85
+ const fileExt = extname(file);
86
+ if (fileExt !== ext && fileExt !== altExt)
87
+ continue;
88
+ const locale = basename(file, fileExt);
89
+ const keys = parseLocaleFile(join(dir, file), format);
90
+ result.set(locale, keys);
91
+ }
92
+ }
93
+ catch {
94
+ // directory unreadable — return empty
95
+ }
96
+ return result;
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // Compute coverage diff — reference locale vs all others
100
+ // ---------------------------------------------------------------------------
101
+ export function computeCoverage(localeMap, defaultLocale) {
102
+ const reference = localeMap.get(defaultLocale) ?? {};
103
+ const referenceKeys = Object.keys(reference);
104
+ const allLocales = [...localeMap.keys()].filter(l => l !== defaultLocale);
105
+ const keys = referenceKeys.map(key => {
106
+ const missingIn = [];
107
+ let status = 'complete';
108
+ for (const locale of allLocales) {
109
+ const localeKeys = localeMap.get(locale) ?? {};
110
+ if (!(key in localeKeys) || localeKeys[key] === '') {
111
+ missingIn.push(locale);
112
+ status = 'missing';
113
+ }
114
+ }
115
+ // If not all missing but some are, mark as fallback for those
116
+ if (status === 'missing' && missingIn.length < allLocales.length) {
117
+ status = 'fallback';
118
+ }
119
+ return {
120
+ key,
121
+ status,
122
+ missingIn,
123
+ referenceValue: (reference[key] ?? '').slice(0, 60),
124
+ };
125
+ });
126
+ // Keys that exist in non-reference locales but NOT in reference
127
+ // These are orphaned keys — we add them as 'missing' from reference perspective
128
+ for (const [locale, localeKeys] of localeMap) {
129
+ if (locale === defaultLocale)
130
+ continue;
131
+ for (const key of Object.keys(localeKeys)) {
132
+ if (!reference[key]) {
133
+ const existing = keys.find(k => k.key === key);
134
+ if (!existing) {
135
+ keys.push({
136
+ key,
137
+ status: 'missing',
138
+ missingIn: [defaultLocale],
139
+ referenceValue: '',
140
+ });
141
+ }
142
+ }
143
+ }
144
+ }
145
+ // Sort: missing first, then fallback, then complete
146
+ keys.sort((a, b) => {
147
+ const order = { missing: 0, fallback: 1, complete: 2 };
148
+ return order[a.status] - order[b.status] || a.key.localeCompare(b.key);
149
+ });
150
+ const summary = allLocales.map(locale => {
151
+ const total = referenceKeys.length;
152
+ const missing = keys.filter(k => k.missingIn.includes(locale)).length;
153
+ const complete = total - missing;
154
+ const fallback = 0; // simplified — fallback = missing for per-locale view
155
+ return {
156
+ locale,
157
+ total,
158
+ complete,
159
+ fallback,
160
+ missing,
161
+ percent: total > 0 ? Math.round((complete / total) * 100) : 100,
162
+ };
163
+ });
164
+ return { keys, summary };
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Derive locale from a URL pathname using Astro's i18n config pattern
168
+ // e.g. /fr/about → 'fr', /about → defaultLocale
169
+ // ---------------------------------------------------------------------------
170
+ export function deriveLocaleFromUrl(pathname, locales, defaultLocale) {
171
+ const segments = pathname.split('/').filter(Boolean);
172
+ const first = segments[0]?.toLowerCase();
173
+ if (first && locales.map(l => l.toLowerCase()).includes(first)) {
174
+ return locales.find(l => l.toLowerCase() === first) ?? defaultLocale;
175
+ }
176
+ return defaultLocale;
177
+ }
178
+ // ---------------------------------------------------------------------------
179
+ // Build locale-switched URL: replace or prepend the locale prefix
180
+ // ---------------------------------------------------------------------------
181
+ export function buildLocaleUrl(pathname, targetLocale, locales, defaultLocale, prefixDefault) {
182
+ const segments = pathname.split('/').filter(Boolean);
183
+ const firstIsLocale = segments.length > 0 &&
184
+ locales.map(l => l.toLowerCase()).includes(segments[0].toLowerCase());
185
+ // Remove existing locale prefix
186
+ const rest = firstIsLocale ? segments.slice(1) : segments;
187
+ // Add new locale prefix
188
+ const addPrefix = targetLocale !== defaultLocale || prefixDefault;
189
+ const newSegments = addPrefix ? [targetLocale, ...rest] : rest;
190
+ return '/' + newSegments.join('/');
191
+ }
192
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@shiftescape/astro-i18n-toolkit",
3
+ "version": "0.1.0",
4
+ "description": "Dev toolbar integration for Astro that shows a live i18n debugger — translation coverage map across all locales and a one-click locale switcher, library-agnostic.",
5
+ "keywords": [
6
+ "astro",
7
+ "astro-integration",
8
+ "withastro",
9
+ "dev-toolbar",
10
+ "i18n",
11
+ "internationalization",
12
+ "translation",
13
+ "tooling",
14
+ "dx"
15
+ ],
16
+ "author": {
17
+ "name": "Alvin James Bellero",
18
+ "email": "ajames.bellero@gmail.com",
19
+ "url": "https://github.com/shiftEscape"
20
+ },
21
+ "sponsor": {
22
+ "url": "https://github.com/sponsors/shiftEscape"
23
+ },
24
+ "homepage": "https://github.com/shiftEscape/astro-integrations/tree/main/packages/astro-i18n-toolkit#readme",
25
+ "bugs": {
26
+ "url": "https://github.com/shiftescape/astro-integrations/issues"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/shiftEscape/astro-integrations.git"
31
+ },
32
+ "license": "MIT",
33
+ "type": "module",
34
+ "exports": {
35
+ ".": "./dist/index.js"
36
+ },
37
+ "main": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "dev": "tsc -p tsconfig.json --watch",
47
+ "test": "tsc -p tsconfig.test.json && node --test dist-test/test/utils.test.js dist-test/test/integration.test.js",
48
+ "prepublishOnly": "npm test && npm run build"
49
+ },
50
+ "peerDependencies": {
51
+ "astro": "^4.0.0 || ^5.0.0 || ^6.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.0.0",
55
+ "astro": "^5.0.0",
56
+ "typescript": "^5.0.0"
57
+ },
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ }
61
+ }