@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 +76 -0
- package/dist/dev-server/server.js +11 -0
- package/package.json +11 -6
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.
|
|
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
|
-
"
|
|
53
|
+
"vitest": "^4.0.18"
|
|
49
54
|
},
|
|
50
55
|
"engines": {
|
|
51
56
|
"node": ">=18"
|