@verbaly/sveltekit 0.47.0 → 0.48.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/README.md CHANGED
@@ -37,14 +37,14 @@ export default { plugins: [verbaly(), sveltekit()] };
37
37
  <html lang="%verbaly.lang%" dir="%verbaly.dir%"></html>
38
38
  ```
39
39
 
40
- **3. Server hook**: negotiates the locale per request:
40
+ **3. Server hook**: resolves the locale per request:
41
41
 
42
42
  ```ts
43
43
  // src/hooks.server.ts
44
44
  import { verbalyHandle } from '@verbaly/sveltekit';
45
- import { locales } from 'virtual:verbaly';
45
+ import { locales, routing } from 'virtual:verbaly';
46
46
 
47
- export const handle = verbalyHandle({ locales });
47
+ export const handle = verbalyHandle({ locales, routing });
48
48
  ```
49
49
 
50
50
  ```ts
@@ -56,6 +56,23 @@ declare namespace App {
56
56
  }
57
57
  ```
58
58
 
59
+ Passing `routing` is what makes the address decide the language: on `/es/docs` the answer is Spanish
60
+ whatever the visitor's cookie says. Leave it out and the cookie and browser decide, which is the
61
+ right answer only when your URLs carry no language.
62
+
63
+ **3b. If your URLs carry the language**, add the universal hook so one route tree serves them all:
64
+
65
+ ```ts
66
+ // src/hooks.ts
67
+ import { verbalyReroute } from '@verbaly/sveltekit';
68
+ import { locales } from 'virtual:verbaly';
69
+
70
+ export const reroute = verbalyReroute({ locales });
71
+ ```
72
+
73
+ `/es/docs` is then served by your `/docs` route, so you never duplicate route folders per language.
74
+ It goes in `hooks.ts`, not `hooks.server.ts`: client-side navigation needs it too.
75
+
59
76
  **4. Hand the locale to the client**:
60
77
 
61
78
  ```ts
@@ -77,7 +94,7 @@ export const load = async ({ data }) => ({
77
94
 
78
95
  (`createInstance(options)` remains available when you need custom options.)
79
96
 
80
- ```html
97
+ ```svelte
81
98
  <!-- src/routes/+layout.svelte -->
82
99
  <script>
83
100
  import { provideVerbaly } from '@verbaly/svelte';
@@ -91,7 +108,7 @@ export const load = async ({ data }) => ({
91
108
 
92
109
  **6. Switching languages** (client): persists the cookie the server hook reads:
93
110
 
94
- ```html
111
+ ```svelte
95
112
  <script>
96
113
  import { switchLocale } from '@verbaly/sveltekit';
97
114
  import { useVerbaly } from '@verbaly/svelte';
@@ -99,17 +116,19 @@ export const load = async ({ data }) => ({
99
116
  const verbaly = useVerbaly();
100
117
  </script>
101
118
 
102
- <button on:click="{()" ="">switchLocale(verbaly, 'es')}>Español</button>
119
+ <button onclick={() => switchLocale(verbaly, 'es')}>Español</button>
103
120
  ```
104
121
 
105
122
  ## 📖 API
106
123
 
107
- | Export | What it does |
108
- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
109
- | `verbalyHandle({ locales, fallback?, cookie? })` | SvelteKit `handle` hook: resolves the request locale (cookie → `Accept-Language` → fallback), sets `event.locals.verbalyLocale`, fills `%verbaly.lang%` and `%verbaly.dir%` in `app.html`. |
110
- | `switchLocale(instance, locale, { cookie?, maxAge? })` | Awaits the catalog, switches the locale, writes the cookie and syncs `<html lang>`. SSR-safe. |
111
- | `LOCALE_COOKIE` | Default cookie name (`verbaly-locale`), shared with core's `localStorage` key. |
124
+ | Export | What it does |
125
+ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
126
+ | `verbalyHandle({ locales, routing?, base?, fallback?, cookie? })` | SvelteKit `handle` hook: resolves the request locale (URL → cookie → `Accept-Language` → fallback), sets `event.locals.verbalyLocale`, fills `%verbaly.lang%` and `%verbaly.dir%` in `app.html`. |
127
+ | `verbalyReroute({ locales, base? })` | Universal `reroute` hook for `hooks.ts`: `/es/docs` routes as `/docs`, so one route tree serves every language. |
128
+ | `switchLocale(instance, locale, { cookie?, maxAge? })` | Awaits the catalog, switches the locale, writes the cookie and syncs `<html lang>`. SSR-safe. |
129
+ | `LOCALE_COOKIE` | Default cookie name (`verbaly-locale`), shared with core's `localStorage` key. |
112
130
 
131
+ - **`routing` decides whether the URL is consulted.** With `prefix-except-source`, a prefixed URL is the answer and an unprefixed one means your source language, so a saved choice can never translate your source tree. Without `routing`, only the cookie and the header are read.
113
132
  - `cookie: false` disables cookie reading/writing (pure `Accept-Language`).
114
133
  - No dependency on `@sveltejs/kit`: the hook is typed structurally and stays compatible across Kit 2.x.
115
134
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { SwitchLocaleOptions, switchLocale } from "verbaly";
1
+ import { Routing, SwitchLocaleOptions, switchLocale } from "verbaly";
2
2
  //#region src/index.d.ts
3
3
  declare const LOCALE_COOKIE: string;
4
4
  interface HandleEvent {
5
5
  request: Request;
6
+ url: URL;
6
7
  cookies: {
7
8
  get(name: string): string | undefined;
8
9
  };
@@ -24,8 +25,17 @@ interface VerbalyHandleOptions {
24
25
  locales: string[];
25
26
  fallback?: string;
26
27
  cookie?: string | false;
28
+ routing?: Routing;
29
+ base?: string;
27
30
  }
28
31
  declare function verbalyHandle(options: VerbalyHandleOptions): ({ event, resolve }: HandleInput) => Response | Promise<Response>;
32
+ interface VerbalyRerouteOptions {
33
+ locales: string[];
34
+ base?: string;
35
+ }
36
+ declare function verbalyReroute(options: VerbalyRerouteOptions): ({ url }: {
37
+ url: URL;
38
+ }) => string;
29
39
  //#endregion
30
- export { LOCALE_COOKIE, type SwitchLocaleOptions, VerbalyHandleOptions, switchLocale, verbalyHandle };
40
+ export { LOCALE_COOKIE, type SwitchLocaleOptions, VerbalyHandleOptions, VerbalyRerouteOptions, switchLocale, verbalyHandle, verbalyReroute };
31
41
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { LOCALE_STORAGE_KEY, localeDirection, resolveRequestLocale, switchLocale } from "verbaly";
1
+ import { LOCALE_STORAGE_KEY, localeDirection, resolveRequestLocale, stripLocalePath, switchLocale } from "verbaly";
2
2
  //#region src/index.ts
3
3
  const LOCALE_COOKIE = LOCALE_STORAGE_KEY;
4
4
  const LANG_PLACEHOLDER = "%verbaly.lang%";
@@ -10,6 +10,9 @@ function verbalyHandle(options) {
10
10
  return ({ event, resolve }) => {
11
11
  const resolved = resolveRequestLocale({
12
12
  supported: locales,
13
+ path: event.url.pathname,
14
+ routing: options.routing,
15
+ base: options.base,
13
16
  cookie: cookie ? event.cookies.get(cookie) : void 0,
14
17
  header: event.request.headers.get("accept-language"),
15
18
  fallback
@@ -18,7 +21,16 @@ function verbalyHandle(options) {
18
21
  return resolve(event, { transformPageChunk: ({ html }) => html.replaceAll(LANG_PLACEHOLDER, resolved).replaceAll(DIR_PLACEHOLDER, localeDirection(resolved)) });
19
22
  };
20
23
  }
24
+ function verbalyReroute(options) {
25
+ const { locales } = options;
26
+ if (!Array.isArray(locales) || locales.length === 0) throw new Error("[verbaly] verbalyReroute needs `locales`: pass the `locales` export from 'virtual:verbaly'");
27
+ return ({ url }) => stripLocalePath({
28
+ supported: locales,
29
+ path: url.pathname,
30
+ base: options.base
31
+ });
32
+ }
21
33
  //#endregion
22
- export { LOCALE_COOKIE, switchLocale, verbalyHandle };
34
+ export { LOCALE_COOKIE, switchLocale, verbalyHandle, verbalyReroute };
23
35
 
24
36
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { LOCALE_STORAGE_KEY, localeDirection, resolveRequestLocale } from 'verbaly';\n\n// derived from core's localStorage key: one identity per user across channels\nexport const LOCALE_COOKIE: string = LOCALE_STORAGE_KEY;\nconst LANG_PLACEHOLDER = '%verbaly.lang%';\nconst DIR_PLACEHOLDER = '%verbaly.dir%';\n\n// structural subset of @sveltejs/kit: no runtime or type dependency on kit\ninterface HandleEvent {\n request: Request;\n cookies: { get(name: string): string | undefined };\n locals: { verbalyLocale?: string };\n}\ninterface ResolveOptions {\n transformPageChunk?(input: { html: string; done: boolean }): string | undefined;\n}\ninterface HandleInput {\n event: HandleEvent;\n resolve(event: HandleEvent, opts?: ResolveOptions): Response | Promise<Response>;\n}\n\nexport interface VerbalyHandleOptions {\n locales: string[];\n fallback?: string;\n cookie?: string | false;\n}\n\n// server hook: sets event.locals.verbalyLocale and fills %verbaly.lang% / %verbaly.dir%\nexport function verbalyHandle(options: VerbalyHandleOptions) {\n const { locales, cookie = LOCALE_COOKIE } = options;\n if (!Array.isArray(locales) || locales.length === 0) {\n throw new Error(\n \"[verbaly] verbalyHandle needs `locales`: pass the `locales` export from 'virtual:verbaly' \" +\n '(requires @verbaly/vite ≥0.16) or list them yourself: verbalyHandle({ locales: [...] })',\n );\n }\n const fallback = options.fallback ?? locales[0]!;\n\n return ({ event, resolve }: HandleInput): Response | Promise<Response> => {\n const resolved = resolveRequestLocale({\n supported: locales,\n cookie: cookie ? event.cookies.get(cookie) : undefined,\n header: event.request.headers.get('accept-language'),\n fallback,\n });\n\n event.locals.verbalyLocale = resolved;\n return resolve(event, {\n transformPageChunk: ({ html }) =>\n html\n .replaceAll(LANG_PLACEHOLDER, resolved)\n .replaceAll(DIR_PLACEHOLDER, localeDirection(resolved)),\n });\n };\n}\n\n// switchLocale moved to core in 0.18.0 (shared with @verbaly/nuxt): same API, re-exported\nexport { switchLocale } from 'verbaly';\nexport type { SwitchLocaleOptions } from 'verbaly';\n"],"mappings":";;AAGA,MAAa,gBAAwB;AACrC,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AAuBxB,SAAgB,cAAc,SAA+B;CAC3D,MAAM,EAAE,SAAS,SAAS,kBAAkB;CAC5C,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAChD,MAAM,IAAI,MACR,mLAEF;CAEF,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAE7C,QAAQ,EAAE,OAAO,cAAyD;EACxE,MAAM,WAAW,qBAAqB;GACpC,WAAW;GACX,QAAQ,SAAS,MAAM,QAAQ,IAAI,MAAM,IAAI,KAAA;GAC7C,QAAQ,MAAM,QAAQ,QAAQ,IAAI,iBAAiB;GACnD;EACF,CAAC;EAED,MAAM,OAAO,gBAAgB;EAC7B,OAAO,QAAQ,OAAO,EACpB,qBAAqB,EAAE,WACrB,KACG,WAAW,kBAAkB,QAAQ,CAAC,CACtC,WAAW,iBAAiB,gBAAgB,QAAQ,CAAC,EAC5D,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n LOCALE_STORAGE_KEY,\n localeDirection,\n resolveRequestLocale,\n stripLocalePath,\n type Routing,\n} from 'verbaly';\n\n// derived from core's localStorage key: one identity per user across channels\nexport const LOCALE_COOKIE: string = LOCALE_STORAGE_KEY;\nconst LANG_PLACEHOLDER = '%verbaly.lang%';\nconst DIR_PLACEHOLDER = '%verbaly.dir%';\n\n// structural subset of @sveltejs/kit: no runtime or type dependency on kit\ninterface HandleEvent {\n request: Request;\n url: URL;\n cookies: { get(name: string): string | undefined };\n locals: { verbalyLocale?: string };\n}\ninterface ResolveOptions {\n transformPageChunk?(input: { html: string; done: boolean }): string | undefined;\n}\ninterface HandleInput {\n event: HandleEvent;\n resolve(event: HandleEvent, opts?: ResolveOptions): Response | Promise<Response>;\n}\n\nexport interface VerbalyHandleOptions {\n locales: string[];\n fallback?: string;\n cookie?: string | false;\n routing?: Routing;\n base?: string;\n}\n\n// server hook: sets event.locals.verbalyLocale and fills %verbaly.lang% / %verbaly.dir%\nexport function verbalyHandle(options: VerbalyHandleOptions) {\n const { locales, cookie = LOCALE_COOKIE } = options;\n if (!Array.isArray(locales) || locales.length === 0) {\n throw new Error(\n \"[verbaly] verbalyHandle needs `locales`: pass the `locales` export from 'virtual:verbaly' \" +\n '(requires @verbaly/vite ≥0.16) or list them yourself: verbalyHandle({ locales: [...] })',\n );\n }\n const fallback = options.fallback ?? locales[0]!;\n\n return ({ event, resolve }: HandleInput): Response | Promise<Response> => {\n const resolved = resolveRequestLocale({\n supported: locales,\n path: event.url.pathname,\n routing: options.routing,\n base: options.base,\n cookie: cookie ? event.cookies.get(cookie) : undefined,\n header: event.request.headers.get('accept-language'),\n fallback,\n });\n\n event.locals.verbalyLocale = resolved;\n return resolve(event, {\n transformPageChunk: ({ html }) =>\n html\n .replaceAll(LANG_PLACEHOLDER, resolved)\n .replaceAll(DIR_PLACEHOLDER, localeDirection(resolved)),\n });\n };\n}\n\nexport interface VerbalyRerouteOptions {\n locales: string[];\n base?: string;\n}\n\n// universal hook (hooks.ts, never hooks.server.ts): /es/docs routes as /docs, one route tree\nexport function verbalyReroute(options: VerbalyRerouteOptions) {\n const { locales } = options;\n if (!Array.isArray(locales) || locales.length === 0) {\n throw new Error(\n \"[verbaly] verbalyReroute needs `locales`: pass the `locales` export from 'virtual:verbaly'\",\n );\n }\n return ({ url }: { url: URL }): string =>\n stripLocalePath({ supported: locales, path: url.pathname, base: options.base });\n}\n\n// switchLocale moved to core in 0.18.0 (shared with @verbaly/nuxt): same API, re-exported\nexport { switchLocale } from 'verbaly';\nexport type { SwitchLocaleOptions } from 'verbaly';\n"],"mappings":";;AASA,MAAa,gBAAwB;AACrC,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AA0BxB,SAAgB,cAAc,SAA+B;CAC3D,MAAM,EAAE,SAAS,SAAS,kBAAkB;CAC5C,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAChD,MAAM,IAAI,MACR,mLAEF;CAEF,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAE7C,QAAQ,EAAE,OAAO,cAAyD;EACxE,MAAM,WAAW,qBAAqB;GACpC,WAAW;GACX,MAAM,MAAM,IAAI;GAChB,SAAS,QAAQ;GACjB,MAAM,QAAQ;GACd,QAAQ,SAAS,MAAM,QAAQ,IAAI,MAAM,IAAI,KAAA;GAC7C,QAAQ,MAAM,QAAQ,QAAQ,IAAI,iBAAiB;GACnD;EACF,CAAC;EAED,MAAM,OAAO,gBAAgB;EAC7B,OAAO,QAAQ,OAAO,EACpB,qBAAqB,EAAE,WACrB,KACG,WAAW,kBAAkB,QAAQ,CAAC,CACtC,WAAW,iBAAiB,gBAAgB,QAAQ,CAAC,EAC5D,CAAC;CACH;AACF;AAQA,SAAgB,eAAe,SAAgC;CAC7D,MAAM,EAAE,YAAY;CACpB,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAChD,MAAM,IAAI,MACR,4FACF;CAEF,QAAQ,EAAE,UACR,gBAAgB;EAAE,WAAW;EAAS,MAAM,IAAI;EAAU,MAAM,QAAQ;CAAK,CAAC;AAClF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/sveltekit",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "description": "SvelteKit SSR integration for Verbaly: per-request locale negotiation and flash-free hydration.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -40,12 +40,12 @@
40
40
  "access": "public"
41
41
  },
42
42
  "peerDependencies": {
43
- "verbaly": "^0.47.0"
43
+ "verbaly": "^0.48.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@sveltejs/kit": "^2.70.3",
47
47
  "svelte": "^5.57.0",
48
- "verbaly": "^0.47.0"
48
+ "verbaly": "^0.48.0"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsdown",