@geenius/i18n 0.1.0 → 0.3.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 +134 -1
- package/package.json +16 -3
- package/packages/convex/dist/index.d.ts +62 -0
- package/packages/convex/dist/index.js +157 -0
- package/packages/convex/dist/index.js.map +1 -0
- package/packages/react/README.md +1 -1
- package/packages/react/dist/index.d.ts +100 -0
- package/packages/react/dist/index.js +284 -0
- package/packages/react/dist/index.js.map +1 -0
- package/packages/react-css/README.md +1 -1
- package/packages/react-css/dist/index.d.ts +34 -0
- package/packages/react-css/dist/index.js +134 -0
- package/packages/react-css/dist/index.js.map +1 -0
- package/packages/shared/README.md +1 -1
- package/packages/shared/dist/index.d.ts +77 -0
- package/packages/shared/dist/index.js +158 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/solidjs/README.md +1 -1
- package/packages/solidjs/dist/index.d.ts +105 -0
- package/packages/solidjs/dist/index.js +328 -0
- package/packages/solidjs/dist/index.js.map +1 -0
- package/packages/solidjs-css/README.md +1 -1
- package/packages/solidjs-css/dist/index.d.ts +59 -0
- package/packages/solidjs-css/dist/index.js +244 -0
- package/packages/solidjs-css/dist/index.js.map +1 -0
- package/.changeset/config.json +0 -11
- package/.github/CODEOWNERS +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/ci.yml +0 -23
- package/.github/workflows/release.yml +0 -29
- package/.nvmrc +0 -1
- package/.project/ACCOUNT.yaml +0 -4
- package/.project/IDEAS.yaml +0 -7
- package/.project/PROJECT.yaml +0 -11
- package/.project/ROADMAP.yaml +0 -15
- package/CODE_OF_CONDUCT.md +0 -16
- package/CONTRIBUTING.md +0 -26
- package/SECURITY.md +0 -15
- package/SUPPORT.md +0 -8
- package/packages/convex/package.json +0 -42
- package/packages/convex/src/index.ts +0 -3
- package/packages/convex/src/mutations.ts +0 -65
- package/packages/convex/src/queries.ts +0 -54
- package/packages/convex/src/schema.ts +0 -26
- package/packages/convex/tsconfig.json +0 -18
- package/packages/convex/tsup.config.ts +0 -17
- package/packages/react/package.json +0 -51
- package/packages/react/src/components/index.tsx +0 -87
- package/packages/react/src/hooks/index.ts +0 -4
- package/packages/react/src/hooks/useI18n.tsx +0 -50
- package/packages/react/src/hooks/useI18nAdmin.ts +0 -12
- package/packages/react/src/hooks/useLocaleDetect.ts +0 -10
- package/packages/react/src/hooks/useTranslations.ts +0 -11
- package/packages/react/src/index.tsx +0 -8
- package/packages/react/src/pages/I18nAdminPage.tsx +0 -42
- package/packages/react/src/pages/LocalePreviewPage.tsx +0 -54
- package/packages/react/src/pages/index.ts +0 -2
- package/packages/react/tsconfig.json +0 -19
- package/packages/react/tsup.config.ts +0 -12
- package/packages/react-css/package.json +0 -36
- package/packages/react-css/src/components/index.tsx +0 -66
- package/packages/react-css/src/hooks/index.ts +0 -4
- package/packages/react-css/src/index.tsx +0 -4
- package/packages/react-css/src/pages/LocaleSettingsPage.tsx +0 -74
- package/packages/react-css/src/pages/TranslationsPage.tsx +0 -98
- package/packages/react-css/src/styles.css +0 -210
- package/packages/react-css/tsconfig.json +0 -19
- package/packages/react-css/tsup.config.ts +0 -10
- package/packages/shared/package.json +0 -44
- package/packages/shared/src/__tests__/i18n.test.ts +0 -78
- package/packages/shared/src/config.ts +0 -344
- package/packages/shared/src/index.ts +0 -106
- package/packages/shared/src/types.ts +0 -51
- package/packages/shared/tsconfig.json +0 -18
- package/packages/shared/tsup.config.ts +0 -11
- package/packages/shared/vitest.config.ts +0 -4
- package/packages/solidjs/package.json +0 -47
- package/packages/solidjs/src/components/LocaleCard.tsx +0 -44
- package/packages/solidjs/src/components/LocaleStatsCard.tsx +0 -35
- package/packages/solidjs/src/components/LocaleSwitcher.tsx +0 -65
- package/packages/solidjs/src/components/MissingKeyAlert.tsx +0 -21
- package/packages/solidjs/src/components/RTLWrapper.tsx +0 -13
- package/packages/solidjs/src/components/TranslationKeyRow.tsx +0 -41
- package/packages/solidjs/src/components/index.ts +0 -6
- package/packages/solidjs/src/index.tsx +0 -8
- package/packages/solidjs/src/pages/I18nAdminPage.tsx +0 -188
- package/packages/solidjs/src/pages/LocalePreviewPage.tsx +0 -99
- package/packages/solidjs/src/pages/index.ts +0 -2
- package/packages/solidjs/src/primitives/I18nProvider.tsx +0 -56
- package/packages/solidjs/src/primitives/createI18nAdmin.ts +0 -7
- package/packages/solidjs/src/primitives/createLocaleDetect.ts +0 -8
- package/packages/solidjs/src/primitives/createTranslations.ts +0 -22
- package/packages/solidjs/src/primitives/index.ts +0 -4
- package/packages/solidjs/tsconfig.json +0 -20
- package/packages/solidjs/tsup.config.ts +0 -12
- package/packages/solidjs-css/package.json +0 -33
- package/packages/solidjs-css/src/components/LocaleCard.tsx +0 -45
- package/packages/solidjs-css/src/components/LocaleStatsCard.tsx +0 -43
- package/packages/solidjs-css/src/components/LocaleSwitcher.tsx +0 -51
- package/packages/solidjs-css/src/components/MissingKeyAlert.tsx +0 -24
- package/packages/solidjs-css/src/components/RTLWrapper.tsx +0 -16
- package/packages/solidjs-css/src/components/TranslationKeyRow.tsx +0 -47
- package/packages/solidjs-css/src/components/index.ts +0 -6
- package/packages/solidjs-css/src/i18n.css +0 -1322
- package/packages/solidjs-css/src/index.tsx +0 -3
- package/packages/solidjs-css/src/pages/I18nAdminPage.tsx +0 -134
- package/packages/solidjs-css/src/pages/LocalePreviewPage.tsx +0 -116
- package/packages/solidjs-css/src/pages/index.ts +0 -2
- package/packages/solidjs-css/src/primitives/index.ts +0 -1
- package/packages/solidjs-css/tsconfig.json +0 -20
- package/packages/solidjs-css/tsup.config.bundled_dcjc4sct21j.mjs +0 -18
- package/packages/solidjs-css/tsup.config.ts +0 -14
- package/pnpm-workspace.yaml +0 -2
- package/tsconfig.json +0 -23
package/README.md
CHANGED
|
@@ -1 +1,134 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @geenius/i18n
|
|
2
|
+
|
|
3
|
+
Internationalization for Convex apps with React and SolidJS support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 14 built-in locales with metadata (name, native name, direction, flag)
|
|
8
|
+
- RTL detection for Arabic and Hebrew
|
|
9
|
+
- Pluralization with `_zero`, `_one`, `_other` suffixes
|
|
10
|
+
- `{{variable}}` interpolation
|
|
11
|
+
- Intl-based date, number, and currency formatting
|
|
12
|
+
- localStorage locale persistence and browser detection
|
|
13
|
+
- Convex backend schema for translation management
|
|
14
|
+
- Admin dashboard components (Tailwind and vanilla CSS variants)
|
|
15
|
+
|
|
16
|
+
## Packages
|
|
17
|
+
|
|
18
|
+
| Package | Description |
|
|
19
|
+
|---------|-------------|
|
|
20
|
+
| `@geenius/i18n-shared` | Core types, utilities, locale data |
|
|
21
|
+
| `@geenius/i18n-convex` | Convex schema and query/mutation factories |
|
|
22
|
+
| `@geenius/i18n-react` | React hooks, components, admin pages (Tailwind) |
|
|
23
|
+
| `@geenius/i18n-solidjs` | SolidJS primitives, components, admin pages |
|
|
24
|
+
| `@geenius/i18n-react-css` | React components (vanilla CSS) |
|
|
25
|
+
| `@geenius/i18n-solidjs-css` | SolidJS components (vanilla CSS) |
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### React
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { I18nProvider, useI18n } from '@geenius/i18n/react'
|
|
33
|
+
|
|
34
|
+
const translations = {
|
|
35
|
+
greeting: 'Hello {{name}}!',
|
|
36
|
+
items_zero: 'No items',
|
|
37
|
+
items_one: '1 item',
|
|
38
|
+
items_other: '{{count}} items',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
return (
|
|
43
|
+
<I18nProvider
|
|
44
|
+
config={{ defaultLocale: 'en', supportedLocales: ['en', 'es', 'fr'], persistLocale: true, detectBrowser: true }}
|
|
45
|
+
translations={translations}
|
|
46
|
+
>
|
|
47
|
+
<Dashboard />
|
|
48
|
+
</I18nProvider>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Dashboard() {
|
|
53
|
+
const { t, locale, setLocale, formatDate, formatCurrency, isRTL } = useI18n()
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div dir={isRTL ? 'rtl' : 'ltr'}>
|
|
57
|
+
<h1>{t('greeting', { name: 'Mehdi' })}</h1>
|
|
58
|
+
<p>{formatDate(new Date())}</p>
|
|
59
|
+
<p>{formatCurrency(49.99, 'USD')}</p>
|
|
60
|
+
<button onClick={() => setLocale('es')}>Switch to Spanish</button>
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### SolidJS
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { I18nProvider, createI18n } from '@geenius/i18n/solidjs'
|
|
70
|
+
|
|
71
|
+
function Dashboard() {
|
|
72
|
+
const { t, locale, setLocale } = createI18n()
|
|
73
|
+
return <h1>{t('greeting', { name: 'Mehdi' })}</h1>
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Core Utilities
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { interpolate, t, plural, isRTL, formatDate, formatNumber, formatCurrency, detectLocale } from '@geenius/i18n/shared'
|
|
81
|
+
|
|
82
|
+
interpolate('Hello {{name}}!', { name: 'Alice' }) // "Hello Alice!"
|
|
83
|
+
isRTL('ar') // true
|
|
84
|
+
formatCurrency(42.99, 'EUR', 'de') // "42,99 €"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Convex Backend
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { schema, createI18nQueries, createI18nMutations } from '@geenius/i18n/convex'
|
|
91
|
+
import { query, mutation } from './_generated/server'
|
|
92
|
+
|
|
93
|
+
// Wire into your Convex project
|
|
94
|
+
const { listTranslations, searchTranslations } = createI18nQueries(query)
|
|
95
|
+
const { upsertTranslation, importTranslations } = createI18nMutations(mutation)
|
|
96
|
+
|
|
97
|
+
export { listTranslations, searchTranslations, upsertTranslation, importTranslations }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Config Builder
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { createI18nConfig, i18nPresets } from '@geenius/i18n/shared'
|
|
104
|
+
|
|
105
|
+
// Builder pattern
|
|
106
|
+
const config = createI18nConfig()
|
|
107
|
+
.withDefaultLocale('en')
|
|
108
|
+
.withSupportedLocales(['en', 'fr', 'de', 'es'])
|
|
109
|
+
.withFallbackLocale('en')
|
|
110
|
+
.withDebug(true)
|
|
111
|
+
.build()
|
|
112
|
+
|
|
113
|
+
// Or use presets
|
|
114
|
+
const simple = i18nPresets.simple('en')
|
|
115
|
+
const multi = i18nPresets.multi()
|
|
116
|
+
const global = i18nPresets.global()
|
|
117
|
+
const dev = i18nPresets.development()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Supported Locales
|
|
121
|
+
|
|
122
|
+
English, French, German, Spanish, Portuguese, Italian, Dutch, Russian, Chinese, Japanese, Korean, Arabic (RTL), Hebrew (RTL), Turkish
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pnpm install
|
|
128
|
+
pnpm build # Build all packages
|
|
129
|
+
pnpm test # Run tests
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geenius/i18n",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Geenius I18n — Internationalization for Convex apps (React + SolidJS)",
|
|
6
6
|
"author": "Antigravity HQ",
|
|
7
7
|
"license": "MIT",
|
|
@@ -19,19 +19,32 @@
|
|
|
19
19
|
"solidjs",
|
|
20
20
|
"geenius"
|
|
21
21
|
],
|
|
22
|
+
"files": [
|
|
23
|
+
"packages/shared/dist",
|
|
24
|
+
"packages/react/dist",
|
|
25
|
+
"packages/solidjs/dist",
|
|
26
|
+
"packages/convex/dist",
|
|
27
|
+
"packages/react-css/dist",
|
|
28
|
+
"packages/solidjs-css/dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"CHANGELOG.md"
|
|
32
|
+
],
|
|
22
33
|
"scripts": {
|
|
23
34
|
"dev": "pnpm -r --parallel type-check",
|
|
24
35
|
"build": "pnpm -r build",
|
|
25
36
|
"clean": "pnpm -r clean",
|
|
26
37
|
"lint": "pnpm -r --parallel type-check",
|
|
27
|
-
"test": "
|
|
38
|
+
"test": "pnpm -r test",
|
|
28
39
|
"type-check": "pnpm -r type-check",
|
|
29
40
|
"format": "prettier --write \"packages/*/src/**/*.{ts,tsx}\"",
|
|
30
41
|
"version:patch": "pnpm -r exec -- npm version patch --no-git-tag-version && npm version patch -m 'v%s'",
|
|
31
42
|
"version:minor": "pnpm -r exec -- npm version minor --no-git-tag-version && npm version minor -m 'v%s'",
|
|
32
43
|
"version:major": "pnpm -r exec -- npm version major --no-git-tag-version && npm version major -m 'v%s'",
|
|
33
44
|
"release": "git push && git push --tags",
|
|
34
|
-
"publish:all": "pnpm -r publish --access public"
|
|
45
|
+
"publish:all": "pnpm -r publish --access public",
|
|
46
|
+
"publish:root": "npm publish --access public",
|
|
47
|
+
"prepublishOnly": "pnpm build"
|
|
35
48
|
},
|
|
36
49
|
"devDependencies": {
|
|
37
50
|
"prettier": "^3.8.1",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as convex_server from 'convex/server';
|
|
2
|
+
import * as convex_values from 'convex/values';
|
|
3
|
+
|
|
4
|
+
declare const _default: convex_server.SchemaDefinition<{
|
|
5
|
+
i18n_translations: convex_server.TableDefinition<convex_values.VObject<{
|
|
6
|
+
lastEditedBy?: string | undefined;
|
|
7
|
+
locale: string;
|
|
8
|
+
namespace: string;
|
|
9
|
+
key: string;
|
|
10
|
+
value: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
}, {
|
|
14
|
+
locale: convex_values.VString<string, "required">;
|
|
15
|
+
namespace: convex_values.VString<string, "required">;
|
|
16
|
+
key: convex_values.VString<string, "required">;
|
|
17
|
+
value: convex_values.VString<string, "required">;
|
|
18
|
+
lastEditedBy: convex_values.VString<string | undefined, "optional">;
|
|
19
|
+
updatedAt: convex_values.VString<string, "required">;
|
|
20
|
+
createdAt: convex_values.VString<string, "required">;
|
|
21
|
+
}, "required", "locale" | "namespace" | "key" | "value" | "lastEditedBy" | "updatedAt" | "createdAt">, {
|
|
22
|
+
by_locale_ns: ["locale", "namespace", "_creationTime"];
|
|
23
|
+
by_key: ["key", "_creationTime"];
|
|
24
|
+
}, {}, {}>;
|
|
25
|
+
i18n_missing_keys: convex_server.TableDefinition<convex_values.VObject<{
|
|
26
|
+
locale: string;
|
|
27
|
+
namespace: string;
|
|
28
|
+
key: string;
|
|
29
|
+
detectedAt: string;
|
|
30
|
+
count: number;
|
|
31
|
+
}, {
|
|
32
|
+
locale: convex_values.VString<string, "required">;
|
|
33
|
+
namespace: convex_values.VString<string, "required">;
|
|
34
|
+
key: convex_values.VString<string, "required">;
|
|
35
|
+
detectedAt: convex_values.VString<string, "required">;
|
|
36
|
+
count: convex_values.VFloat64<number, "required">;
|
|
37
|
+
}, "required", "locale" | "namespace" | "key" | "detectedAt" | "count">, {
|
|
38
|
+
by_locale: ["locale", "_creationTime"];
|
|
39
|
+
by_key: ["key", "_creationTime"];
|
|
40
|
+
}, {}, {}>;
|
|
41
|
+
}, true>;
|
|
42
|
+
|
|
43
|
+
type QueryFn = (...args: any[]) => any;
|
|
44
|
+
declare function createI18nQueries(query: QueryFn): {
|
|
45
|
+
listTranslations: any;
|
|
46
|
+
getTranslation: any;
|
|
47
|
+
listLocaleStats: any;
|
|
48
|
+
listMissingKeys: any;
|
|
49
|
+
searchTranslations: any;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type MutationFn = (...args: any[]) => any;
|
|
53
|
+
declare function createI18nMutations(mutation: MutationFn): {
|
|
54
|
+
upsertTranslation: any;
|
|
55
|
+
deleteTranslation: any;
|
|
56
|
+
importTranslations: any;
|
|
57
|
+
reportMissingKey: any;
|
|
58
|
+
clearMissingKey: any;
|
|
59
|
+
bulkUpsert: any;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { createI18nMutations, createI18nQueries, _default as schema };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
3
|
+
import { v } from "convex/values";
|
|
4
|
+
var schema_default = defineSchema({
|
|
5
|
+
i18n_translations: defineTable({
|
|
6
|
+
locale: v.string(),
|
|
7
|
+
namespace: v.string(),
|
|
8
|
+
key: v.string(),
|
|
9
|
+
value: v.string(),
|
|
10
|
+
lastEditedBy: v.optional(v.string()),
|
|
11
|
+
updatedAt: v.string(),
|
|
12
|
+
createdAt: v.string()
|
|
13
|
+
}).index("by_locale_ns", ["locale", "namespace"]).index("by_key", ["key"]),
|
|
14
|
+
i18n_missing_keys: defineTable({
|
|
15
|
+
locale: v.string(),
|
|
16
|
+
namespace: v.string(),
|
|
17
|
+
key: v.string(),
|
|
18
|
+
detectedAt: v.string(),
|
|
19
|
+
count: v.number()
|
|
20
|
+
}).index("by_locale", ["locale"]).index("by_key", ["key"])
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// src/queries.ts
|
|
24
|
+
import { v as v2 } from "convex/values";
|
|
25
|
+
function createI18nQueries(query) {
|
|
26
|
+
const listTranslations = query({
|
|
27
|
+
args: { locale: v2.string(), namespace: v2.optional(v2.string()) },
|
|
28
|
+
handler: async (ctx, args) => {
|
|
29
|
+
if (args.namespace) return ctx.db.query("i18n_translations").withIndex("by_locale_ns", (q) => q.eq("locale", args.locale).eq("namespace", args.namespace)).collect();
|
|
30
|
+
return ctx.db.query("i18n_translations").withIndex("by_locale_ns", (q) => q.eq("locale", args.locale)).collect();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const getTranslation = query({
|
|
34
|
+
args: { locale: v2.string(), key: v2.string(), namespace: v2.optional(v2.string()) },
|
|
35
|
+
handler: async (ctx, args) => {
|
|
36
|
+
const results = await ctx.db.query("i18n_translations").withIndex("by_key", (q) => q.eq("key", args.key)).collect();
|
|
37
|
+
return results.find((r) => r.locale === args.locale && (!args.namespace || r.namespace === args.namespace)) ?? null;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const listLocaleStats = query({
|
|
41
|
+
args: {},
|
|
42
|
+
handler: async (ctx) => {
|
|
43
|
+
const all = await ctx.db.query("i18n_translations").collect();
|
|
44
|
+
const missing = await ctx.db.query("i18n_missing_keys").collect();
|
|
45
|
+
const localeMap = /* @__PURE__ */ new Map();
|
|
46
|
+
const missingMap = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const t of all) localeMap.set(t.locale, (localeMap.get(t.locale) ?? 0) + 1);
|
|
48
|
+
for (const m of missing) missingMap.set(m.locale, (missingMap.get(m.locale) ?? 0) + 1);
|
|
49
|
+
const stats = [];
|
|
50
|
+
for (const [locale, total] of localeMap) {
|
|
51
|
+
const miss = missingMap.get(locale) ?? 0;
|
|
52
|
+
stats.push({ locale, totalKeys: total, missingKeys: miss, coverage: total > 0 ? Math.round((total - miss) / total * 100) : 0 });
|
|
53
|
+
}
|
|
54
|
+
return stats;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const listMissingKeys = query({
|
|
58
|
+
args: { locale: v2.optional(v2.string()), limit: v2.optional(v2.number()) },
|
|
59
|
+
handler: async (ctx, args) => {
|
|
60
|
+
if (args.locale) return ctx.db.query("i18n_missing_keys").withIndex("by_locale", (q) => q.eq("locale", args.locale)).take(args.limit ?? 100);
|
|
61
|
+
return ctx.db.query("i18n_missing_keys").order("desc").take(args.limit ?? 100);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const searchTranslations = query({
|
|
65
|
+
args: { query: v2.string(), locale: v2.optional(v2.string()) },
|
|
66
|
+
handler: async (ctx, args) => {
|
|
67
|
+
let items = await ctx.db.query("i18n_translations").collect();
|
|
68
|
+
if (args.locale) items = items.filter((i) => i.locale === args.locale);
|
|
69
|
+
const q = args.query.toLowerCase();
|
|
70
|
+
return items.filter((i) => i.key.toLowerCase().includes(q) || i.value.toLowerCase().includes(q)).slice(0, 50);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return { listTranslations, getTranslation, listLocaleStats, listMissingKeys, searchTranslations };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/mutations.ts
|
|
77
|
+
import { v as v3 } from "convex/values";
|
|
78
|
+
function createI18nMutations(mutation) {
|
|
79
|
+
const upsertTranslation = mutation({
|
|
80
|
+
args: { locale: v3.string(), namespace: v3.string(), key: v3.string(), value: v3.string(), editedBy: v3.optional(v3.string()) },
|
|
81
|
+
handler: async (ctx, args) => {
|
|
82
|
+
const existing = await ctx.db.query("i18n_translations").withIndex("by_key", (q) => q.eq("key", args.key)).collect();
|
|
83
|
+
const match = existing.find((e) => e.locale === args.locale && e.namespace === args.namespace);
|
|
84
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
85
|
+
if (match) {
|
|
86
|
+
await ctx.db.patch(match._id, { value: args.value, lastEditedBy: args.editedBy, updatedAt: now });
|
|
87
|
+
} else {
|
|
88
|
+
await ctx.db.insert("i18n_translations", { locale: args.locale, namespace: args.namespace, key: args.key, value: args.value, lastEditedBy: args.editedBy, updatedAt: now, createdAt: now });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const deleteTranslation = mutation({
|
|
93
|
+
args: { locale: v3.string(), key: v3.string(), namespace: v3.optional(v3.string()) },
|
|
94
|
+
handler: async (ctx, args) => {
|
|
95
|
+
const items = await ctx.db.query("i18n_translations").withIndex("by_key", (q) => q.eq("key", args.key)).collect();
|
|
96
|
+
for (const item of items) {
|
|
97
|
+
if (item.locale === args.locale && (!args.namespace || item.namespace === args.namespace)) await ctx.db.delete(item._id);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const importTranslations = mutation({
|
|
102
|
+
args: { locale: v3.string(), namespace: v3.string(), dict: v3.any() },
|
|
103
|
+
handler: async (ctx, args) => {
|
|
104
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
105
|
+
const entries = args.dict;
|
|
106
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
107
|
+
const existing = await ctx.db.query("i18n_translations").withIndex("by_key", (q) => q.eq("key", key)).collect();
|
|
108
|
+
const match = existing.find((e) => e.locale === args.locale && e.namespace === args.namespace);
|
|
109
|
+
if (match) {
|
|
110
|
+
await ctx.db.patch(match._id, { value, updatedAt: now });
|
|
111
|
+
} else {
|
|
112
|
+
await ctx.db.insert("i18n_translations", { locale: args.locale, namespace: args.namespace, key, value, updatedAt: now, createdAt: now });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const reportMissingKey = mutation({
|
|
118
|
+
args: { locale: v3.string(), namespace: v3.string(), key: v3.string() },
|
|
119
|
+
handler: async (ctx, args) => {
|
|
120
|
+
const existing = await ctx.db.query("i18n_missing_keys").withIndex("by_key", (q) => q.eq("key", args.key)).collect();
|
|
121
|
+
const match = existing.find((e) => e.locale === args.locale && e.namespace === args.namespace);
|
|
122
|
+
if (match) {
|
|
123
|
+
await ctx.db.patch(match._id, { count: match.count + 1 });
|
|
124
|
+
} else {
|
|
125
|
+
await ctx.db.insert("i18n_missing_keys", { ...args, detectedAt: (/* @__PURE__ */ new Date()).toISOString(), count: 1 });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const clearMissingKey = mutation({
|
|
130
|
+
args: { id: v3.id("i18n_missing_keys") },
|
|
131
|
+
handler: async (ctx, args) => {
|
|
132
|
+
await ctx.db.delete(args.id);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
const bulkUpsert = mutation({
|
|
136
|
+
args: { items: v3.array(v3.object({ locale: v3.string(), namespace: v3.string(), key: v3.string(), value: v3.string() })) },
|
|
137
|
+
handler: async (ctx, args) => {
|
|
138
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
139
|
+
for (const item of args.items) {
|
|
140
|
+
const existing = await ctx.db.query("i18n_translations").withIndex("by_key", (q) => q.eq("key", item.key)).collect();
|
|
141
|
+
const match = existing.find((e) => e.locale === item.locale && e.namespace === item.namespace);
|
|
142
|
+
if (match) {
|
|
143
|
+
await ctx.db.patch(match._id, { value: item.value, updatedAt: now });
|
|
144
|
+
} else {
|
|
145
|
+
await ctx.db.insert("i18n_translations", { ...item, updatedAt: now, createdAt: now });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
return { upsertTranslation, deleteTranslation, importTranslations, reportMissingKey, clearMissingKey, bulkUpsert };
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
createI18nMutations,
|
|
154
|
+
createI18nQueries,
|
|
155
|
+
schema_default as schema
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts","../src/queries.ts","../src/mutations.ts"],"sourcesContent":["import { defineSchema, defineTable } from 'convex/server'\nimport { v } from 'convex/values'\n\nexport default defineSchema({\n i18n_translations: defineTable({\n locale: v.string(),\n namespace: v.string(),\n key: v.string(),\n value: v.string(),\n lastEditedBy: v.optional(v.string()),\n updatedAt: v.string(),\n createdAt: v.string(),\n })\n .index('by_locale_ns', ['locale', 'namespace'])\n .index('by_key', ['key']),\n\n i18n_missing_keys: defineTable({\n locale: v.string(),\n namespace: v.string(),\n key: v.string(),\n detectedAt: v.string(),\n count: v.number(),\n })\n .index('by_locale', ['locale'])\n .index('by_key', ['key']),\n})\n","import { v } from 'convex/values'\n\ntype QueryFn = (...args: any[]) => any\n\nexport function createI18nQueries(query: QueryFn) {\n const listTranslations = query({\n args: { locale: v.string(), namespace: v.optional(v.string()) },\n handler: async (ctx: any, args: { locale: string; namespace?: string }) => {\n if (args.namespace) return ctx.db.query('i18n_translations').withIndex('by_locale_ns', (q: any) => q.eq('locale', args.locale).eq('namespace', args.namespace!)).collect()\n return ctx.db.query('i18n_translations').withIndex('by_locale_ns', (q: any) => q.eq('locale', args.locale)).collect()\n },\n })\n\n const getTranslation = query({\n args: { locale: v.string(), key: v.string(), namespace: v.optional(v.string()) },\n handler: async (ctx: any, args: { locale: string; key: string; namespace?: string }) => {\n const results = await ctx.db.query('i18n_translations').withIndex('by_key', (q: any) => q.eq('key', args.key)).collect()\n return results.find((r: any) => r.locale === args.locale && (!args.namespace || r.namespace === args.namespace)) ?? null\n },\n })\n\n const listLocaleStats = query({\n args: {},\n handler: async (ctx: any) => {\n const all = await ctx.db.query('i18n_translations').collect()\n const missing = await ctx.db.query('i18n_missing_keys').collect()\n const localeMap = new Map<string, number>()\n const missingMap = new Map<string, number>()\n for (const t of all) localeMap.set(t.locale, (localeMap.get(t.locale) ?? 0) + 1)\n for (const m of missing) missingMap.set(m.locale, (missingMap.get(m.locale) ?? 0) + 1)\n const stats: { locale: string; totalKeys: number; missingKeys: number; coverage: number }[] = []\n for (const [locale, total] of localeMap) {\n const miss = missingMap.get(locale) ?? 0\n stats.push({ locale, totalKeys: total, missingKeys: miss, coverage: total > 0 ? Math.round(((total - miss) / total) * 100) : 0 })\n }\n return stats\n },\n })\n\n const listMissingKeys = query({\n args: { locale: v.optional(v.string()), limit: v.optional(v.number()) },\n handler: async (ctx: any, args: { locale?: string; limit?: number }) => {\n if (args.locale) return ctx.db.query('i18n_missing_keys').withIndex('by_locale', (q: any) => q.eq('locale', args.locale!)).take(args.limit ?? 100)\n return ctx.db.query('i18n_missing_keys').order('desc').take(args.limit ?? 100)\n },\n })\n\n const searchTranslations = query({\n args: { query: v.string(), locale: v.optional(v.string()) },\n handler: async (ctx: any, args: { query: string; locale?: string }) => {\n let items = await ctx.db.query('i18n_translations').collect()\n if (args.locale) items = items.filter((i: any) => i.locale === args.locale)\n const q = args.query.toLowerCase()\n return items.filter((i: any) => i.key.toLowerCase().includes(q) || i.value.toLowerCase().includes(q)).slice(0, 50)\n },\n })\n\n return { listTranslations, getTranslation, listLocaleStats, listMissingKeys, searchTranslations }\n}\n","import { v } from 'convex/values'\n\ntype MutationFn = (...args: any[]) => any\n\nexport function createI18nMutations(mutation: MutationFn) {\n const upsertTranslation = mutation({\n args: { locale: v.string(), namespace: v.string(), key: v.string(), value: v.string(), editedBy: v.optional(v.string()) },\n handler: async (ctx: any, args: { locale: string; namespace: string; key: string; value: string; editedBy?: string }) => {\n const existing = await ctx.db.query('i18n_translations').withIndex('by_key', (q: any) => q.eq('key', args.key)).collect()\n const match = existing.find((e: any) => e.locale === args.locale && e.namespace === args.namespace)\n const now = new Date().toISOString()\n if (match) { await ctx.db.patch(match._id, { value: args.value, lastEditedBy: args.editedBy, updatedAt: now }) }\n else { await ctx.db.insert('i18n_translations', { locale: args.locale, namespace: args.namespace, key: args.key, value: args.value, lastEditedBy: args.editedBy, updatedAt: now, createdAt: now }) }\n },\n })\n\n const deleteTranslation = mutation({\n args: { locale: v.string(), key: v.string(), namespace: v.optional(v.string()) },\n handler: async (ctx: any, args: { locale: string; key: string; namespace?: string }) => {\n const items = await ctx.db.query('i18n_translations').withIndex('by_key', (q: any) => q.eq('key', args.key)).collect()\n for (const item of items) {\n if (item.locale === args.locale && (!args.namespace || item.namespace === args.namespace)) await ctx.db.delete(item._id)\n }\n },\n })\n\n const importTranslations = mutation({\n args: { locale: v.string(), namespace: v.string(), dict: v.any() },\n handler: async (ctx: any, args: { locale: string; namespace: string; dict: any }) => {\n const now = new Date().toISOString()\n const entries = args.dict as Record<string, string>\n for (const [key, value] of Object.entries(entries)) {\n const existing = await ctx.db.query('i18n_translations').withIndex('by_key', (q: any) => q.eq('key', key)).collect()\n const match = existing.find((e: any) => e.locale === args.locale && e.namespace === args.namespace)\n if (match) { await ctx.db.patch(match._id, { value, updatedAt: now }) }\n else { await ctx.db.insert('i18n_translations', { locale: args.locale, namespace: args.namespace, key, value, updatedAt: now, createdAt: now }) }\n }\n },\n })\n\n const reportMissingKey = mutation({\n args: { locale: v.string(), namespace: v.string(), key: v.string() },\n handler: async (ctx: any, args: { locale: string; namespace: string; key: string }) => {\n const existing = await ctx.db.query('i18n_missing_keys').withIndex('by_key', (q: any) => q.eq('key', args.key)).collect()\n const match = existing.find((e: any) => e.locale === args.locale && e.namespace === args.namespace)\n if (match) { await ctx.db.patch(match._id, { count: match.count + 1 }) }\n else { await ctx.db.insert('i18n_missing_keys', { ...args, detectedAt: new Date().toISOString(), count: 1 }) }\n },\n })\n\n const clearMissingKey = mutation({\n args: { id: v.id('i18n_missing_keys') },\n handler: async (ctx: any, args: { id: any }) => { await ctx.db.delete(args.id) },\n })\n\n const bulkUpsert = mutation({\n args: { items: v.array(v.object({ locale: v.string(), namespace: v.string(), key: v.string(), value: v.string() })) },\n handler: async (ctx: any, args: { items: Array<{ locale: string; namespace: string; key: string; value: string }> }) => {\n const now = new Date().toISOString()\n for (const item of args.items) {\n const existing = await ctx.db.query('i18n_translations').withIndex('by_key', (q: any) => q.eq('key', item.key)).collect()\n const match = existing.find((e: any) => e.locale === item.locale && e.namespace === item.namespace)\n if (match) { await ctx.db.patch(match._id, { value: item.value, updatedAt: now }) }\n else { await ctx.db.insert('i18n_translations', { ...item, updatedAt: now, createdAt: now }) }\n }\n },\n })\n\n return { upsertTranslation, deleteTranslation, importTranslations, reportMissingKey, clearMissingKey, bulkUpsert }\n}\n"],"mappings":"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","names":["v","v"]}
|
package/packages/react/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
# ✦ @geenius-
|
|
1
|
+
# ✦ @geenius/i18n-react\n\n> Geenius I18n — React components & hooks\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius/i18n-react\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius/i18n-react';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import react__default from 'react';
|
|
4
|
+
import * as _geenius_i18n_shared from '@geenius/i18n-shared';
|
|
5
|
+
import { I18nConfig, TranslationDict, Locale, Direction, I18nNamespace, TranslationEntry, MissingKey, LocaleStat } from '@geenius/i18n-shared';
|
|
6
|
+
export { Direction, I18nConfig, I18nNamespace, Locale, LocaleInfo, LocaleStat, MissingKey, TranslationDict, TranslationEntry } from '@geenius/i18n-shared';
|
|
7
|
+
|
|
8
|
+
interface I18nContextValue {
|
|
9
|
+
locale: Locale;
|
|
10
|
+
direction: Direction;
|
|
11
|
+
isRTL: boolean;
|
|
12
|
+
setLocale: (l: Locale) => void;
|
|
13
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
14
|
+
formatDate: (date: Date | string, opts?: Intl.DateTimeFormatOptions) => string;
|
|
15
|
+
formatNumber: (n: number, opts?: Intl.NumberFormatOptions) => string;
|
|
16
|
+
formatCurrency: (amount: number, currency: string) => string;
|
|
17
|
+
}
|
|
18
|
+
declare function I18nProvider({ children, config, translations }: {
|
|
19
|
+
children: react__default.ReactNode;
|
|
20
|
+
config: I18nConfig;
|
|
21
|
+
translations?: TranslationDict;
|
|
22
|
+
}): react_jsx_runtime.JSX.Element;
|
|
23
|
+
declare function useI18n(): I18nContextValue;
|
|
24
|
+
|
|
25
|
+
declare function useLocaleDetect(supportedLocales: Locale[]): {
|
|
26
|
+
detectedLocale: Locale;
|
|
27
|
+
isDetecting: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
declare function useTranslations(locale: Locale, namespace: I18nNamespace): {
|
|
31
|
+
dict: TranslationDict;
|
|
32
|
+
isLoading: boolean;
|
|
33
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
declare function useI18nAdmin(data: {
|
|
37
|
+
translations?: TranslationEntry[];
|
|
38
|
+
missingKeys?: MissingKey[];
|
|
39
|
+
localeStats?: LocaleStat[];
|
|
40
|
+
}, mutations: {
|
|
41
|
+
upsert: (locale: string, ns: string, key: string, value: string) => Promise<void>;
|
|
42
|
+
deleteKey: (locale: string, key: string) => Promise<void>;
|
|
43
|
+
importDict: (locale: string, ns: string, dict: Record<string, string>) => Promise<void>;
|
|
44
|
+
clearMissing: (id: string) => Promise<void>;
|
|
45
|
+
}): {
|
|
46
|
+
upsert: (locale: string, ns: string, key: string, value: string) => Promise<void>;
|
|
47
|
+
deleteKey: (locale: string, key: string) => Promise<void>;
|
|
48
|
+
importDict: (locale: string, ns: string, dict: Record<string, string>) => Promise<void>;
|
|
49
|
+
clearMissing: (id: string) => Promise<void>;
|
|
50
|
+
translations: TranslationEntry[];
|
|
51
|
+
missingKeys: MissingKey[];
|
|
52
|
+
localeStats: LocaleStat[];
|
|
53
|
+
search: string;
|
|
54
|
+
setSearch: react.Dispatch<react.SetStateAction<string>>;
|
|
55
|
+
isLoading: boolean;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
declare function LocaleSwitcher({ locales, current, onChange }: {
|
|
59
|
+
locales: Locale[];
|
|
60
|
+
current: Locale;
|
|
61
|
+
onChange: (l: Locale) => void;
|
|
62
|
+
}): react_jsx_runtime.JSX.Element;
|
|
63
|
+
declare function RTLWrapper({ children, locale }: {
|
|
64
|
+
children: react__default.ReactNode;
|
|
65
|
+
locale?: Locale;
|
|
66
|
+
}): react_jsx_runtime.JSX.Element;
|
|
67
|
+
declare function TranslationEditor({ onSave, initialKey, initialLocale, namespaces }: {
|
|
68
|
+
onSave: (locale: string, ns: string, key: string, value: string) => void;
|
|
69
|
+
initialKey?: string;
|
|
70
|
+
initialLocale?: Locale;
|
|
71
|
+
namespaces?: string[];
|
|
72
|
+
}): react_jsx_runtime.JSX.Element;
|
|
73
|
+
declare function TranslationTable({ translations, locale, onUpdate, onDelete }: {
|
|
74
|
+
translations: TranslationEntry[];
|
|
75
|
+
locale: Locale;
|
|
76
|
+
onUpdate?: (key: string, value: string) => void;
|
|
77
|
+
onDelete?: (key: string) => void;
|
|
78
|
+
}): react_jsx_runtime.JSX.Element;
|
|
79
|
+
declare function MissingKeyAlert({ count, locale }: {
|
|
80
|
+
count: number;
|
|
81
|
+
locale: Locale;
|
|
82
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
83
|
+
declare function LocaleStatsCard({ stats }: {
|
|
84
|
+
stats: LocaleStat[];
|
|
85
|
+
}): react_jsx_runtime.JSX.Element;
|
|
86
|
+
|
|
87
|
+
interface I18nAdminPageProps {
|
|
88
|
+
translations?: TranslationEntry[];
|
|
89
|
+
missingKeys?: MissingKey[];
|
|
90
|
+
localeStats?: LocaleStat[];
|
|
91
|
+
mutations: Parameters<typeof useI18nAdmin>[1];
|
|
92
|
+
}
|
|
93
|
+
declare function I18nAdminPage({ translations, missingKeys, localeStats, mutations }: I18nAdminPageProps): react_jsx_runtime.JSX.Element;
|
|
94
|
+
|
|
95
|
+
declare function LocalePreviewPage({ config, translations }: {
|
|
96
|
+
config?: Partial<_geenius_i18n_shared.I18nConfig>;
|
|
97
|
+
translations?: TranslationDict;
|
|
98
|
+
}): react_jsx_runtime.JSX.Element;
|
|
99
|
+
|
|
100
|
+
export { I18nAdminPage, I18nProvider, LocalePreviewPage, LocaleStatsCard, LocaleSwitcher, MissingKeyAlert, RTLWrapper, TranslationEditor, TranslationTable, useI18n, useI18nAdmin, useLocaleDetect, useTranslations };
|