@verbadev/js 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/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # @verbadev/js
2
+
3
+ Official JavaScript SDK for [Verba](https://verba.dev) - Translation Management System.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @verbadev/js
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Verba } from '@verbadev/js'
15
+
16
+ const verba = new Verba({
17
+ projectId: 'your-project-id',
18
+ publicKey: 'pk_your-public-key',
19
+ })
20
+
21
+ // Get a translation
22
+ const text = verba.t('welcome.message')
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ ```typescript
28
+ const verba = new Verba({
29
+ projectId: string, // Required - Your project ID from the dashboard
30
+ publicKey: string, // Required - Your public key (safe for client-side)
31
+ locale?: string, // Optional - defaults to auto-detection from browser
32
+ baseUrl?: string, // Optional - API base URL (default: 'https://verba.dev')
33
+ })
34
+ ```
35
+
36
+ ## Locale Auto-Detection
37
+
38
+ By default, the SDK automatically detects the user's locale from the browser (`navigator.language`) and matches it against your project's available locales.
39
+
40
+ ```typescript
41
+ // Auto-detect (default behavior)
42
+ const verba = new Verba({ projectId, publicKey })
43
+
44
+ // Explicit auto-detect
45
+ const verba = new Verba({ projectId, publicKey, locale: 'auto' })
46
+
47
+ // Override with specific locale
48
+ const verba = new Verba({ projectId, publicKey, locale: 'es' })
49
+ ```
50
+
51
+ **Matching logic:**
52
+ 1. Exact match (e.g., `en-US` → `en-US`)
53
+ 2. Base language match (e.g., `en-US` → `en`)
54
+ 3. Falls back to project's default locale if no match
55
+
56
+ ## API Reference
57
+
58
+ ### `verba.t(key, defaultValue?, params?)`
59
+
60
+ Get a translation for the current locale with optional interpolation.
61
+
62
+ ```typescript
63
+ // Basic usage
64
+ verba.t('welcome.message')
65
+
66
+ // With default value (auto-creates missing keys)
67
+ verba.t('greeting', 'Hello!')
68
+
69
+ // With interpolation params
70
+ verba.t('greeting', { name: 'Łukasz' })
71
+ // 'Hello, {name}!' → 'Hello, Łukasz!'
72
+
73
+ // With both default value and params
74
+ verba.t('greeting', 'Hello, {name}!', { name: 'Łukasz' })
75
+ ```
76
+
77
+ **Flexible signature:**
78
+ - `t(key)` - just the key
79
+ - `t(key, defaultValue)` - with fallback string
80
+ - `t(key, params)` - with interpolation params (object)
81
+ - `t(key, defaultValue, params)` - with both
82
+
83
+ **Parameters:**
84
+ - `key` (string) - The translation key
85
+ - `defaultValue` (string, optional) - Fallback value if key doesn't exist
86
+ - `params` (object, optional) - Values to interpolate into `{placeholder}` tokens
87
+
88
+ **Returns:** The translated string with interpolation applied.
89
+
90
+ **Auto-creation:** When you call `t()` with a `defaultValue` and the key doesn't exist, the SDK automatically:
91
+ 1. Returns the `defaultValue` immediately
92
+ 2. Creates the key in Verba in the background
93
+ 3. Triggers AI translation to all your project's locales
94
+
95
+ This enables a powerful workflow where you can write code first and translations are created on-the-fly.
96
+
97
+ ### `verba.setLocale(locale)`
98
+
99
+ Change the active locale.
100
+
101
+ ```typescript
102
+ verba.setLocale('es')
103
+ const text = verba.t('welcome.message') // Returns Spanish translation
104
+ ```
105
+
106
+ ### `verba.getLocale()`
107
+
108
+ Get the current locale.
109
+
110
+ ```typescript
111
+ const locale = verba.getLocale() // 'en'
112
+ ```
113
+
114
+ ### `verba.getLocales()`
115
+
116
+ Get all available locales for the project.
117
+
118
+ ```typescript
119
+ const locales = verba.getLocales() // ['en', 'es', 'fr']
120
+ ```
121
+
122
+ ### `verba.getDefaultLocale()`
123
+
124
+ Get the project's default locale.
125
+
126
+ ```typescript
127
+ const defaultLocale = verba.getDefaultLocale() // 'en'
128
+ ```
129
+
130
+ ### `verba.ready()`
131
+
132
+ Wait for translations to be loaded. Useful if you need to ensure translations are available before rendering.
133
+
134
+ ```typescript
135
+ await verba.ready()
136
+ // Translations are now loaded
137
+ ```
138
+
139
+ ## How It Works
140
+
141
+ 1. **Initialization**: When you create a `Verba` instance, it immediately fetches all translations from the API.
142
+
143
+ 2. **Caching**: Translations are cached in memory. The SDK uses ETags for efficient cache validation.
144
+
145
+ 3. **Synchronous Access**: After initialization, `t()` calls are synchronous and instant.
146
+
147
+ 4. **Auto-creation**: Missing keys with default values are created in the background without blocking your app.
148
+
149
+ ## Framework Examples
150
+
151
+ ### React
152
+
153
+ ```tsx
154
+ import { Verba } from '@verbadev/js'
155
+ import { createContext, useContext, useState, useEffect } from 'react'
156
+
157
+ const verba = new Verba({
158
+ projectId: 'your-project-id',
159
+ publicKey: 'pk_your-public-key',
160
+ })
161
+
162
+ const VerbaContext = createContext(verba)
163
+
164
+ export function VerbaProvider({ children }: { children: React.ReactNode }) {
165
+ const [, forceUpdate] = useState(0)
166
+
167
+ useEffect(() => {
168
+ verba.ready().then(() => forceUpdate(1))
169
+ }, [])
170
+
171
+ return (
172
+ <VerbaContext.Provider value={verba}>
173
+ {children}
174
+ </VerbaContext.Provider>
175
+ )
176
+ }
177
+
178
+ export function useTranslation() {
179
+ const verba = useContext(VerbaContext)
180
+ return {
181
+ t: verba.t.bind(verba),
182
+ setLocale: (locale: string) => verba.setLocale(locale),
183
+ locale: verba.getLocale(),
184
+ }
185
+ }
186
+
187
+ // Usage:
188
+ // const { t } = useTranslation()
189
+ // t('greeting', 'Hello, {name}!', { name: 'World' })
190
+ ```
191
+
192
+ ### Next.js (App Router)
193
+
194
+ ```tsx
195
+ // lib/verba.ts
196
+ import { Verba } from '@verbadev/js'
197
+
198
+ export const verba = new Verba({
199
+ projectId: 'your-project-id',
200
+ publicKey: 'pk_your-public-key',
201
+ })
202
+
203
+ // components/LocaleSwitcher.tsx
204
+ 'use client'
205
+ import { verba } from '@/lib/verba'
206
+
207
+ export function LocaleSwitcher() {
208
+ return (
209
+ <select onChange={(e) => verba.setLocale(e.target.value)}>
210
+ {verba.getLocales().map((locale) => (
211
+ <option key={locale} value={locale}>{locale}</option>
212
+ ))}
213
+ </select>
214
+ )
215
+ }
216
+ ```
217
+
218
+ ### Vue
219
+
220
+ ```vue
221
+ <script setup>
222
+ import { Verba } from '@verbadev/js'
223
+ import { ref, onMounted } from 'vue'
224
+
225
+ const verba = new Verba({
226
+ projectId: 'your-project-id',
227
+ publicKey: 'pk_your-public-key',
228
+ })
229
+
230
+ const ready = ref(false)
231
+
232
+ onMounted(async () => {
233
+ await verba.ready()
234
+ ready.value = true
235
+ })
236
+
237
+ const t = (key, defaultValue) => verba.t(key, defaultValue)
238
+ </script>
239
+
240
+ <template>
241
+ <div v-if="ready">
242
+ <h1>{{ t('welcome.title', 'Welcome!') }}</h1>
243
+ </div>
244
+ </template>
245
+ ```
246
+
247
+ ## TypeScript
248
+
249
+ The SDK is written in TypeScript and includes full type definitions.
250
+
251
+ ```typescript
252
+ import { Verba, VerbaConfig } from '@verbadev/js'
253
+
254
+ const config: VerbaConfig = {
255
+ projectId: 'your-project-id',
256
+ publicKey: 'pk_your-public-key',
257
+ locale: 'en',
258
+ }
259
+
260
+ const verba = new Verba(config)
261
+ ```
262
+
263
+ ## License
264
+
265
+ MIT
@@ -0,0 +1,84 @@
1
+ interface VerbaConfig {
2
+ projectId: string;
3
+ publicKey: string;
4
+ /**
5
+ * Locale to use.
6
+ * - If omitted or 'auto': auto-detects from browser (navigator.language)
7
+ * - If specified: uses the exact locale provided
8
+ */
9
+ locale?: string | 'auto';
10
+ baseUrl?: string;
11
+ }
12
+ interface TranslationsResponse {
13
+ version: number;
14
+ defaultLocale: string;
15
+ locales: string[];
16
+ translations: Record<string, Record<string, string>>;
17
+ }
18
+ interface CreateKeyResponse {
19
+ created: boolean;
20
+ key: string;
21
+ translations: Record<string, string>;
22
+ }
23
+ interface CachedTranslations {
24
+ version: number;
25
+ defaultLocale: string;
26
+ locales: string[];
27
+ data: Record<string, Record<string, string>>;
28
+ }
29
+
30
+ declare class Verba {
31
+ private projectId;
32
+ private publicKey;
33
+ private baseUrl;
34
+ private locale;
35
+ private autoDetect;
36
+ private cache;
37
+ private fetchPromise;
38
+ private pendingKeys;
39
+ constructor(config: VerbaConfig);
40
+ /**
41
+ * Set the current locale.
42
+ */
43
+ setLocale(locale: string): void;
44
+ /**
45
+ * Get the current locale.
46
+ */
47
+ getLocale(): string;
48
+ /**
49
+ * Get list of available locales.
50
+ */
51
+ getLocales(): string[];
52
+ /**
53
+ * Get the default locale.
54
+ */
55
+ getDefaultLocale(): string | null;
56
+ /**
57
+ * Get a translation for a key.
58
+ *
59
+ * @param key - The translation key
60
+ * @param defaultValueOrParams - Either a default value string, or params object for interpolation
61
+ * @param params - Params object for interpolation (when defaultValue is also provided)
62
+ *
63
+ * @example
64
+ * verba.t('greeting') // just key
65
+ * verba.t('greeting', 'Hello!') // with fallback
66
+ * verba.t('greeting', { name: 'World' }) // with params
67
+ * verba.t('greeting', 'Hello, {name}!', { name: 'World' }) // both
68
+ */
69
+ t(key: string, defaultValueOrParams?: string | Record<string, string | number>, params?: Record<string, string | number>): string;
70
+ /**
71
+ * Interpolate params into a string.
72
+ * Replaces {key} with the corresponding value from params.
73
+ */
74
+ private interpolate;
75
+ /**
76
+ * Wait for translations to be loaded.
77
+ * Call this if you need to ensure translations are ready.
78
+ */
79
+ ready(): Promise<void>;
80
+ private fetchTranslations;
81
+ private createKeyInBackground;
82
+ }
83
+
84
+ export { type CachedTranslations, type CreateKeyResponse, type TranslationsResponse, Verba, type VerbaConfig };
@@ -0,0 +1,84 @@
1
+ interface VerbaConfig {
2
+ projectId: string;
3
+ publicKey: string;
4
+ /**
5
+ * Locale to use.
6
+ * - If omitted or 'auto': auto-detects from browser (navigator.language)
7
+ * - If specified: uses the exact locale provided
8
+ */
9
+ locale?: string | 'auto';
10
+ baseUrl?: string;
11
+ }
12
+ interface TranslationsResponse {
13
+ version: number;
14
+ defaultLocale: string;
15
+ locales: string[];
16
+ translations: Record<string, Record<string, string>>;
17
+ }
18
+ interface CreateKeyResponse {
19
+ created: boolean;
20
+ key: string;
21
+ translations: Record<string, string>;
22
+ }
23
+ interface CachedTranslations {
24
+ version: number;
25
+ defaultLocale: string;
26
+ locales: string[];
27
+ data: Record<string, Record<string, string>>;
28
+ }
29
+
30
+ declare class Verba {
31
+ private projectId;
32
+ private publicKey;
33
+ private baseUrl;
34
+ private locale;
35
+ private autoDetect;
36
+ private cache;
37
+ private fetchPromise;
38
+ private pendingKeys;
39
+ constructor(config: VerbaConfig);
40
+ /**
41
+ * Set the current locale.
42
+ */
43
+ setLocale(locale: string): void;
44
+ /**
45
+ * Get the current locale.
46
+ */
47
+ getLocale(): string;
48
+ /**
49
+ * Get list of available locales.
50
+ */
51
+ getLocales(): string[];
52
+ /**
53
+ * Get the default locale.
54
+ */
55
+ getDefaultLocale(): string | null;
56
+ /**
57
+ * Get a translation for a key.
58
+ *
59
+ * @param key - The translation key
60
+ * @param defaultValueOrParams - Either a default value string, or params object for interpolation
61
+ * @param params - Params object for interpolation (when defaultValue is also provided)
62
+ *
63
+ * @example
64
+ * verba.t('greeting') // just key
65
+ * verba.t('greeting', 'Hello!') // with fallback
66
+ * verba.t('greeting', { name: 'World' }) // with params
67
+ * verba.t('greeting', 'Hello, {name}!', { name: 'World' }) // both
68
+ */
69
+ t(key: string, defaultValueOrParams?: string | Record<string, string | number>, params?: Record<string, string | number>): string;
70
+ /**
71
+ * Interpolate params into a string.
72
+ * Replaces {key} with the corresponding value from params.
73
+ */
74
+ private interpolate;
75
+ /**
76
+ * Wait for translations to be loaded.
77
+ * Call this if you need to ensure translations are ready.
78
+ */
79
+ ready(): Promise<void>;
80
+ private fetchTranslations;
81
+ private createKeyInBackground;
82
+ }
83
+
84
+ export { type CachedTranslations, type CreateKeyResponse, type TranslationsResponse, Verba, type VerbaConfig };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var l=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var g=(n,e)=>{for(var s in e)l(n,s,{get:e[s],enumerable:!0})},p=(n,e,s,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of u(e))!d.call(n,t)&&t!==s&&l(n,t,{get:()=>e[t],enumerable:!(r=h(e,t))||r.enumerable});return n};var f=n=>p(l({},"__esModule",{value:!0}),n);var m={};g(m,{Verba:()=>o});module.exports=f(m);var y="https://verba.dev";function b(){return typeof navigator>"u"?null:(navigator.languages??[navigator.language])[0]??null}function v(n,e){if(e.includes(n))return n;let s=n.split("-")[0];if(e.includes(s))return s;let r=e.find(t=>t.startsWith(s+"-"));return r||null}var o=class{constructor(e){this.cache=null;this.fetchPromise=null;this.pendingKeys=new Set;this.projectId=e.projectId,this.publicKey=e.publicKey,this.baseUrl=e.baseUrl??y,this.autoDetect=e.locale===void 0||e.locale==="auto",this.autoDetect?this.locale=b()??"en":this.locale=e.locale,this.fetchPromise=this.fetchTranslations()}setLocale(e){this.locale=e}getLocale(){return this.locale}getLocales(){return this.cache?.locales??[]}getDefaultLocale(){return this.cache?.defaultLocale??null}t(e,s,r){let t,i;typeof s=="string"?(t=s,i=r):typeof s=="object"&&(i=s);let a;if(!this.cache)t!==void 0&&this.createKeyInBackground(e,t),a=t??e;else{let c=this.cache.data[e];c?a=c[this.locale]??c[this.cache.defaultLocale]??t??e:(t!==void 0&&this.createKeyInBackground(e,t),a=t??e)}return i&&(a=this.interpolate(a,i)),a}interpolate(e,s){return e.replace(/\{(\w+)\}/g,(r,t)=>s[t]!==void 0?String(s[t]):r)}async ready(){this.fetchPromise&&await this.fetchPromise}async fetchTranslations(){let e=`${this.baseUrl}/api/sdk/${this.projectId}/translations`,s={"X-Public-Key":this.publicKey};this.cache&&(s["If-None-Match"]=String(this.cache.version));try{let r=await fetch(e,{headers:s});if(r.status===304&&this.cache)return this.cache;if(!r.ok)throw new Error(`Failed to fetch translations: ${r.status}`);let t=await r.json();if(this.cache={version:t.version,defaultLocale:t.defaultLocale,locales:t.locales,data:t.translations},this.autoDetect){let i=v(this.locale,t.locales);this.locale=i??t.defaultLocale}return this.cache}catch(r){throw console.error("[Verba] Failed to fetch translations:",r),r}}async createKeyInBackground(e,s){if(!this.pendingKeys.has(e)){this.pendingKeys.add(e);try{let r=`${this.baseUrl}/api/sdk/${this.projectId}/keys`,t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json","X-Public-Key":this.publicKey},body:JSON.stringify({key:e,defaultValue:s})});if(!t.ok){let a=await t.json().catch(()=>({}));console.error("[Verba] Failed to create key:",a.error??t.status);return}let i=await t.json();this.cache&&i.translations&&(this.cache.data[e]=i.translations)}catch(r){console.error("[Verba] Failed to create key:",r)}finally{this.pendingKeys.delete(e)}}}};0&&(module.exports={Verba});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var l="https://verba.dev";function h(){return typeof navigator>"u"?null:(navigator.languages??[navigator.language])[0]??null}function u(a,e){if(e.includes(a))return a;let r=a.split("-")[0];if(e.includes(r))return r;let s=e.find(t=>t.startsWith(r+"-"));return s||null}var c=class{constructor(e){this.cache=null;this.fetchPromise=null;this.pendingKeys=new Set;this.projectId=e.projectId,this.publicKey=e.publicKey,this.baseUrl=e.baseUrl??l,this.autoDetect=e.locale===void 0||e.locale==="auto",this.autoDetect?this.locale=h()??"en":this.locale=e.locale,this.fetchPromise=this.fetchTranslations()}setLocale(e){this.locale=e}getLocale(){return this.locale}getLocales(){return this.cache?.locales??[]}getDefaultLocale(){return this.cache?.defaultLocale??null}t(e,r,s){let t,n;typeof r=="string"?(t=r,n=s):typeof r=="object"&&(n=r);let i;if(!this.cache)t!==void 0&&this.createKeyInBackground(e,t),i=t??e;else{let o=this.cache.data[e];o?i=o[this.locale]??o[this.cache.defaultLocale]??t??e:(t!==void 0&&this.createKeyInBackground(e,t),i=t??e)}return n&&(i=this.interpolate(i,n)),i}interpolate(e,r){return e.replace(/\{(\w+)\}/g,(s,t)=>r[t]!==void 0?String(r[t]):s)}async ready(){this.fetchPromise&&await this.fetchPromise}async fetchTranslations(){let e=`${this.baseUrl}/api/sdk/${this.projectId}/translations`,r={"X-Public-Key":this.publicKey};this.cache&&(r["If-None-Match"]=String(this.cache.version));try{let s=await fetch(e,{headers:r});if(s.status===304&&this.cache)return this.cache;if(!s.ok)throw new Error(`Failed to fetch translations: ${s.status}`);let t=await s.json();if(this.cache={version:t.version,defaultLocale:t.defaultLocale,locales:t.locales,data:t.translations},this.autoDetect){let n=u(this.locale,t.locales);this.locale=n??t.defaultLocale}return this.cache}catch(s){throw console.error("[Verba] Failed to fetch translations:",s),s}}async createKeyInBackground(e,r){if(!this.pendingKeys.has(e)){this.pendingKeys.add(e);try{let s=`${this.baseUrl}/api/sdk/${this.projectId}/keys`,t=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json","X-Public-Key":this.publicKey},body:JSON.stringify({key:e,defaultValue:r})});if(!t.ok){let i=await t.json().catch(()=>({}));console.error("[Verba] Failed to create key:",i.error??t.status);return}let n=await t.json();this.cache&&n.translations&&(this.cache.data[e]=n.translations)}catch(s){console.error("[Verba] Failed to create key:",s)}finally{this.pendingKeys.delete(e)}}}};export{c as Verba};
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@verbadev/js",
3
+ "version": "0.1.0",
4
+ "description": "JavaScript SDK for Verba Translation Management System",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "verba",
26
+ "translations",
27
+ "i18n",
28
+ "localization",
29
+ "l10n"
30
+ ],
31
+ "author": "",
32
+ "license": "MIT",
33
+ "devDependencies": {
34
+ "tsup": "^8.0.0",
35
+ "tsx": "^4.21.0",
36
+ "typescript": "^5.0.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": ""
41
+ }
42
+ }