@pantho075/locale 0.1.0 → 0.1.1
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 +5 -37
- package/dist/index.cjs +3 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,12 +55,10 @@ console.log(title, home.header, home.footer);
|
|
|
55
55
|
|
|
56
56
|
### Adding your own keys
|
|
57
57
|
|
|
58
|
-
Edit `src/locales/en.ts` (and `bn.ts`, `ne.ts`) in this package, or fork it. Each file is just a
|
|
58
|
+
Edit `src/locales/en.ts` (and `bn.ts`, `ne.ts`) in this package, or fork it. Each file is just a plain object exported as default:
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
// src/locales/en.ts
|
|
62
|
-
import { withNullTopLevel } from "../normalize";
|
|
63
|
-
|
|
64
62
|
const data = {
|
|
65
63
|
title: "Welcome",
|
|
66
64
|
home: {
|
|
@@ -70,44 +68,14 @@ const data = {
|
|
|
70
68
|
greeting: "Hi there",
|
|
71
69
|
};
|
|
72
70
|
|
|
73
|
-
export default
|
|
71
|
+
export default data;
|
|
74
72
|
```
|
|
75
73
|
|
|
76
74
|
### Adding a new locale
|
|
77
75
|
|
|
78
|
-
1. Create `src/locales/<code>.ts` that exports
|
|
76
|
+
1. Create `src/locales/<code>.ts` that exports the locale object as default.
|
|
79
77
|
2. Import it in `src/getTranslation.ts` and add it to the `locales` map.
|
|
80
78
|
|
|
81
|
-
## Missing-value semantics
|
|
82
|
-
|
|
83
|
-
This is important — read it.
|
|
84
|
-
|
|
85
|
-
| Situation | Returned value |
|
|
86
|
-
| ---------------------------------------- | ------------------------------------------- |
|
|
87
|
-
| Top-level key is **absent** from source | `null` (Proxy returns null on miss) |
|
|
88
|
-
| Nested key is **absent** from source | `undefined` (standard JS) |
|
|
89
|
-
| Key is present with value `null` | `null` |
|
|
90
|
-
| Key is present with a string/object/etc. | that value |
|
|
91
|
-
|
|
92
|
-
So:
|
|
93
|
-
|
|
94
|
-
- `useTranslation('en').totallyMissing` → `null`
|
|
95
|
-
- `useTranslation('en').home.missing` → `undefined`
|
|
96
|
-
- `useTranslation('en').home.footer` (where `home.footer` isn't in source) → `undefined`
|
|
97
|
-
|
|
98
|
-
If you need `null` for a missing nested key, write it explicitly in the source:
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
const data = {
|
|
102
|
-
home: {
|
|
103
|
-
header: "Hello",
|
|
104
|
-
footer: null, // explicit null, not "missing"
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
The Proxy only wraps the **top level**. Nested objects are returned by reference, so they keep standard JS behavior — missing nested keys read as `undefined` and you can iterate them with `Object.keys()` etc.
|
|
110
|
-
|
|
111
79
|
## API
|
|
112
80
|
|
|
113
81
|
### `getTranslation<T>(lang: string): T`
|
|
@@ -132,7 +100,7 @@ React hook wrapping `getTranslation`. Memoized on `lang` — same language retur
|
|
|
132
100
|
```ts
|
|
133
101
|
import { useTranslation } from "@pantho075/locale";
|
|
134
102
|
|
|
135
|
-
const { title } = useTranslation("en"); // string |
|
|
103
|
+
const { title } = useTranslation("en"); // string | undefined
|
|
136
104
|
const en = useTranslation<English>("en"); // typed
|
|
137
105
|
```
|
|
138
106
|
|
|
@@ -144,7 +112,7 @@ The default return type — `Record<string, unknown>`. Most consumers will const
|
|
|
144
112
|
|
|
145
113
|
Because the package ships its own locale data, it doesn't need to scan the consumer's filesystem, doesn't need a bundler alias, and doesn't need `resolveJsonModule`. The data is just regular TypeScript that gets tree-shaken and bundled like any other code.
|
|
146
114
|
|
|
147
|
-
If you want to override locales from your own app, you can fork the package
|
|
115
|
+
If you want to override locales from your own app, you can fork the package and build your own locale map.
|
|
148
116
|
|
|
149
117
|
## License
|
|
150
118
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,18 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
|
|
5
|
-
// src/normalize.ts
|
|
6
|
-
function withNullTopLevel(data4) {
|
|
7
|
-
return new Proxy(data4, {
|
|
8
|
-
get(target, prop, receiver) {
|
|
9
|
-
if (typeof prop === "string" && !(prop in target)) {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
return Reflect.get(target, prop, receiver);
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
5
|
// src/locales/bn.ts
|
|
18
6
|
var data = {
|
|
19
7
|
title: "\u09B6\u09BF\u09B0\u09CB\u09A8\u09BE\u09AE",
|
|
@@ -22,7 +10,7 @@ var data = {
|
|
|
22
10
|
footer: "\u09AB\u09C1\u099F\u09BE\u09B0"
|
|
23
11
|
}
|
|
24
12
|
};
|
|
25
|
-
var bn_default =
|
|
13
|
+
var bn_default = data;
|
|
26
14
|
|
|
27
15
|
// src/locales/en.ts
|
|
28
16
|
var data2 = {
|
|
@@ -32,7 +20,7 @@ var data2 = {
|
|
|
32
20
|
footer: "this is footer"
|
|
33
21
|
}
|
|
34
22
|
};
|
|
35
|
-
var en_default =
|
|
23
|
+
var en_default = data2;
|
|
36
24
|
|
|
37
25
|
// src/locales/ne.ts
|
|
38
26
|
var data3 = {
|
|
@@ -42,7 +30,7 @@ var data3 = {
|
|
|
42
30
|
footer: "this is footer"
|
|
43
31
|
}
|
|
44
32
|
};
|
|
45
|
-
var ne_default =
|
|
33
|
+
var ne_default = data3;
|
|
46
34
|
|
|
47
35
|
// src/getTranslation.ts
|
|
48
36
|
var EMPTY = Object.freeze({});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/locales/bn.ts","../src/locales/en.ts","../src/locales/ne.ts","../src/getTranslation.ts","../src/useTranslation.ts"],"names":["data","useMemo"],"mappings":";;;;;AAAA,IAAM,IAAA,GAAO;AAAA,EACX,KAAA,EAAO,4CAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gCAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQ,IAAA;;;ACRf,IAAMA,KAAAA,GAAO;AAAA,EACX,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gBAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQA,KAAAA;;;ACRf,IAAMA,KAAAA,GAAO;AAAA,EACX,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gBAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQA,KAAAA;;;ACFf,IAAM,KAAA,GAAyB,MAAA,CAAO,MAAA,CAAO,EAAE,CAAA;AAQ/C,IAAM,OAAA,GAA2C;AAAA,EAC/C,EAAA,EAAA,UAAA;AAAA,EACA,EAAA,EAAA,UAAA;AAAA,EACA,EAAA,EAAA;AACF,CAAA;AASO,SAAS,eACd,IAAA,EACG;AACH,EAAA,OAAQ,OAAA,CAAQ,IAAI,CAAA,IAAK,KAAA;AAC3B;ACpBO,SAAS,eACd,IAAA,EACG;AACH,EAAA,OAAOC,cAAQ,MAAM,cAAA,CAAkB,IAAI,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACtD","file":"index.cjs","sourcesContent":["const data = {\n title: \"শিরোনাম\",\n home: {\n header: \"হেডার\",\n footer: \"ফুটার\",\n },\n};\n\nexport default data;\n","const data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default data;\n","const data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default data;\n","import bn from \"./locales/bn\";\nimport en from \"./locales/en\";\nimport ne from \"./locales/ne\";\nimport type { TranslationData } from \"./types\";\n\n/** Shared empty fallback for unknown languages. Frozen so it can't be mutated. */\nconst EMPTY: TranslationData = Object.freeze({});\n\n/**\n * Locale registry. Adding a new language means:\n * 1. drop a `src/locales/<code>.ts` that exports the locale object as default\n * 2. import it here\n * 3. add it to this map\n */\nconst locales: Record<string, TranslationData> = {\n en,\n bn,\n ne,\n};\n\n/**\n * Returns the translation bundle for the given language code.\n * Unknown / undefined / empty values fall back to a stable empty object.\n *\n * Framework-agnostic: works in React, Next.js server components, Vue,\n * Svelte, or vanilla Node scripts.\n */\nexport function getTranslation<T extends object = TranslationData>(\n lang: string,\n): T {\n return (locales[lang] ?? EMPTY) as T;\n}\n","import { useMemo } from \"react\";\nimport { getTranslation } from \"./getTranslation\";\nimport type { TranslationData } from \"./types\";\n\n/**\n * React hook: returns the translation bundle for the given language.\n *\n * Memoized on `lang`, so the same language produces a stable object\n * reference across re-renders. Switching language swaps the reference\n * (and any consumers destructuring top-level keys will re-render).\n */\nexport function useTranslation<T extends object = TranslationData>(\n lang: string,\n): T {\n return useMemo(() => getTranslation<T>(lang), [lang]);\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
|
|
3
|
-
// src/normalize.ts
|
|
4
|
-
function withNullTopLevel(data4) {
|
|
5
|
-
return new Proxy(data4, {
|
|
6
|
-
get(target, prop, receiver) {
|
|
7
|
-
if (typeof prop === "string" && !(prop in target)) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
return Reflect.get(target, prop, receiver);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
3
|
// src/locales/bn.ts
|
|
16
4
|
var data = {
|
|
17
5
|
title: "\u09B6\u09BF\u09B0\u09CB\u09A8\u09BE\u09AE",
|
|
@@ -20,7 +8,7 @@ var data = {
|
|
|
20
8
|
footer: "\u09AB\u09C1\u099F\u09BE\u09B0"
|
|
21
9
|
}
|
|
22
10
|
};
|
|
23
|
-
var bn_default =
|
|
11
|
+
var bn_default = data;
|
|
24
12
|
|
|
25
13
|
// src/locales/en.ts
|
|
26
14
|
var data2 = {
|
|
@@ -30,7 +18,7 @@ var data2 = {
|
|
|
30
18
|
footer: "this is footer"
|
|
31
19
|
}
|
|
32
20
|
};
|
|
33
|
-
var en_default =
|
|
21
|
+
var en_default = data2;
|
|
34
22
|
|
|
35
23
|
// src/locales/ne.ts
|
|
36
24
|
var data3 = {
|
|
@@ -40,7 +28,7 @@ var data3 = {
|
|
|
40
28
|
footer: "this is footer"
|
|
41
29
|
}
|
|
42
30
|
};
|
|
43
|
-
var ne_default =
|
|
31
|
+
var ne_default = data3;
|
|
44
32
|
|
|
45
33
|
// src/getTranslation.ts
|
|
46
34
|
var EMPTY = Object.freeze({});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/locales/bn.ts","../src/locales/en.ts","../src/locales/ne.ts","../src/getTranslation.ts","../src/useTranslation.ts"],"names":["data"],"mappings":";;;AAAA,IAAM,IAAA,GAAO;AAAA,EACX,KAAA,EAAO,4CAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gCAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQ,IAAA;;;ACRf,IAAMA,KAAAA,GAAO;AAAA,EACX,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gBAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQA,KAAAA;;;ACRf,IAAMA,KAAAA,GAAO;AAAA,EACX,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,gBAAA;AAAA,IACR,MAAA,EAAQ;AAAA;AAEZ,CAAA;AAEA,IAAO,UAAA,GAAQA,KAAAA;;;ACFf,IAAM,KAAA,GAAyB,MAAA,CAAO,MAAA,CAAO,EAAE,CAAA;AAQ/C,IAAM,OAAA,GAA2C;AAAA,EAC/C,EAAA,EAAA,UAAA;AAAA,EACA,EAAA,EAAA,UAAA;AAAA,EACA,EAAA,EAAA;AACF,CAAA;AASO,SAAS,eACd,IAAA,EACG;AACH,EAAA,OAAQ,OAAA,CAAQ,IAAI,CAAA,IAAK,KAAA;AAC3B;ACpBO,SAAS,eACd,IAAA,EACG;AACH,EAAA,OAAO,QAAQ,MAAM,cAAA,CAAkB,IAAI,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACtD","file":"index.js","sourcesContent":["const data = {\n title: \"শিরোনাম\",\n home: {\n header: \"হেডার\",\n footer: \"ফুটার\",\n },\n};\n\nexport default data;\n","const data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default data;\n","const data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default data;\n","import bn from \"./locales/bn\";\nimport en from \"./locales/en\";\nimport ne from \"./locales/ne\";\nimport type { TranslationData } from \"./types\";\n\n/** Shared empty fallback for unknown languages. Frozen so it can't be mutated. */\nconst EMPTY: TranslationData = Object.freeze({});\n\n/**\n * Locale registry. Adding a new language means:\n * 1. drop a `src/locales/<code>.ts` that exports the locale object as default\n * 2. import it here\n * 3. add it to this map\n */\nconst locales: Record<string, TranslationData> = {\n en,\n bn,\n ne,\n};\n\n/**\n * Returns the translation bundle for the given language code.\n * Unknown / undefined / empty values fall back to a stable empty object.\n *\n * Framework-agnostic: works in React, Next.js server components, Vue,\n * Svelte, or vanilla Node scripts.\n */\nexport function getTranslation<T extends object = TranslationData>(\n lang: string,\n): T {\n return (locales[lang] ?? EMPTY) as T;\n}\n","import { useMemo } from \"react\";\nimport { getTranslation } from \"./getTranslation\";\nimport type { TranslationData } from \"./types\";\n\n/**\n * React hook: returns the translation bundle for the given language.\n *\n * Memoized on `lang`, so the same language produces a stable object\n * reference across re-renders. Switching language swaps the reference\n * (and any consumers destructuring top-level keys will re-render).\n */\nexport function useTranslation<T extends object = TranslationData>(\n lang: string,\n): T {\n return useMemo(() => getTranslation<T>(lang), [lang]);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pantho075/locale",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Tiny framework-agnostic i18n core. Translation data lives in TS, no JSON files, no provider. Works in React, Next.js, Vue, Svelte, and Node.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|