@pantho075/locale 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pantho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @pantho075/locale
2
+
3
+ Tiny framework-agnostic i18n core. Translation data lives in TypeScript, not JSON. No provider, no context, no runtime detection. Works in React, Next.js, Vue, Svelte, and Node.
4
+
5
+ ```tsx
6
+ import { useTranslation } from "@pantho075/locale";
7
+
8
+ export function Example() {
9
+ const { title, home } = useTranslation("en");
10
+ return (
11
+ <div>
12
+ <div>{title}</div>
13
+ <div>{home.header}</div>
14
+ <div>{home.footer}</div>
15
+ </div>
16
+ );
17
+ }
18
+ ```
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pnpm add @pantho075/locale
24
+ ```
25
+
26
+ React is an **optional** peer dependency — install it in your app if you're using `useTranslation`. The core `getTranslation` function works in any JS runtime.
27
+
28
+ ## Quick start
29
+
30
+ ### React / Next.js (client component)
31
+
32
+ ```tsx
33
+ import { useTranslation } from "@pantho075/locale";
34
+
35
+ export function Greeting({ lang }: { lang: string }) {
36
+ const { title, home } = useTranslation(lang);
37
+ return (
38
+ <div>
39
+ <h1>{title}</h1>
40
+ <p>{home.header}</p>
41
+ <p>{home.footer}</p>
42
+ </div>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ### Next.js (server component) / Vue / Svelte / Node
48
+
49
+ ```ts
50
+ import { getTranslation } from "@pantho075/locale";
51
+
52
+ const { title, home } = getTranslation("en");
53
+ console.log(title, home.header, home.footer);
54
+ ```
55
+
56
+ ### Adding your own keys
57
+
58
+ Edit `src/locales/en.ts` (and `bn.ts`, `ne.ts`) in this package, or fork it. Each file is just a `const data = { ... }` wrapped in `withNullTopLevel`:
59
+
60
+ ```ts
61
+ // src/locales/en.ts
62
+ import { withNullTopLevel } from "../normalize";
63
+
64
+ const data = {
65
+ title: "Welcome",
66
+ home: {
67
+ header: "Hello",
68
+ footer: "Goodbye",
69
+ },
70
+ greeting: "Hi there",
71
+ };
72
+
73
+ export default withNullTopLevel(data);
74
+ ```
75
+
76
+ ### Adding a new locale
77
+
78
+ 1. Create `src/locales/<code>.ts` that exports `withNullTopLevel(...)`.
79
+ 2. Import it in `src/getTranslation.ts` and add it to the `locales` map.
80
+
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
+ ## API
112
+
113
+ ### `getTranslation<T>(lang: string): T`
114
+
115
+ Framework-agnostic. Returns the bundle for the requested language, or `{}` for unknown languages. The same empty object reference is returned every time, so you can safely use `===` comparisons.
116
+
117
+ The optional generic lets consumers constrain the return type:
118
+
119
+ ```ts
120
+ interface English {
121
+ title: string;
122
+ home: { header: string; footer: string };
123
+ }
124
+
125
+ const en = getTranslation<English>("en");
126
+ ```
127
+
128
+ ### `useTranslation<T>(lang: string): T`
129
+
130
+ React hook wrapping `getTranslation`. Memoized on `lang` — same language returns the same object reference across re-renders.
131
+
132
+ ```ts
133
+ import { useTranslation } from "@pantho075/locale";
134
+
135
+ const { title } = useTranslation("en"); // string | null
136
+ const en = useTranslation<English>("en"); // typed
137
+ ```
138
+
139
+ ### `TranslationData`
140
+
141
+ The default return type — `Record<string, unknown>`. Most consumers will constrain this with their own interface via the generic.
142
+
143
+ ## Why no JSON files?
144
+
145
+ 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
+
147
+ If you want to override locales from your own app, you can fork the package or import the `normalize` helper directly and build your own locale map.
148
+
149
+ ## License
150
+
151
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
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
+ // src/locales/bn.ts
18
+ var data = {
19
+ title: "\u09B6\u09BF\u09B0\u09CB\u09A8\u09BE\u09AE",
20
+ home: {
21
+ header: "\u09B9\u09C7\u09A1\u09BE\u09B0",
22
+ footer: "\u09AB\u09C1\u099F\u09BE\u09B0"
23
+ }
24
+ };
25
+ var bn_default = withNullTopLevel(data);
26
+
27
+ // src/locales/en.ts
28
+ var data2 = {
29
+ title: "the title",
30
+ home: {
31
+ header: "this is header",
32
+ footer: "this is footer"
33
+ }
34
+ };
35
+ var en_default = withNullTopLevel(data2);
36
+
37
+ // src/locales/ne.ts
38
+ var data3 = {
39
+ title: "the title",
40
+ home: {
41
+ header: "this is header",
42
+ footer: "this is footer"
43
+ }
44
+ };
45
+ var ne_default = withNullTopLevel(data3);
46
+
47
+ // src/getTranslation.ts
48
+ var EMPTY = Object.freeze({});
49
+ var locales = {
50
+ en: en_default,
51
+ bn: bn_default,
52
+ ne: ne_default
53
+ };
54
+ function getTranslation(lang) {
55
+ return locales[lang] ?? EMPTY;
56
+ }
57
+ function useTranslation(lang) {
58
+ return react.useMemo(() => getTranslation(lang), [lang]);
59
+ }
60
+
61
+ exports.getTranslation = getTranslation;
62
+ exports.useTranslation = useTranslation;
63
+ //# sourceMappingURL=index.cjs.map
64
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/normalize.ts","../src/locales/bn.ts","../src/locales/en.ts","../src/locales/ne.ts","../src/getTranslation.ts","../src/useTranslation.ts"],"names":["data","useMemo"],"mappings":";;;;;AAUO,SAAS,iBAA4CA,KAAAA,EAAY;AACtE,EAAA,OAAO,IAAI,MAAMA,KAAAA,EAAM;AAAA,IACrB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAC1B,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,EAAE,QAAQ,MAAA,CAAA,EAAS;AACjD,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAAA,IAC3C;AAAA,GACD,CAAA;AACH;;;ACjBA,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,iBAAiB,IAAI,CAAA;;;ACRpC,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,GAAQ,iBAAiBA,KAAI,CAAA;;;ACRpC,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,GAAQ,iBAAiBA,KAAI,CAAA;;;ACJpC,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":["import type { TranslationData } from \"./types\";\n\n/**\n * Wrap a translation bundle in a Proxy that returns `null` for any\n * missing top-level key, while leaving nested objects untouched so\n * missing nested keys still resolve to `undefined` (standard JS\n * semantics).\n *\n * Explicit `null` values in the source are preserved.\n */\nexport function withNullTopLevel<T extends TranslationData>(data: T): T {\n return new Proxy(data, {\n get(target, prop, receiver) {\n if (typeof prop === \"string\" && !(prop in target)) {\n return null;\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as T;\n}\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"শিরোনাম\",\n home: {\n header: \"হেডার\",\n footer: \"ফুটার\",\n },\n};\n\nexport default withNullTopLevel(data);\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default withNullTopLevel(data);\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default withNullTopLevel(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 `normalizeTopLevel(data)`\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"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A loose shape for translation bundles. Consumers typically constrain
3
+ * this with their own interface via the hook's generic parameter.
4
+ */
5
+ type TranslationData = Record<string, unknown>;
6
+
7
+ /**
8
+ * Returns the translation bundle for the given language code.
9
+ * Unknown / undefined / empty values fall back to a stable empty object.
10
+ *
11
+ * Framework-agnostic: works in React, Next.js server components, Vue,
12
+ * Svelte, or vanilla Node scripts.
13
+ */
14
+ declare function getTranslation<T extends object = TranslationData>(lang: string): T;
15
+
16
+ /**
17
+ * React hook: returns the translation bundle for the given language.
18
+ *
19
+ * Memoized on `lang`, so the same language produces a stable object
20
+ * reference across re-renders. Switching language swaps the reference
21
+ * (and any consumers destructuring top-level keys will re-render).
22
+ */
23
+ declare function useTranslation<T extends object = TranslationData>(lang: string): T;
24
+
25
+ export { type TranslationData, getTranslation, useTranslation };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A loose shape for translation bundles. Consumers typically constrain
3
+ * this with their own interface via the hook's generic parameter.
4
+ */
5
+ type TranslationData = Record<string, unknown>;
6
+
7
+ /**
8
+ * Returns the translation bundle for the given language code.
9
+ * Unknown / undefined / empty values fall back to a stable empty object.
10
+ *
11
+ * Framework-agnostic: works in React, Next.js server components, Vue,
12
+ * Svelte, or vanilla Node scripts.
13
+ */
14
+ declare function getTranslation<T extends object = TranslationData>(lang: string): T;
15
+
16
+ /**
17
+ * React hook: returns the translation bundle for the given language.
18
+ *
19
+ * Memoized on `lang`, so the same language produces a stable object
20
+ * reference across re-renders. Switching language swaps the reference
21
+ * (and any consumers destructuring top-level keys will re-render).
22
+ */
23
+ declare function useTranslation<T extends object = TranslationData>(lang: string): T;
24
+
25
+ export { type TranslationData, getTranslation, useTranslation };
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { useMemo } from 'react';
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
+ // src/locales/bn.ts
16
+ var data = {
17
+ title: "\u09B6\u09BF\u09B0\u09CB\u09A8\u09BE\u09AE",
18
+ home: {
19
+ header: "\u09B9\u09C7\u09A1\u09BE\u09B0",
20
+ footer: "\u09AB\u09C1\u099F\u09BE\u09B0"
21
+ }
22
+ };
23
+ var bn_default = withNullTopLevel(data);
24
+
25
+ // src/locales/en.ts
26
+ var data2 = {
27
+ title: "the title",
28
+ home: {
29
+ header: "this is header",
30
+ footer: "this is footer"
31
+ }
32
+ };
33
+ var en_default = withNullTopLevel(data2);
34
+
35
+ // src/locales/ne.ts
36
+ var data3 = {
37
+ title: "the title",
38
+ home: {
39
+ header: "this is header",
40
+ footer: "this is footer"
41
+ }
42
+ };
43
+ var ne_default = withNullTopLevel(data3);
44
+
45
+ // src/getTranslation.ts
46
+ var EMPTY = Object.freeze({});
47
+ var locales = {
48
+ en: en_default,
49
+ bn: bn_default,
50
+ ne: ne_default
51
+ };
52
+ function getTranslation(lang) {
53
+ return locales[lang] ?? EMPTY;
54
+ }
55
+ function useTranslation(lang) {
56
+ return useMemo(() => getTranslation(lang), [lang]);
57
+ }
58
+
59
+ export { getTranslation, useTranslation };
60
+ //# sourceMappingURL=index.js.map
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/normalize.ts","../src/locales/bn.ts","../src/locales/en.ts","../src/locales/ne.ts","../src/getTranslation.ts","../src/useTranslation.ts"],"names":["data"],"mappings":";;;AAUO,SAAS,iBAA4CA,KAAAA,EAAY;AACtE,EAAA,OAAO,IAAI,MAAMA,KAAAA,EAAM;AAAA,IACrB,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAC1B,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,EAAE,QAAQ,MAAA,CAAA,EAAS;AACjD,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAAA,IAC3C;AAAA,GACD,CAAA;AACH;;;ACjBA,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,iBAAiB,IAAI,CAAA;;;ACRpC,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,GAAQ,iBAAiBA,KAAI,CAAA;;;ACRpC,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,GAAQ,iBAAiBA,KAAI,CAAA;;;ACJpC,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":["import type { TranslationData } from \"./types\";\n\n/**\n * Wrap a translation bundle in a Proxy that returns `null` for any\n * missing top-level key, while leaving nested objects untouched so\n * missing nested keys still resolve to `undefined` (standard JS\n * semantics).\n *\n * Explicit `null` values in the source are preserved.\n */\nexport function withNullTopLevel<T extends TranslationData>(data: T): T {\n return new Proxy(data, {\n get(target, prop, receiver) {\n if (typeof prop === \"string\" && !(prop in target)) {\n return null;\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as T;\n}\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"শিরোনাম\",\n home: {\n header: \"হেডার\",\n footer: \"ফুটার\",\n },\n};\n\nexport default withNullTopLevel(data);\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default withNullTopLevel(data);\n","import { withNullTopLevel } from \"../normalize\";\n\nconst data = {\n title: \"the title\",\n home: {\n header: \"this is header\",\n footer: \"this is footer\",\n },\n};\n\nexport default withNullTopLevel(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 `normalizeTopLevel(data)`\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 ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@pantho075/locale",
3
+ "version": "0.1.0",
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
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "typecheck": "tsc --noEmit",
26
+ "lint": "eslint src",
27
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
28
+ },
29
+ "peerDependencies": {
30
+ "react": ">=18.0.0 <20.0.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "react": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@testing-library/react": "^16.1.0",
39
+ "@types/react": "^18.3.12",
40
+ "@types/react-dom": "^18.3.1",
41
+ "@vitejs/plugin-react": "^4.3.4",
42
+ "jsdom": "^25.0.1",
43
+ "react": "^18.3.1",
44
+ "react-dom": "^18.3.1",
45
+ "tsup": "^8.3.5",
46
+ "typescript": "~5.6.3",
47
+ "vite": "^6.0.5",
48
+ "vitest": "^2.1.8"
49
+ },
50
+ "keywords": [
51
+ "i18n",
52
+ "localization",
53
+ "translation",
54
+ "react",
55
+ "nextjs",
56
+ "vue",
57
+ "svelte",
58
+ "pantho"
59
+ ],
60
+ "engines": {
61
+ "node": ">=18"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "license": "MIT"
67
+ }