@better-i18n/vite 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.
@@ -0,0 +1,3 @@
1
+ export { betterI18n } from "./plugin.js";
2
+ export type { BetterI18nPluginOptions } from "./plugin.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { betterI18n } from "./plugin.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,57 @@
1
+ import type { Plugin } from "vite";
2
+ export interface BetterI18nPluginOptions {
3
+ /**
4
+ * Project identifier in "org/project" format.
5
+ * This is the single source of truth — `BetterI18nProvider` reads it
6
+ * from the injected `<script>` tag, so you don't need to pass it as a prop.
7
+ *
8
+ * @example "acme/dashboard"
9
+ */
10
+ project: string;
11
+ /**
12
+ * Default locale when no locale is detected from URL, cookie, or Accept-Language.
13
+ * @default "en"
14
+ */
15
+ defaultLocale?: string;
16
+ /**
17
+ * Cookie name used to persist the user's locale preference.
18
+ * @default "locale"
19
+ */
20
+ localeCookie?: string;
21
+ /**
22
+ * CDN base URL override.
23
+ * @default "https://cdn.better-i18n.com"
24
+ */
25
+ cdnBaseUrl?: string;
26
+ }
27
+ /**
28
+ * Vite plugin for Better i18n — server-side translation injection.
29
+ *
30
+ * Fetches translations on the server (dev server or build) and injects
31
+ * them into the HTML as a `<script id="__better_i18n__">` tag.
32
+ * `BetterI18nProvider` reads this data synchronously on first render,
33
+ * eliminating FOUC (Flash of Untranslated Content).
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * // vite.config.ts
38
+ * import { betterI18n } from '@better-i18n/vite'
39
+ *
40
+ * export default defineConfig({
41
+ * plugins: [
42
+ * betterI18n({ project: 'acme/dashboard' }),
43
+ * react(),
44
+ * ],
45
+ * })
46
+ * ```
47
+ *
48
+ * Then in your app — no project/locale/messages props needed:
49
+ * ```tsx
50
+ * <BetterI18nProvider>
51
+ * <LocaleDropdown />
52
+ * <App />
53
+ * </BetterI18nProvider>
54
+ * ```
55
+ */
56
+ export declare function betterI18n(options: BetterI18nPluginOptions): Plugin;
57
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CA8EnE"}
package/dist/plugin.js ADDED
@@ -0,0 +1,135 @@
1
+ import { createI18nCore, normalizeLocale } from "@better-i18n/core";
2
+ /**
3
+ * Vite plugin for Better i18n — server-side translation injection.
4
+ *
5
+ * Fetches translations on the server (dev server or build) and injects
6
+ * them into the HTML as a `<script id="__better_i18n__">` tag.
7
+ * `BetterI18nProvider` reads this data synchronously on first render,
8
+ * eliminating FOUC (Flash of Untranslated Content).
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // vite.config.ts
13
+ * import { betterI18n } from '@better-i18n/vite'
14
+ *
15
+ * export default defineConfig({
16
+ * plugins: [
17
+ * betterI18n({ project: 'acme/dashboard' }),
18
+ * react(),
19
+ * ],
20
+ * })
21
+ * ```
22
+ *
23
+ * Then in your app — no project/locale/messages props needed:
24
+ * ```tsx
25
+ * <BetterI18nProvider>
26
+ * <LocaleDropdown />
27
+ * <App />
28
+ * </BetterI18nProvider>
29
+ * ```
30
+ */
31
+ export function betterI18n(options) {
32
+ const { project, defaultLocale = "en", localeCookie = "locale", cdnBaseUrl, } = options;
33
+ // Singleton core instance — TtlCache shared across requests
34
+ const i18nCore = createI18nCore({
35
+ project,
36
+ defaultLocale,
37
+ cdnBaseUrl,
38
+ });
39
+ // Request-scoped locale detection (middleware → transformIndexHtml bridge)
40
+ const requestLocales = new Map();
41
+ return {
42
+ name: "better-i18n",
43
+ configureServer(server) {
44
+ // Middleware runs BEFORE Vite serves HTML — detects locale from request
45
+ server.middlewares.use((req, _res, next) => {
46
+ if (!req.url)
47
+ return next();
48
+ const locale = detectLocaleFromRequest(req.headers.cookie, req.headers["accept-language"], req.url, localeCookie, defaultLocale);
49
+ requestLocales.set(req.url, locale);
50
+ next();
51
+ });
52
+ },
53
+ transformIndexHtml: {
54
+ order: "pre",
55
+ handler: async (_html, ctx) => {
56
+ // Resolve locale: middleware-detected → default
57
+ const requestUrl = ctx.originalUrl || ctx.path;
58
+ const locale = requestLocales.get(requestUrl) || defaultLocale;
59
+ requestLocales.delete(requestUrl);
60
+ // Fetch translations + languages server-side (TtlCache makes this fast)
61
+ const [messages, languages] = await Promise.all([
62
+ i18nCore.getMessages(locale),
63
+ i18nCore.getLanguages(),
64
+ ]);
65
+ // Inject as <script> tag — provider reads this synchronously
66
+ const payload = JSON.stringify({
67
+ project,
68
+ locale,
69
+ messages,
70
+ languages,
71
+ });
72
+ return {
73
+ html: _html,
74
+ tags: [
75
+ {
76
+ tag: "script",
77
+ attrs: {
78
+ id: "__better_i18n__",
79
+ type: "application/json",
80
+ },
81
+ children: payload,
82
+ injectTo: "head",
83
+ },
84
+ ],
85
+ };
86
+ },
87
+ },
88
+ };
89
+ }
90
+ // ─── Locale Detection ─────────────────────────────────────────────
91
+ /**
92
+ * Detect locale from request context.
93
+ * Priority: URL path segment → cookie → Accept-Language → default
94
+ */
95
+ function detectLocaleFromRequest(cookieHeader, acceptLanguageHeader, url, cookieName, defaultLocale) {
96
+ // 1. URL path locale (e.g., /tr/about → "tr")
97
+ const pathname = url.split("?")[0] || "/";
98
+ const segments = pathname.split("/").filter(Boolean);
99
+ const firstSegment = segments[0];
100
+ if (firstSegment && /^[a-z]{2}(-[a-z]{2,4})?$/i.test(firstSegment)) {
101
+ return normalizeLocale(firstSegment);
102
+ }
103
+ // 2. Cookie locale
104
+ const cookieLocale = parseCookieValue(cookieHeader, cookieName);
105
+ if (cookieLocale) {
106
+ return normalizeLocale(cookieLocale);
107
+ }
108
+ // 3. Accept-Language header (first match)
109
+ if (acceptLanguageHeader) {
110
+ const preferred = parseAcceptLanguageFirst(acceptLanguageHeader);
111
+ if (preferred) {
112
+ return normalizeLocale(preferred);
113
+ }
114
+ }
115
+ return defaultLocale;
116
+ }
117
+ /** Extract a single cookie value by name */
118
+ function parseCookieValue(header, name) {
119
+ if (!header)
120
+ return null;
121
+ const match = header.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
122
+ return match?.[1]?.trim() || null;
123
+ }
124
+ /** Extract the highest-priority language from Accept-Language header */
125
+ function parseAcceptLanguageFirst(header) {
126
+ const first = header.split(",")[0];
127
+ if (!first)
128
+ return null;
129
+ const tag = first.split(";")[0]?.trim();
130
+ if (!tag || tag === "*")
131
+ return null;
132
+ // Return base language (e.g., "tr-TR" → "tr")
133
+ return tag.split("-")[0] || null;
134
+ }
135
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@better-i18n/vite",
3
+ "version": "0.1.0",
4
+ "description": "Vite plugin for Better i18n — SSR translation injection, zero FOUC",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/better-i18n/better-i18n.git",
9
+ "directory": "packages/vite"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/better-i18n/better-i18n/issues"
13
+ },
14
+ "homepage": "https://github.com/better-i18n/better-i18n/tree/main/packages/vite",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "bun": "./src/index.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "package.json",
29
+ "README.md"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "keywords": [
35
+ "i18n",
36
+ "localization",
37
+ "vite",
38
+ "vite-plugin",
39
+ "ssr",
40
+ "better-i18n"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsc",
44
+ "typecheck": "tsc --noEmit",
45
+ "clean": "rm -rf dist",
46
+ "prepublishOnly": "bun run build"
47
+ },
48
+ "dependencies": {
49
+ "@better-i18n/core": "^0.3.0"
50
+ },
51
+ "peerDependencies": {
52
+ "vite": ">=5.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "vite": "^6.3.5",
56
+ "typescript": "~5.9.2"
57
+ }
58
+ }