kotori 6.1.2 → 6.1.4

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
@@ -8,10 +8,13 @@
8
8
  <img src="https://img.shields.io/badge/bundle%20size-0.28kB-emerald" alt="Bundle Size">
9
9
  <a href="https://github.com/tylim88/Kotori/blob/main/LICENSE"><img src="https://img.shields.io/github/license/tylim88/Kotori?color=blue" alt="License"></a>
10
10
  <img src="https://img.shields.io/badge/dependencies-0-success" alt="Dependencies">
11
+ <a href="https://snyk.io/test/github/tylim88/Kotori">
12
+ <img src="https://snyk.io/test/github/tylim88/Kotori/badge.svg" alt="Snyk Vulnerabilities for npm package">
13
+ </a>
11
14
  </p>
12
15
 
13
16
  <p align="center">
14
- 🕊️ Kotori is a zero-config, fully type-safe, and modular internationalization library for React that compiles down to just 0.29kB. No JSON, no external CLI tools, no codegen—just live type inference from your strings.
17
+ 🕊️ Kotori is a zero-config, fully type-safe, and modular internationalization library for React that compiles down to just 0.28kB. No JSON, no external CLI tools, no codegen—just live type inference from your strings.
15
18
  </p>
16
19
 
17
20
  ```ts
@@ -55,13 +58,13 @@ const Component = () => {
55
58
  - No JSON
56
59
  - No dependencies
57
60
  - No build step
58
- - 0.29kB minified and gzipped
61
+ - 0.28kB minified and gzipped
59
62
  - Modular and tree-shakeable
60
63
  - Language change in one page rerenders all pages
61
64
  - Variables typed and inferred from string literals — no more string typos
62
65
  - Maximum type safety with minimum types
63
66
 
64
- Demo: <https://stackblitz.com/edit/kotori?file=src%2FApp.tsx>
67
+ Demo: <https://github.com/tylim88/kotori-demo>
65
68
 
66
69
  ## Installation
67
70
 
@@ -81,7 +84,7 @@ export const { useT, d, setLanguage } = kotori({
81
84
  secondaries: ['zh', 'ja', 'ms'],
82
85
  })
83
86
 
84
- // you can define your dicts in the same file or separate them by component, it's up to you
87
+ // you can define your dicts in the same file/folder or separate them by component, it's up to you
85
88
  export const intro = d({
86
89
  en: 'my name is {{name}}, I am {{age}} years old.',
87
90
  zh: '我叫{{name}},我今年{{age}}岁了。',
@@ -163,7 +166,7 @@ export const Page2 = () => {
163
166
 
164
167
  ![how kotori works](image.webp)
165
168
 
166
- ### `kotori(options)` (0.29kB)
169
+ ### `kotori(options)` (0.28kB)
167
170
 
168
171
  Creates a scoped i18n instance.
169
172
 
@@ -248,9 +251,16 @@ Detects the user's preferred language from browser settings and sets it on the k
248
251
 
249
252
  ```ts
250
253
  import { detectLanguage } from 'kotori'
251
- import { i18n } from './locales'
254
+ import { kotori } from 'kotori'
255
+
256
+ const i18n = kotori({
257
+ primary: 'en',
258
+ secondaries: ['zh', 'ja', 'ms'],
259
+ })
252
260
 
253
261
  detectLanguage(i18n)
262
+
263
+ export const { useT, d, setLanguage } = i18n
254
264
  ```
255
265
 
256
266
  | option | type | default | description |
@@ -289,7 +299,8 @@ kotori({ primary: 'klingon', secondaries: ['zh'] }) // ❌ compile error
289
299
  ## Tips
290
300
 
291
301
  - If you plan to add new languages frequently, consider colocating all your dicts in a single file or multiple files in one folder. It is easier to copy the entire files and hand it to an AI to translate.
292
- - If your supported languages are fixed, consider splitting dicts by page or component. Translations stay close to the code that uses them and are easier to maintain.
302
+ - If your supported languages are fixed, consider splitting dicts by page or component. This keeps translations close to the code that uses them and makes them easier to maintain.
303
+ - For large or rarely used components, you can also reduce your bundle size by dynamically importing them only when they are needed on the page.
293
304
 
294
305
  ## Roadmap
295
306
 
package/dist/index.cjs CHANGED
@@ -35,6 +35,8 @@ let react = require("react");
35
35
  *
36
36
  * detectLanguage(i18n, { fallbackToSubtag: false })
37
37
  * // browser: ['zh-CN'] → no exact match → no-op, stays 'en'
38
+ *
39
+ * export const { useT, d, setLanguage } = i18n
38
40
  * ```
39
41
  *
40
42
  * @example
@@ -110,7 +112,7 @@ const kotori = (config) => {
110
112
  * )
111
113
  * }
112
114
  */
113
- const t = (dictionary, ...args) => (dictionary().d[snapshot.language] || "").replace(/\{\{\s*([\w-]+)\s*\}\}/g, (_, key) => String(args[0]?.[key]));
115
+ const t = (dictionary, ...args) => (dictionary().d[snapshot.language] + "").replace(/\{\{\s*([\w-]+)\s*\}\}/g, (_, key) => args[0]?.[key] + "");
114
116
  let snapshot = {
115
117
  language: config.primary,
116
118
  t
@@ -145,15 +147,11 @@ const kotori = (config) => {
145
147
  language,
146
148
  t: (...args) => t(...args)
147
149
  };
148
- listeners.forEach((listener) => {
149
- listener();
150
- });
150
+ listeners.forEach((listener) => listener());
151
151
  };
152
152
  const subscribe = (listener) => {
153
153
  listeners.add(listener);
154
- return () => {
155
- listeners.delete(listener);
156
- };
154
+ return () => listeners.delete(listener);
157
155
  };
158
156
  return {
159
157
  config,
package/dist/index.d.cts CHANGED
@@ -208,6 +208,8 @@ declare const kotori: <const Primary extends BCP47LanguageTagNameWithSubTag, con
208
208
  *
209
209
  * detectLanguage(i18n, { fallbackToSubtag: false })
210
210
  * // browser: ['zh-CN'] → no exact match → no-op, stays 'en'
211
+ *
212
+ * export const { useT, d, setLanguage } = i18n
211
213
  * ```
212
214
  *
213
215
  * @example
package/dist/index.d.mts CHANGED
@@ -208,6 +208,8 @@ declare const kotori: <const Primary extends BCP47LanguageTagNameWithSubTag, con
208
208
  *
209
209
  * detectLanguage(i18n, { fallbackToSubtag: false })
210
210
  * // browser: ['zh-CN'] → no exact match → no-op, stays 'en'
211
+ *
212
+ * export const { useT, d, setLanguage } = i18n
211
213
  * ```
212
214
  *
213
215
  * @example
package/dist/index.mjs CHANGED
@@ -34,6 +34,8 @@ import { useSyncExternalStore } from "react";
34
34
  *
35
35
  * detectLanguage(i18n, { fallbackToSubtag: false })
36
36
  * // browser: ['zh-CN'] → no exact match → no-op, stays 'en'
37
+ *
38
+ * export const { useT, d, setLanguage } = i18n
37
39
  * ```
38
40
  *
39
41
  * @example
@@ -109,7 +111,7 @@ const kotori = (config) => {
109
111
  * )
110
112
  * }
111
113
  */
112
- const t = (dictionary, ...args) => (dictionary().d[snapshot.language] || "").replace(/\{\{\s*([\w-]+)\s*\}\}/g, (_, key) => String(args[0]?.[key]));
114
+ const t = (dictionary, ...args) => (dictionary().d[snapshot.language] + "").replace(/\{\{\s*([\w-]+)\s*\}\}/g, (_, key) => args[0]?.[key] + "");
113
115
  let snapshot = {
114
116
  language: config.primary,
115
117
  t
@@ -144,15 +146,11 @@ const kotori = (config) => {
144
146
  language,
145
147
  t: (...args) => t(...args)
146
148
  };
147
- listeners.forEach((listener) => {
148
- listener();
149
- });
149
+ listeners.forEach((listener) => listener());
150
150
  };
151
151
  const subscribe = (listener) => {
152
152
  listeners.add(listener);
153
- return () => {
154
- listeners.delete(listener);
155
- };
153
+ return () => listeners.delete(listener);
156
154
  };
157
155
  return {
158
156
  config,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kotori",
3
3
  "description": "0.29kB Strongly-typed and tree-shakeable internationalization library for React",
4
- "version": "6.1.2",
4
+ "version": "6.1.4",
5
5
  "scripts": {
6
6
  "setup": "rm -rf node_modules && npm i && git init && husky",
7
7
  "prepublishOnly": "npm i && npx tsc && npm run build",