@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 +21 -0
- package/README.md +69 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +90 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +3 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +29 -0
- package/dist/middleware.js.map +1 -0
- package/dist/toolbar-app.d.ts +3 -0
- package/dist/toolbar-app.d.ts.map +1 -0
- package/dist/toolbar-app.js +419 -0
- package/dist/toolbar-app.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +192 -0
- package/dist/utils.js.map +1 -0
- package/package.json +61 -0
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
|
+
[](http://npm.im/@shiftescape/astro-i18n-toolkit) [](https://npm-stat.com/charts.html?package=@shiftescape/astro-i18n-toolkit&from=2016-11-24) [](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)
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -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
|
+
}
|