@nuraly/lumenjs 0.1.1 → 0.1.2

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
@@ -105,6 +105,7 @@ Loaders run server-side on initial load (SSR) and are fetched via `/__nk_loader/
105
105
  | `query` | `Record<string, string>` | Query string parameters |
106
106
  | `url` | `string` | Request pathname |
107
107
  | `headers` | `Record<string, any>` | Request headers |
108
+ | `locale` | `string` | Current locale (when i18n is configured) |
108
109
 
109
110
  ### Redirects
110
111
 
@@ -223,6 +224,81 @@ Pages with loaders are automatically server-rendered using `@lit-labs/ssr`:
223
224
 
224
225
  Pages without loaders render client-side only (SPA mode). If SSR fails, LumenJS falls back gracefully to client-side rendering.
225
226
 
227
+ ## Internationalization (i18n)
228
+
229
+ LumenJS has built-in i18n support with URL-prefix-based locale routing.
230
+
231
+ ### Setup
232
+
233
+ 1. Add i18n config to `lumenjs.config.ts`:
234
+
235
+ ```typescript
236
+ export default {
237
+ title: 'My App',
238
+ i18n: {
239
+ locales: ['en', 'fr'],
240
+ defaultLocale: 'en',
241
+ prefixDefault: false, // / instead of /en/
242
+ },
243
+ };
244
+ ```
245
+
246
+ 2. Create translation files in `locales/`:
247
+
248
+ ```
249
+ my-app/
250
+ ├── locales/
251
+ │ ├── en.json # { "home.title": "Welcome", "nav.docs": "Docs" }
252
+ │ └── fr.json # { "home.title": "Bienvenue", "nav.docs": "Documentation" }
253
+ ├── pages/
254
+ └── lumenjs.config.ts
255
+ ```
256
+
257
+ ### Usage
258
+
259
+ ```typescript
260
+ import { t, getLocale, setLocale } from '@lumenjs/i18n';
261
+
262
+ @customElement('page-index')
263
+ export class PageIndex extends LitElement {
264
+ render() {
265
+ return html`<h1>${t('home.title')}</h1>`;
266
+ }
267
+ }
268
+ ```
269
+
270
+ ### API
271
+
272
+ | Function | Description |
273
+ |---|---|
274
+ | `t(key)` | Returns the translated string for the key, or the key itself if not found |
275
+ | `getLocale()` | Returns the current locale string |
276
+ | `setLocale(locale)` | Switches locale — sets cookie, navigates to the localized URL |
277
+
278
+ ### Locale Resolution
279
+
280
+ Locale is resolved in this order:
281
+
282
+ 1. URL prefix: `/fr/about` → locale `fr`, pathname `/about`
283
+ 2. Cookie `nk-locale` (set on explicit locale switch)
284
+ 3. `Accept-Language` header (SSR)
285
+ 4. Config `defaultLocale`
286
+
287
+ ### URL Routing
288
+
289
+ With `prefixDefault: false`, the default locale uses clean URLs:
290
+
291
+ | URL | Locale | Page |
292
+ |---|---|---|
293
+ | `/about` | `en` (default) | `pages/about.ts` |
294
+ | `/fr/about` | `fr` | `pages/about.ts` |
295
+
296
+ Routes are locale-agnostic — you don't need separate pages per locale. The router strips the locale prefix before matching and prepends it during navigation.
297
+
298
+ ### SSR
299
+
300
+ Translations are server-rendered. The `<html lang="...">` attribute is set dynamically, and translations are inlined in the response for hydration without flash of untranslated content.
301
+
226
302
  ## Integrations
227
303
 
228
304
  ### Tailwind CSS
@@ -110,6 +110,17 @@ export async function createDevServer(options) {
110
110
  name: 'lumenjs-index-html',
111
111
  configureServer(server) {
112
112
  server.middlewares.use((req, res, next) => {
113
+ // Guard against malformed percent-encoded URLs that crash Vite's transformIndexHtml
114
+ if (req.url) {
115
+ try {
116
+ decodeURIComponent(req.url);
117
+ }
118
+ catch {
119
+ res.statusCode = 400;
120
+ res.end('Bad Request');
121
+ return;
122
+ }
123
+ }
113
124
  if (req.url && !req.url.startsWith('/@') && !req.url.startsWith('/node_modules') &&
114
125
  !req.url.startsWith('/api/') && !req.url.startsWith('/__nk_loader/') &&
115
126
  !req.url.startsWith('/__nk_i18n/') &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuraly/lumenjs",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Full-stack Lit web component framework with file-based routing, server loaders, SSR, and API routes",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -14,7 +14,10 @@
14
14
  "scripts": {
15
15
  "build": "tsc",
16
16
  "dev": "tsc --watch",
17
- "prepublishOnly": "tsc"
17
+ "prepublishOnly": "tsc",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "test:coverage": "vitest run --coverage"
18
21
  },
19
22
  "keywords": [
20
23
  "lit",
@@ -38,14 +41,16 @@
38
41
  "author": "labidiaymen <labidi@aymen.co>",
39
42
  "license": "MIT",
40
43
  "dependencies": {
41
- "vite": "^5.4.0",
42
- "lit": "^3.1.0",
43
44
  "@lit-labs/ssr": "^3.2.0",
44
- "glob": "^10.3.0"
45
+ "glob": "^10.3.0",
46
+ "lit": "^3.1.0",
47
+ "vite": "^5.4.0"
45
48
  },
46
49
  "devDependencies": {
50
+ "@types/node": "^20.14.2",
51
+ "@vitest/coverage-v8": "^4.0.18",
47
52
  "typescript": "^5.4.5",
48
- "@types/node": "^20.14.2"
53
+ "vitest": "^4.0.18"
49
54
  },
50
55
  "engines": {
51
56
  "node": ">=18"