@kokimoki/app 3.1.1 → 3.1.3
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/dist/index.d.ts +1 -1
- package/dist/services/kokimoki-ai.d.ts +15 -53
- package/dist/services/kokimoki-ai.js +11 -82
- package/dist/services/kokimoki-i18n.d.ts +25 -97
- package/dist/services/kokimoki-i18n.js +54 -115
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/kokimoki-ai.instructions.md +29 -86
- package/docs/kokimoki-i18n.instructions.md +8 -115
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { APP_META_STORE_NAME } from "./core/kokimoki-client";
|
|
|
3
3
|
export type { AppMetaState } from "./core/kokimoki-client";
|
|
4
4
|
export { KokimokiStore } from "./stores";
|
|
5
5
|
export type { KokimokiClientEvents, KokimokiEnv, Paginated, PollOptions, Upload, } from "./types";
|
|
6
|
-
export type {
|
|
6
|
+
export type { I18nOptions, TranslationStatus as LanguageStatus, TranslationStatus as NamespaceStatus, TranslationStatus, } from "./services/kokimoki-i18n";
|
|
7
7
|
export type { AiJob, AiJobStatus, AiJobType } from "./services/kokimoki-ai";
|
|
8
8
|
export { getKmClient } from "./utils/kokimoki-client";
|
|
9
9
|
export { getKmEnv } from "./utils/kokimoki-env";
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ZodType } from "zod/v4";
|
|
2
2
|
import { KokimokiClient } from "../core";
|
|
3
|
-
import type { PollOptions, Upload } from "../types";
|
|
4
3
|
/** AI job status */
|
|
5
4
|
export type AiJobStatus = "processing" | "queued" | "completed" | "failed";
|
|
6
5
|
/** AI job type */
|
|
7
6
|
export type AiJobType = "text" | "json" | "image";
|
|
8
7
|
/** AI job information */
|
|
9
|
-
export interface AiJob {
|
|
8
|
+
export interface AiJob<T = unknown> {
|
|
10
9
|
jobId: string;
|
|
11
10
|
type: AiJobType;
|
|
12
11
|
status: AiJobStatus;
|
|
13
|
-
result?:
|
|
12
|
+
result?: T;
|
|
14
13
|
error?: {
|
|
15
14
|
message: string;
|
|
16
15
|
code?: string;
|
|
@@ -109,7 +108,7 @@ export declare class KokimokiAiService {
|
|
|
109
108
|
* Generate text content from the AI model.
|
|
110
109
|
*
|
|
111
110
|
* Submits a text generation job and returns immediately with a jobId.
|
|
112
|
-
* Use `
|
|
111
|
+
* Use `getJob()` to poll for the result.
|
|
113
112
|
*
|
|
114
113
|
* @param req The generation request parameters.
|
|
115
114
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -122,7 +121,8 @@ export declare class KokimokiAiService {
|
|
|
122
121
|
* temperature: 0.8
|
|
123
122
|
* });
|
|
124
123
|
*
|
|
125
|
-
* const
|
|
124
|
+
* const job = await kmClient.ai.getJob<string>(jobId);
|
|
125
|
+
* console.log('Generated quest:', job.result);
|
|
126
126
|
* ```
|
|
127
127
|
*/
|
|
128
128
|
generateText(req: TextGenerateRequest): Promise<JobSubmitResponse>;
|
|
@@ -131,7 +131,7 @@ export declare class KokimokiAiService {
|
|
|
131
131
|
*
|
|
132
132
|
* Submits a JSON generation job and returns immediately with a jobId.
|
|
133
133
|
* The Zod schema is converted to JSON Schema and sent to the AI.
|
|
134
|
-
* Use `
|
|
134
|
+
* Use `getJob()` to poll for the result with type inference.
|
|
135
135
|
*
|
|
136
136
|
* @param req The generation request parameters including Zod schema.
|
|
137
137
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -144,13 +144,16 @@ export declare class KokimokiAiService {
|
|
|
144
144
|
* attack: z.number()
|
|
145
145
|
* });
|
|
146
146
|
*
|
|
147
|
+
* type Enemy = z.infer<typeof enemySchema>;
|
|
148
|
+
*
|
|
147
149
|
* const { jobId } = await kmClient.ai.generateJson({
|
|
148
150
|
* schema: enemySchema,
|
|
149
151
|
* prompt: 'Create a level 5 goblin warrior'
|
|
150
152
|
* });
|
|
151
153
|
*
|
|
152
154
|
* // Poll with schema for type inference and validation
|
|
153
|
-
* const
|
|
155
|
+
* const job = await kmClient.ai.getJob<Enemy>(jobId);
|
|
156
|
+
* console.log('Generated enemy:', job.result);
|
|
154
157
|
* ```
|
|
155
158
|
*/
|
|
156
159
|
generateJson<T extends ZodType>(req: JsonGenerateRequest<T>): Promise<JobSubmitResponse>;
|
|
@@ -158,7 +161,7 @@ export declare class KokimokiAiService {
|
|
|
158
161
|
* Generate or modify an image using the AI model.
|
|
159
162
|
*
|
|
160
163
|
* Submits an image generation job and returns immediately with a jobId.
|
|
161
|
-
* Use `
|
|
164
|
+
* Use `getJob()` to poll for the result.
|
|
162
165
|
*
|
|
163
166
|
* @param req The generation request parameters.
|
|
164
167
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -171,7 +174,8 @@ export declare class KokimokiAiService {
|
|
|
171
174
|
* prompt: 'Make it look like pixel art'
|
|
172
175
|
* });
|
|
173
176
|
*
|
|
174
|
-
* const
|
|
177
|
+
* const job = await kmClient.ai.getJob<string>(jobId);
|
|
178
|
+
* console.log('Generated image:', job.result);
|
|
175
179
|
* ```
|
|
176
180
|
*/
|
|
177
181
|
generateImage(req: ImageGenerateRequest): Promise<JobSubmitResponse>;
|
|
@@ -183,48 +187,6 @@ export declare class KokimokiAiService {
|
|
|
183
187
|
* @param jobId The job ID to check.
|
|
184
188
|
* @returns A promise that resolves to the job information.
|
|
185
189
|
*/
|
|
186
|
-
getJob(jobId: string): Promise<AiJob
|
|
187
|
-
/**
|
|
188
|
-
* Poll a text generation job until completion.
|
|
189
|
-
*
|
|
190
|
-
* @param jobId The job ID to poll.
|
|
191
|
-
* @param options Polling options (interval, timeout, progress callback).
|
|
192
|
-
* @returns A promise that resolves to the generated text.
|
|
193
|
-
* @throws If the job fails or times out.
|
|
194
|
-
*/
|
|
195
|
-
pollText(jobId: string, options?: PollOptions<AiJobStatus>): Promise<string>;
|
|
196
|
-
/**
|
|
197
|
-
* Poll a JSON generation job until completion.
|
|
198
|
-
*
|
|
199
|
-
* @param jobId The job ID to poll.
|
|
200
|
-
* @param options Polling options including the Zod schema for validation.
|
|
201
|
-
* @returns A promise that resolves to the parsed and validated JSON.
|
|
202
|
-
* @throws If the job fails, times out, or validation fails.
|
|
203
|
-
*
|
|
204
|
-
* @example
|
|
205
|
-
* ```typescript
|
|
206
|
-
* const enemy = await kmClient.ai.pollJson(savedJobId, {
|
|
207
|
-
* schema: enemySchema,
|
|
208
|
-
* timeout: 60000,
|
|
209
|
-
* onProgress: (status) => console.log('Status:', status)
|
|
210
|
-
* });
|
|
211
|
-
* ```
|
|
212
|
-
*/
|
|
213
|
-
pollJson<T extends ZodType>(jobId: string, options: PollOptions<AiJobStatus> & {
|
|
214
|
-
schema: T;
|
|
215
|
-
}): Promise<z.infer<T>>;
|
|
216
|
-
/**
|
|
217
|
-
* Poll an image generation job until completion.
|
|
218
|
-
*
|
|
219
|
-
* @param jobId The job ID to poll.
|
|
220
|
-
* @param options Polling options (interval, timeout, progress callback).
|
|
221
|
-
* @returns A promise that resolves to the upload information.
|
|
222
|
-
* @throws If the job fails or times out.
|
|
223
|
-
*/
|
|
224
|
-
pollImage(jobId: string, options?: PollOptions<AiJobStatus>): Promise<Upload>;
|
|
225
|
-
/**
|
|
226
|
-
* Internal polling implementation.
|
|
227
|
-
*/
|
|
228
|
-
private pollJob;
|
|
190
|
+
getJob<T = unknown>(jobId: string): Promise<AiJob<T>>;
|
|
229
191
|
}
|
|
230
192
|
export {};
|
|
@@ -58,7 +58,7 @@ export class KokimokiAiService {
|
|
|
58
58
|
* Generate text content from the AI model.
|
|
59
59
|
*
|
|
60
60
|
* Submits a text generation job and returns immediately with a jobId.
|
|
61
|
-
* Use `
|
|
61
|
+
* Use `getJob()` to poll for the result.
|
|
62
62
|
*
|
|
63
63
|
* @param req The generation request parameters.
|
|
64
64
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -71,7 +71,8 @@ export class KokimokiAiService {
|
|
|
71
71
|
* temperature: 0.8
|
|
72
72
|
* });
|
|
73
73
|
*
|
|
74
|
-
* const
|
|
74
|
+
* const job = await kmClient.ai.getJob<string>(jobId);
|
|
75
|
+
* console.log('Generated quest:', job.result);
|
|
75
76
|
* ```
|
|
76
77
|
*/
|
|
77
78
|
async generateText(req) {
|
|
@@ -93,7 +94,7 @@ export class KokimokiAiService {
|
|
|
93
94
|
*
|
|
94
95
|
* Submits a JSON generation job and returns immediately with a jobId.
|
|
95
96
|
* The Zod schema is converted to JSON Schema and sent to the AI.
|
|
96
|
-
* Use `
|
|
97
|
+
* Use `getJob()` to poll for the result with type inference.
|
|
97
98
|
*
|
|
98
99
|
* @param req The generation request parameters including Zod schema.
|
|
99
100
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -106,13 +107,16 @@ export class KokimokiAiService {
|
|
|
106
107
|
* attack: z.number()
|
|
107
108
|
* });
|
|
108
109
|
*
|
|
110
|
+
* type Enemy = z.infer<typeof enemySchema>;
|
|
111
|
+
*
|
|
109
112
|
* const { jobId } = await kmClient.ai.generateJson({
|
|
110
113
|
* schema: enemySchema,
|
|
111
114
|
* prompt: 'Create a level 5 goblin warrior'
|
|
112
115
|
* });
|
|
113
116
|
*
|
|
114
117
|
* // Poll with schema for type inference and validation
|
|
115
|
-
* const
|
|
118
|
+
* const job = await kmClient.ai.getJob<Enemy>(jobId);
|
|
119
|
+
* console.log('Generated enemy:', job.result);
|
|
116
120
|
* ```
|
|
117
121
|
*/
|
|
118
122
|
async generateJson(req) {
|
|
@@ -136,7 +140,7 @@ export class KokimokiAiService {
|
|
|
136
140
|
* Generate or modify an image using the AI model.
|
|
137
141
|
*
|
|
138
142
|
* Submits an image generation job and returns immediately with a jobId.
|
|
139
|
-
* Use `
|
|
143
|
+
* Use `getJob()` to poll for the result.
|
|
140
144
|
*
|
|
141
145
|
* @param req The generation request parameters.
|
|
142
146
|
* @returns A promise that resolves to an object containing the jobId.
|
|
@@ -149,7 +153,8 @@ export class KokimokiAiService {
|
|
|
149
153
|
* prompt: 'Make it look like pixel art'
|
|
150
154
|
* });
|
|
151
155
|
*
|
|
152
|
-
* const
|
|
156
|
+
* const job = await kmClient.ai.getJob<string>(jobId);
|
|
157
|
+
* console.log('Generated image:', job.result);
|
|
153
158
|
* ```
|
|
154
159
|
*/
|
|
155
160
|
async generateImage(req) {
|
|
@@ -169,9 +174,6 @@ export class KokimokiAiService {
|
|
|
169
174
|
}
|
|
170
175
|
return await res.json();
|
|
171
176
|
}
|
|
172
|
-
// ============================================================
|
|
173
|
-
// Job Status & Polling Methods
|
|
174
|
-
// ============================================================
|
|
175
177
|
/**
|
|
176
178
|
* Get the current status of an AI job.
|
|
177
179
|
*
|
|
@@ -190,77 +192,4 @@ export class KokimokiAiService {
|
|
|
190
192
|
}
|
|
191
193
|
return await res.json();
|
|
192
194
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Poll a text generation job until completion.
|
|
195
|
-
*
|
|
196
|
-
* @param jobId The job ID to poll.
|
|
197
|
-
* @param options Polling options (interval, timeout, progress callback).
|
|
198
|
-
* @returns A promise that resolves to the generated text.
|
|
199
|
-
* @throws If the job fails or times out.
|
|
200
|
-
*/
|
|
201
|
-
async pollText(jobId, options) {
|
|
202
|
-
const result = await this.pollJob(jobId, options);
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Poll a JSON generation job until completion.
|
|
207
|
-
*
|
|
208
|
-
* @param jobId The job ID to poll.
|
|
209
|
-
* @param options Polling options including the Zod schema for validation.
|
|
210
|
-
* @returns A promise that resolves to the parsed and validated JSON.
|
|
211
|
-
* @throws If the job fails, times out, or validation fails.
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```typescript
|
|
215
|
-
* const enemy = await kmClient.ai.pollJson(savedJobId, {
|
|
216
|
-
* schema: enemySchema,
|
|
217
|
-
* timeout: 60000,
|
|
218
|
-
* onProgress: (status) => console.log('Status:', status)
|
|
219
|
-
* });
|
|
220
|
-
* ```
|
|
221
|
-
*/
|
|
222
|
-
async pollJson(jobId, options) {
|
|
223
|
-
const { schema, ...pollOptions } = options;
|
|
224
|
-
const result = await this.pollJob(jobId, pollOptions);
|
|
225
|
-
return schema.parse(result);
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Poll an image generation job until completion.
|
|
229
|
-
*
|
|
230
|
-
* @param jobId The job ID to poll.
|
|
231
|
-
* @param options Polling options (interval, timeout, progress callback).
|
|
232
|
-
* @returns A promise that resolves to the upload information.
|
|
233
|
-
* @throws If the job fails or times out.
|
|
234
|
-
*/
|
|
235
|
-
async pollImage(jobId, options) {
|
|
236
|
-
const result = await this.pollJob(jobId, options);
|
|
237
|
-
return result;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Internal polling implementation.
|
|
241
|
-
*/
|
|
242
|
-
async pollJob(jobId, options) {
|
|
243
|
-
const pollInterval = Math.max(1000, options?.pollInterval ?? 2000);
|
|
244
|
-
const timeout = options?.timeout ?? 120000;
|
|
245
|
-
const startTime = Date.now();
|
|
246
|
-
while (true) {
|
|
247
|
-
const job = await this.getJob(jobId);
|
|
248
|
-
// Call progress callback
|
|
249
|
-
options?.onProgress?.(job.status);
|
|
250
|
-
if (job.status === "completed") {
|
|
251
|
-
return job.result;
|
|
252
|
-
}
|
|
253
|
-
if (job.status === "failed") {
|
|
254
|
-
throw new Error(job.error?.message ?? "AI job failed");
|
|
255
|
-
}
|
|
256
|
-
// processing and queued both continue polling
|
|
257
|
-
// queued jobs will be promoted when slots become available
|
|
258
|
-
// Check timeout
|
|
259
|
-
if (Date.now() - startTime > timeout) {
|
|
260
|
-
throw new Error(`AI job timed out after ${timeout}ms`);
|
|
261
|
-
}
|
|
262
|
-
// Wait before next poll
|
|
263
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
195
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import i18next, { type i18n } from "i18next";
|
|
2
2
|
import type { KokimokiClient } from "../core";
|
|
3
|
-
import type { PollOptions } from "../types";
|
|
4
3
|
/**
|
|
5
4
|
* Options for creating an i18n instance.
|
|
6
5
|
*/
|
|
@@ -15,39 +14,7 @@ export interface I18nOptions {
|
|
|
15
14
|
/**
|
|
16
15
|
* Status of a single namespace translation.
|
|
17
16
|
*/
|
|
18
|
-
export type
|
|
19
|
-
/**
|
|
20
|
-
* Aggregated status of a language across all namespaces.
|
|
21
|
-
*/
|
|
22
|
-
export type LanguageStatus = "available" | "processing" | "failed" | "partial";
|
|
23
|
-
/**
|
|
24
|
-
* Translation status for a specific language.
|
|
25
|
-
*/
|
|
26
|
-
export interface TranslationStatus {
|
|
27
|
-
/** Overall status of the language */
|
|
28
|
-
status: "available" | "processing" | "not_available";
|
|
29
|
-
/** Status per namespace */
|
|
30
|
-
namespaces: Record<string, NamespaceStatus>;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Status of all languages that have been requested.
|
|
34
|
-
*/
|
|
35
|
-
export interface AllLanguagesStatus {
|
|
36
|
-
/** Array of language statuses */
|
|
37
|
-
languages: {
|
|
38
|
-
lng: string;
|
|
39
|
-
status: LanguageStatus;
|
|
40
|
-
}[];
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Result of requesting a translation.
|
|
44
|
-
*/
|
|
45
|
-
export interface RequestTranslationResult {
|
|
46
|
-
/** Language code that was requested */
|
|
47
|
-
lng: string;
|
|
48
|
-
/** Status of the request */
|
|
49
|
-
status: "started" | "already_processing" | "already_available";
|
|
50
|
-
}
|
|
17
|
+
export type TranslationStatus = "available" | "processing" | "failed";
|
|
51
18
|
/**
|
|
52
19
|
* Kokimoki i18n Service
|
|
53
20
|
*
|
|
@@ -61,7 +28,7 @@ export interface RequestTranslationResult {
|
|
|
61
28
|
* - Pre-configured i18next instance creation
|
|
62
29
|
* - Consistent HTTP-based loading in dev and prod
|
|
63
30
|
* - URL resolution for translation namespaces
|
|
64
|
-
* - AI-powered translation requests
|
|
31
|
+
* - AI-powered translation requests and status polling
|
|
65
32
|
*
|
|
66
33
|
* Access via `kmClient.i18n`
|
|
67
34
|
*
|
|
@@ -75,16 +42,20 @@ export interface RequestTranslationResult {
|
|
|
75
42
|
* });
|
|
76
43
|
*
|
|
77
44
|
* // Initialize with primary language
|
|
78
|
-
* await kmClient.i18n.init(
|
|
45
|
+
* await kmClient.i18n.init();
|
|
79
46
|
*
|
|
80
47
|
* // Request AI translation and wait for it
|
|
81
48
|
* await kmClient.i18n.requestTranslation('de');
|
|
82
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
83
|
-
* onProgress: (status) => console.log('Translation status:', status.status)
|
|
84
|
-
* });
|
|
85
49
|
*
|
|
86
|
-
* //
|
|
87
|
-
*
|
|
50
|
+
* // Poll until ready
|
|
51
|
+
* const checkInterval = setInterval(async () => {
|
|
52
|
+
* const status = await kmClient.i18n.getTranslationStatus('de');
|
|
53
|
+
* if (status.status === 'available') {
|
|
54
|
+
* clearInterval(checkInterval);
|
|
55
|
+
* // Now safe to switch language
|
|
56
|
+
* i18next.changeLanguage('de');
|
|
57
|
+
* }
|
|
58
|
+
* }, 2000);
|
|
88
59
|
* ```
|
|
89
60
|
*/
|
|
90
61
|
export declare class KokimokiI18nService {
|
|
@@ -174,17 +145,25 @@ export declare class KokimokiI18nService {
|
|
|
174
145
|
*/
|
|
175
146
|
getLanguages(): string[];
|
|
176
147
|
/**
|
|
177
|
-
*
|
|
148
|
+
* Request AI translation for a target language.
|
|
149
|
+
*
|
|
150
|
+
* Triggers background AI translation jobs for all namespaces that are not yet available.
|
|
151
|
+
* Uses the build's configured primary language as the source.
|
|
178
152
|
*
|
|
179
|
-
* @
|
|
153
|
+
* @param lng - Target language code (e.g., 'de', 'fr', 'es')
|
|
154
|
+
* @returns Promise with the translation status
|
|
180
155
|
*
|
|
181
156
|
* @example
|
|
182
157
|
* ```typescript
|
|
183
|
-
* const
|
|
184
|
-
*
|
|
158
|
+
* const status = await kmClient.i18n.requestTranslation('de');
|
|
159
|
+
*
|
|
160
|
+
* if (status === 'available') {
|
|
161
|
+
* // Already translated, switch immediately
|
|
162
|
+
* i18next.changeLanguage('de');
|
|
163
|
+
* }
|
|
185
164
|
* ```
|
|
186
165
|
*/
|
|
187
|
-
|
|
166
|
+
requestTranslation(lng: string): Promise<TranslationStatus>;
|
|
188
167
|
/**
|
|
189
168
|
* Get the translation status for a specific language.
|
|
190
169
|
*
|
|
@@ -208,55 +187,4 @@ export declare class KokimokiI18nService {
|
|
|
208
187
|
* ```
|
|
209
188
|
*/
|
|
210
189
|
getTranslationStatus(lng: string): Promise<TranslationStatus>;
|
|
211
|
-
/**
|
|
212
|
-
* Request AI translation for a target language.
|
|
213
|
-
*
|
|
214
|
-
* Triggers background AI translation jobs for all namespaces that are not yet available.
|
|
215
|
-
* Uses the build's configured primary language as the source.
|
|
216
|
-
*
|
|
217
|
-
* @param lng - Target language code (e.g., 'de', 'fr', 'es')
|
|
218
|
-
* @returns Promise with the result of the request
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* ```typescript
|
|
222
|
-
* const result = await kmClient.i18n.requestTranslation('de');
|
|
223
|
-
*
|
|
224
|
-
* if (result.status === 'already_available') {
|
|
225
|
-
* // Already translated, switch immediately
|
|
226
|
-
* i18next.changeLanguage('de');
|
|
227
|
-
* } else {
|
|
228
|
-
* // Poll until ready
|
|
229
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
230
|
-
* onProgress: (status) => console.log('Status:', status.status)
|
|
231
|
-
* });
|
|
232
|
-
* i18next.changeLanguage('de');
|
|
233
|
-
* }
|
|
234
|
-
* ```
|
|
235
|
-
*/
|
|
236
|
-
requestTranslation(lng: string): Promise<RequestTranslationResult>;
|
|
237
|
-
/**
|
|
238
|
-
* Poll a translation request until all namespaces are available.
|
|
239
|
-
*
|
|
240
|
-
* @param lng - The language code to poll for.
|
|
241
|
-
* @param options - Polling options (interval, timeout, progress callback).
|
|
242
|
-
* @returns Promise that resolves when all namespaces are available.
|
|
243
|
-
* @throws If the translation fails or times out.
|
|
244
|
-
*
|
|
245
|
-
* @example
|
|
246
|
-
* ```typescript
|
|
247
|
-
* // Request and poll with progress
|
|
248
|
-
* await kmClient.i18n.requestTranslation('de');
|
|
249
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
250
|
-
* timeout: 60000,
|
|
251
|
-
* onProgress: (status) => {
|
|
252
|
-
* console.log('Overall:', status.status);
|
|
253
|
-
* console.log('Namespaces:', status.namespaces);
|
|
254
|
-
* }
|
|
255
|
-
* });
|
|
256
|
-
*
|
|
257
|
-
* // Now safe to switch
|
|
258
|
-
* i18next.changeLanguage('de');
|
|
259
|
-
* ```
|
|
260
|
-
*/
|
|
261
|
-
pollTranslation(lng: string, options?: PollOptions<TranslationStatus>): Promise<void>;
|
|
262
190
|
}
|
|
@@ -14,7 +14,7 @@ import { getKmEnv } from "../utils/kokimoki-env";
|
|
|
14
14
|
* - Pre-configured i18next instance creation
|
|
15
15
|
* - Consistent HTTP-based loading in dev and prod
|
|
16
16
|
* - URL resolution for translation namespaces
|
|
17
|
-
* - AI-powered translation requests
|
|
17
|
+
* - AI-powered translation requests and status polling
|
|
18
18
|
*
|
|
19
19
|
* Access via `kmClient.i18n`
|
|
20
20
|
*
|
|
@@ -28,16 +28,20 @@ import { getKmEnv } from "../utils/kokimoki-env";
|
|
|
28
28
|
* });
|
|
29
29
|
*
|
|
30
30
|
* // Initialize with primary language
|
|
31
|
-
* await kmClient.i18n.init(
|
|
31
|
+
* await kmClient.i18n.init();
|
|
32
32
|
*
|
|
33
33
|
* // Request AI translation and wait for it
|
|
34
34
|
* await kmClient.i18n.requestTranslation('de');
|
|
35
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
36
|
-
* onProgress: (status) => console.log('Translation status:', status.status)
|
|
37
|
-
* });
|
|
38
35
|
*
|
|
39
|
-
* //
|
|
40
|
-
*
|
|
36
|
+
* // Poll until ready
|
|
37
|
+
* const checkInterval = setInterval(async () => {
|
|
38
|
+
* const status = await kmClient.i18n.getTranslationStatus('de');
|
|
39
|
+
* if (status.status === 'available') {
|
|
40
|
+
* clearInterval(checkInterval);
|
|
41
|
+
* // Now safe to switch language
|
|
42
|
+
* i18next.changeLanguage('de');
|
|
43
|
+
* }
|
|
44
|
+
* }, 2000);
|
|
41
45
|
* ```
|
|
42
46
|
*/
|
|
43
47
|
export class KokimokiI18nService {
|
|
@@ -124,23 +128,31 @@ export class KokimokiI18nService {
|
|
|
124
128
|
const namespaces = env.i18nNamespaces ?? [];
|
|
125
129
|
const fallbackLng = this.options.fallbackLng ?? language;
|
|
126
130
|
const defaultNS = this.options.defaultNS;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
// Wrap in a proper Promise to ensure we wait for backend loading
|
|
132
|
+
this.initPromise = new Promise((resolve, reject) => {
|
|
133
|
+
this.instance.init({
|
|
134
|
+
lng: language,
|
|
135
|
+
fallbackLng,
|
|
136
|
+
ns: namespaces,
|
|
137
|
+
defaultNS,
|
|
138
|
+
backend: {
|
|
139
|
+
// i18next-http-backend passes lng as array, extract first element
|
|
140
|
+
loadPath: (lngs, ns) => {
|
|
141
|
+
const lang = Array.isArray(lngs) ? lngs[0] : lngs;
|
|
142
|
+
return this.getNamespaceUrl(lang, ns);
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
interpolation: {
|
|
146
|
+
escapeValue: false,
|
|
139
147
|
},
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
}, (err) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
reject(err);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
144
156
|
});
|
|
145
157
|
return this.initPromise;
|
|
146
158
|
}
|
|
@@ -200,22 +212,31 @@ export class KokimokiI18nService {
|
|
|
200
212
|
return getKmEnv().i18nLanguages ?? [];
|
|
201
213
|
}
|
|
202
214
|
/**
|
|
203
|
-
*
|
|
215
|
+
* Request AI translation for a target language.
|
|
204
216
|
*
|
|
205
|
-
*
|
|
217
|
+
* Triggers background AI translation jobs for all namespaces that are not yet available.
|
|
218
|
+
* Uses the build's configured primary language as the source.
|
|
219
|
+
*
|
|
220
|
+
* @param lng - Target language code (e.g., 'de', 'fr', 'es')
|
|
221
|
+
* @returns Promise with the translation status
|
|
206
222
|
*
|
|
207
223
|
* @example
|
|
208
224
|
* ```typescript
|
|
209
|
-
* const
|
|
210
|
-
*
|
|
225
|
+
* const status = await kmClient.i18n.requestTranslation('de');
|
|
226
|
+
*
|
|
227
|
+
* if (status === 'available') {
|
|
228
|
+
* // Already translated, switch immediately
|
|
229
|
+
* i18next.changeLanguage('de');
|
|
230
|
+
* }
|
|
211
231
|
* ```
|
|
212
232
|
*/
|
|
213
|
-
async
|
|
214
|
-
const res = await fetch(`${this.client.apiUrl}/i18n`, {
|
|
215
|
-
method: "
|
|
233
|
+
async requestTranslation(lng) {
|
|
234
|
+
const res = await fetch(`${this.client.apiUrl}/i18n/${encodeURIComponent(lng)}/translate`, {
|
|
235
|
+
method: "POST",
|
|
216
236
|
headers: this.client.apiHeaders,
|
|
217
237
|
});
|
|
218
|
-
|
|
238
|
+
const { status } = await res.json();
|
|
239
|
+
return status;
|
|
219
240
|
}
|
|
220
241
|
/**
|
|
221
242
|
* Get the translation status for a specific language.
|
|
@@ -244,89 +265,7 @@ export class KokimokiI18nService {
|
|
|
244
265
|
method: "GET",
|
|
245
266
|
headers: this.client.apiHeaders,
|
|
246
267
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Request AI translation for a target language.
|
|
251
|
-
*
|
|
252
|
-
* Triggers background AI translation jobs for all namespaces that are not yet available.
|
|
253
|
-
* Uses the build's configured primary language as the source.
|
|
254
|
-
*
|
|
255
|
-
* @param lng - Target language code (e.g., 'de', 'fr', 'es')
|
|
256
|
-
* @returns Promise with the result of the request
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* ```typescript
|
|
260
|
-
* const result = await kmClient.i18n.requestTranslation('de');
|
|
261
|
-
*
|
|
262
|
-
* if (result.status === 'already_available') {
|
|
263
|
-
* // Already translated, switch immediately
|
|
264
|
-
* i18next.changeLanguage('de');
|
|
265
|
-
* } else {
|
|
266
|
-
* // Poll until ready
|
|
267
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
268
|
-
* onProgress: (status) => console.log('Status:', status.status)
|
|
269
|
-
* });
|
|
270
|
-
* i18next.changeLanguage('de');
|
|
271
|
-
* }
|
|
272
|
-
* ```
|
|
273
|
-
*/
|
|
274
|
-
async requestTranslation(lng) {
|
|
275
|
-
const res = await fetch(`${this.client.apiUrl}/i18n/${encodeURIComponent(lng)}/translate`, {
|
|
276
|
-
method: "POST",
|
|
277
|
-
headers: this.client.apiHeaders,
|
|
278
|
-
});
|
|
279
|
-
const data = await res.json();
|
|
280
|
-
return { lng, ...data };
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Poll a translation request until all namespaces are available.
|
|
284
|
-
*
|
|
285
|
-
* @param lng - The language code to poll for.
|
|
286
|
-
* @param options - Polling options (interval, timeout, progress callback).
|
|
287
|
-
* @returns Promise that resolves when all namespaces are available.
|
|
288
|
-
* @throws If the translation fails or times out.
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```typescript
|
|
292
|
-
* // Request and poll with progress
|
|
293
|
-
* await kmClient.i18n.requestTranslation('de');
|
|
294
|
-
* await kmClient.i18n.pollTranslation('de', {
|
|
295
|
-
* timeout: 60000,
|
|
296
|
-
* onProgress: (status) => {
|
|
297
|
-
* console.log('Overall:', status.status);
|
|
298
|
-
* console.log('Namespaces:', status.namespaces);
|
|
299
|
-
* }
|
|
300
|
-
* });
|
|
301
|
-
*
|
|
302
|
-
* // Now safe to switch
|
|
303
|
-
* i18next.changeLanguage('de');
|
|
304
|
-
* ```
|
|
305
|
-
*/
|
|
306
|
-
async pollTranslation(lng, options) {
|
|
307
|
-
const pollInterval = Math.max(1000, options?.pollInterval ?? 2000);
|
|
308
|
-
const timeout = options?.timeout ?? 120000;
|
|
309
|
-
const startTime = Date.now();
|
|
310
|
-
while (true) {
|
|
311
|
-
const status = await this.getTranslationStatus(lng);
|
|
312
|
-
// Call progress callback
|
|
313
|
-
options?.onProgress?.(status);
|
|
314
|
-
if (status.status === "available") {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
// Check for failed namespaces
|
|
318
|
-
const failedNamespaces = Object.entries(status.namespaces)
|
|
319
|
-
.filter(([_, ns]) => ns === "failed")
|
|
320
|
-
.map(([name]) => name);
|
|
321
|
-
if (failedNamespaces.length > 0) {
|
|
322
|
-
throw new Error(`Translation failed for namespaces: ${failedNamespaces.join(", ")}`);
|
|
323
|
-
}
|
|
324
|
-
// Check timeout
|
|
325
|
-
if (Date.now() - startTime > timeout) {
|
|
326
|
-
throw new Error(`Translation timed out after ${timeout}ms`);
|
|
327
|
-
}
|
|
328
|
-
// Wait before next poll
|
|
329
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
330
|
-
}
|
|
268
|
+
const { status } = await res.json();
|
|
269
|
+
return status;
|
|
331
270
|
}
|
|
332
271
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const KOKIMOKI_APP_VERSION = "3.1.
|
|
1
|
+
export declare const KOKIMOKI_APP_VERSION = "3.1.3";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated file. Do not edit manually.
|
|
2
|
-
export const KOKIMOKI_APP_VERSION = '3.1.
|
|
2
|
+
export const KOKIMOKI_APP_VERSION = '3.1.3';
|
|
@@ -6,8 +6,7 @@ applyTo: "**/*.ts,**/*.tsx"
|
|
|
6
6
|
# AI Integration
|
|
7
7
|
|
|
8
8
|
Built-in methods for AI text generation and image transformation. No API keys required.
|
|
9
|
-
All generation methods are **async job-based** - they submit a job and return immediately with a `jobId`.
|
|
10
|
-
Use the corresponding poll method to wait for the result.
|
|
9
|
+
All generation methods are **async job-based** - they submit a job and return immediately with a `jobId`. Use `getJob()` to poll for the result.
|
|
11
10
|
|
|
12
11
|
For core SDK concepts, see [Kokimoki SDK](./kokimoki-sdk.instructions.md).
|
|
13
12
|
|
|
@@ -19,7 +18,7 @@ Access AI API via `kmClient.ai`.
|
|
|
19
18
|
- Text generation with configurable creativity (temperature)
|
|
20
19
|
- Structured JSON output with Zod schema validation
|
|
21
20
|
- AI-powered image generation and modification
|
|
22
|
-
- Job-based async processing
|
|
21
|
+
- Job-based async processing
|
|
23
22
|
- Resumable after page reload (persist jobId)
|
|
24
23
|
|
|
25
24
|
## Available Models
|
|
@@ -45,7 +44,7 @@ Access AI API via `kmClient.ai`.
|
|
|
45
44
|
|
|
46
45
|
### ai.generateText(req): Promise<{ jobId: string }>
|
|
47
46
|
|
|
48
|
-
Submits a text generation job. Use `
|
|
47
|
+
Submits a text generation job. Use `getJob()` to poll the result.
|
|
49
48
|
|
|
50
49
|
**Parameters:**
|
|
51
50
|
|
|
@@ -67,12 +66,13 @@ const { jobId } = await kmClient.ai.generateText({
|
|
|
67
66
|
});
|
|
68
67
|
|
|
69
68
|
// Poll for result
|
|
70
|
-
const
|
|
69
|
+
const job = await kmClient.ai.getJob<string>(jobId);
|
|
70
|
+
console.log("Generated quest:", job.result);
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
### ai.generateJson<T>(req): Promise<{ jobId: string }>
|
|
74
74
|
|
|
75
|
-
Submits a JSON generation job with Zod schema validation. Use `
|
|
75
|
+
Submits a JSON generation job with Zod schema validation. Use `getJob<T>()` to wait for the result with type inference.
|
|
76
76
|
|
|
77
77
|
**Parameters:**
|
|
78
78
|
|
|
@@ -107,8 +107,8 @@ const { jobId } = await kmClient.ai.generateJson({
|
|
|
107
107
|
await saveJobId(jobId);
|
|
108
108
|
|
|
109
109
|
// Poll with schema for type inference and validation
|
|
110
|
-
const enemy = await kmClient.ai.
|
|
111
|
-
console.log(enemy.name, enemy.health); // Fully typed!
|
|
110
|
+
const enemy = await kmClient.ai.getJob<Enemy>(jobId);
|
|
111
|
+
console.log(enemy.result?.name, enemy.result?.health); // Fully typed!
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
```typescript
|
|
@@ -127,13 +127,13 @@ const { jobId } = await kmClient.ai.generateJson({
|
|
|
127
127
|
prompt: "Create 5 history quiz questions with 4 options each",
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
-
const questions = await kmClient.ai.
|
|
131
|
-
questions.forEach((q) => console.log(q.question));
|
|
130
|
+
const questions = await kmClient.ai.getJob<typeof questionSchema>(jobId);
|
|
131
|
+
questions.result?.forEach((q) => console.log(q.question));
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
### ai.generateImage(req): Promise<{ jobId: string }>
|
|
135
135
|
|
|
136
|
-
Submits an image generation/modification job. Use `
|
|
136
|
+
Submits an image generation/modification job. Use `getJob()` to wait for the result.
|
|
137
137
|
The result is stored as `Upload` object (see [Storage](./kokimoki-storage.instructions.md)).
|
|
138
138
|
|
|
139
139
|
**Parameters:**
|
|
@@ -152,8 +152,8 @@ const { jobId } = await kmClient.ai.generateImage({
|
|
|
152
152
|
tags: ["background", "ai-generated"],
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
const
|
|
156
|
-
console.log(
|
|
155
|
+
const job = await kmClient.ai.getJob<string>(jobId);
|
|
156
|
+
console.log("Generated image:", job.result); // CDN URL to the generated image
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
```typescript
|
|
@@ -164,12 +164,13 @@ const { jobId } = await kmClient.ai.generateImage({
|
|
|
164
164
|
tags: ["art", "ai-generated"],
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
const
|
|
167
|
+
const job = await kmClient.ai.getJob<string>(jobId);
|
|
168
|
+
console.log("Generated image:", job.result); // CDN URL to the generated image
|
|
168
169
|
```
|
|
169
170
|
|
|
170
171
|
### ai.getJob(jobId): Promise<AiJob>
|
|
171
172
|
|
|
172
|
-
Get the current status of an AI job
|
|
173
|
+
Get the current status of an AI job.
|
|
173
174
|
|
|
174
175
|
**Parameters:**
|
|
175
176
|
|
|
@@ -199,68 +200,6 @@ if (job.status === "completed") {
|
|
|
199
200
|
}
|
|
200
201
|
```
|
|
201
202
|
|
|
202
|
-
### ai.pollText(jobId, options?): Promise<string>
|
|
203
|
-
|
|
204
|
-
Poll a text generation job until completion.
|
|
205
|
-
|
|
206
|
-
**Parameters:**
|
|
207
|
-
|
|
208
|
-
- **jobId**: `string` The job ID to poll
|
|
209
|
-
- **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
|
|
210
|
-
- **options.timeout**: `number` Timeout in ms (default: 120000)
|
|
211
|
-
- **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
|
|
212
|
-
|
|
213
|
-
**Example:**
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
const text = await kmClient.ai.pollText(jobId, {
|
|
217
|
-
timeout: 60000,
|
|
218
|
-
onProgress: (status) => console.log("Status:", status),
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### ai.pollJson<T>(jobId, options): Promise<T>
|
|
223
|
-
|
|
224
|
-
Poll a JSON generation job until completion with schema validation.
|
|
225
|
-
|
|
226
|
-
**Parameters:**
|
|
227
|
-
|
|
228
|
-
- **jobId**: `string` The job ID to poll
|
|
229
|
-
- **options.schema**: `ZodType` Zod schema for validation and type inference (required)
|
|
230
|
-
- **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
|
|
231
|
-
- **options.timeout**: `number` Timeout in ms (default: 120000)
|
|
232
|
-
- **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
|
|
233
|
-
|
|
234
|
-
**Example:**
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
const enemy = await kmClient.ai.pollJson(jobId, {
|
|
238
|
-
schema: enemySchema,
|
|
239
|
-
timeout: 60000,
|
|
240
|
-
onProgress: (status) => console.log("Status:", status),
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### ai.pollImage(jobId, options?): Promise<Upload>
|
|
245
|
-
|
|
246
|
-
Poll an image generation job until completion.
|
|
247
|
-
|
|
248
|
-
**Parameters:**
|
|
249
|
-
|
|
250
|
-
- **jobId**: `string` The job ID to poll
|
|
251
|
-
- **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
|
|
252
|
-
- **options.timeout**: `number` Timeout in ms (default: 120000)
|
|
253
|
-
- **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
|
|
254
|
-
|
|
255
|
-
**Example:**
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
const upload = await kmClient.ai.pollImage(jobId, {
|
|
259
|
-
timeout: 60000,
|
|
260
|
-
onProgress: (status) => console.log("Status:", status),
|
|
261
|
-
});
|
|
262
|
-
```
|
|
263
|
-
|
|
264
203
|
## Common Patterns
|
|
265
204
|
|
|
266
205
|
### Example: Persist Jobs Across Page Reloads
|
|
@@ -297,20 +236,24 @@ const { jobId } = await kmClient.ai.generateText({
|
|
|
297
236
|
prompt: "Write a story",
|
|
298
237
|
});
|
|
299
238
|
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
239
|
+
const checkInterval = setInterval(async () => {
|
|
240
|
+
const job = await kmClient.ai.getJob<string>(jobId);
|
|
241
|
+
if (job.status === "completed") {
|
|
242
|
+
console.log("Generated text:", job.result);
|
|
243
|
+
clearInterval(checkInterval);
|
|
244
|
+
setLoading(false);
|
|
245
|
+
} else if (job.status === "failed") {
|
|
246
|
+
console.error("AI generation failed:", job.error);
|
|
247
|
+
clearInterval(checkInterval);
|
|
248
|
+
setLoading(false);
|
|
249
|
+
}
|
|
250
|
+
}, 2000);
|
|
307
251
|
```
|
|
308
252
|
|
|
309
253
|
## Key Points
|
|
310
254
|
|
|
311
|
-
- **Job-based**: All generation methods return `{ jobId }` immediately, use poll
|
|
255
|
+
- **Job-based**: All generation methods return `{ jobId }` immediately, use `getJob()` to poll for results
|
|
312
256
|
- **Resumable**: Save `jobId` to store to resume polling after page reload
|
|
313
257
|
- **Zod Schemas**: Use Zod schemas for `generateJson` to get automatic type inference and validation
|
|
314
258
|
- **Temperature**: Range 0.0 (factual) to 1.0 (creative)
|
|
315
|
-
- **Polling Options**: Configure `pollInterval`, `timeout`, and `onProgress` callback
|
|
316
259
|
- **Image URLs**: Gemini models support multimodal input via `imageUrls` parameter
|
|
@@ -110,36 +110,12 @@ const url = kmClient.i18n.getNamespaceUrl("en", "game");
|
|
|
110
110
|
|
|
111
111
|
## AI Translation API
|
|
112
112
|
|
|
113
|
-
### i18n.
|
|
114
|
-
|
|
115
|
-
Get the status of all languages that have been requested for AI translation.
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
interface AllLanguagesStatus {
|
|
119
|
-
languages: { lng: string; status: LanguageStatus }[];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
type LanguageStatus = "available" | "processing" | "failed" | "partial";
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Example:**
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
const { languages } = await kmClient.i18n.getAllLanguagesStatus();
|
|
129
|
-
// [{ lng: 'de', status: 'available' }, { lng: 'fr', status: 'processing' }]
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### i18n.getTranslationStatus(lng): Promise<TranslationStatus>
|
|
113
|
+
### i18n.getTranslationStatus(lng): Promise<{ status: TranslationStatus }>
|
|
133
114
|
|
|
134
115
|
Get the translation status for a specific language.
|
|
135
116
|
|
|
136
117
|
```typescript
|
|
137
|
-
|
|
138
|
-
status: "available" | "processing" | "not_available";
|
|
139
|
-
namespaces: Record<string, NamespaceStatus>;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
type NamespaceStatus = "available" | "processing" | "failed" | "not_available";
|
|
118
|
+
type TranslationStatus = "available" | "processing" | "failed";
|
|
143
119
|
```
|
|
144
120
|
|
|
145
121
|
**Example:**
|
|
@@ -147,117 +123,34 @@ type NamespaceStatus = "available" | "processing" | "failed" | "not_available";
|
|
|
147
123
|
```typescript
|
|
148
124
|
const status = await kmClient.i18n.getTranslationStatus("de");
|
|
149
125
|
|
|
150
|
-
if (status
|
|
126
|
+
if (status === "available") {
|
|
151
127
|
i18next.changeLanguage("de");
|
|
152
|
-
} else if (status.status === "processing") {
|
|
153
|
-
// Show loading indicator
|
|
154
|
-
} else {
|
|
155
|
-
// Request translation
|
|
156
|
-
await kmClient.i18n.requestTranslation("de");
|
|
157
128
|
}
|
|
158
129
|
```
|
|
159
130
|
|
|
160
|
-
### i18n.requestTranslation(lng): Promise<
|
|
131
|
+
### i18n.requestTranslation(lng): Promise<TranslationStatus>
|
|
161
132
|
|
|
162
133
|
Request AI translation for a target language. Triggers background AI translation jobs for all namespaces.
|
|
163
134
|
|
|
164
135
|
```typescript
|
|
165
|
-
|
|
166
|
-
lng: string;
|
|
167
|
-
status: "started" | "already_processing" | "already_available";
|
|
168
|
-
}
|
|
136
|
+
type TranslationStatus = "available" | "processing" | "failed";
|
|
169
137
|
```
|
|
170
138
|
|
|
171
139
|
**Example:**
|
|
172
140
|
|
|
173
141
|
```typescript
|
|
174
|
-
const
|
|
142
|
+
const status = await kmClient.i18n.requestTranslation("de");
|
|
175
143
|
|
|
176
|
-
if (
|
|
144
|
+
if (status === "already_available") {
|
|
177
145
|
// Already translated, switch immediately
|
|
178
146
|
i18next.changeLanguage("de");
|
|
179
147
|
} else {
|
|
180
|
-
// Poll until ready
|
|
181
|
-
await kmClient.i18n.pollTranslation("de");
|
|
182
|
-
i18next.changeLanguage("de");
|
|
148
|
+
// Poll until ready using getTranslationStatus
|
|
183
149
|
}
|
|
184
150
|
```
|
|
185
151
|
|
|
186
|
-
### i18n.pollTranslation(lng, options?): Promise<void>
|
|
187
|
-
|
|
188
|
-
Poll a translation request until all namespaces are available.
|
|
189
|
-
|
|
190
|
-
**Parameters:**
|
|
191
|
-
|
|
192
|
-
- **lng**: `string` Language code to poll for
|
|
193
|
-
- **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
|
|
194
|
-
- **options.timeout**: `number` Timeout in ms (default: 120000)
|
|
195
|
-
- **options.onProgress**: `(status: TranslationStatus) => void` Progress callback
|
|
196
|
-
|
|
197
|
-
**Example:**
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
await kmClient.i18n.pollTranslation("de", {
|
|
201
|
-
timeout: 60000,
|
|
202
|
-
onProgress: (status) => {
|
|
203
|
-
console.log("Overall:", status.status);
|
|
204
|
-
console.log("Namespaces:", status.namespaces);
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Now safe to switch
|
|
209
|
-
i18next.changeLanguage("de");
|
|
210
|
-
```
|
|
211
|
-
|
|
212
152
|
## Common Patterns
|
|
213
153
|
|
|
214
|
-
### Example: Language Switcher with AI Translation
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
import { useState } from "react";
|
|
218
|
-
import { useTranslation } from "react-i18next";
|
|
219
|
-
import { getKmClient } from "@kokimoki/app";
|
|
220
|
-
|
|
221
|
-
const kmClient = getKmClient();
|
|
222
|
-
|
|
223
|
-
const LanguageSwitcher = () => {
|
|
224
|
-
const { i18n } = useTranslation();
|
|
225
|
-
const [loading, setLoading] = useState(false);
|
|
226
|
-
|
|
227
|
-
const switchLanguage = async (lng: string) => {
|
|
228
|
-
// Check if translation is available
|
|
229
|
-
const status = await kmClient.i18n.getTranslationStatus(lng);
|
|
230
|
-
|
|
231
|
-
if (status.status === "available") {
|
|
232
|
-
i18n.changeLanguage(lng);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Request and wait for AI translation
|
|
237
|
-
setLoading(true);
|
|
238
|
-
await kmClient.i18n.requestTranslation(lng);
|
|
239
|
-
await kmClient.i18n.pollTranslation(lng, {
|
|
240
|
-
onProgress: (s) => console.log(`Translating: ${s.status}`),
|
|
241
|
-
});
|
|
242
|
-
setLoading(false);
|
|
243
|
-
|
|
244
|
-
i18n.changeLanguage(lng);
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<select
|
|
249
|
-
value={i18n.language}
|
|
250
|
-
onChange={(e) => switchLanguage(e.target.value)}
|
|
251
|
-
disabled={loading}
|
|
252
|
-
>
|
|
253
|
-
<option value="en">English</option>
|
|
254
|
-
<option value="de">Deutsch</option>
|
|
255
|
-
<option value="fr">Français</option>
|
|
256
|
-
</select>
|
|
257
|
-
);
|
|
258
|
-
};
|
|
259
|
-
```
|
|
260
|
-
|
|
261
154
|
### Example: Using Translations in Components
|
|
262
155
|
|
|
263
156
|
```tsx
|