Skip to content

Commit d6e6d5c

Browse files
committed
feat(cli): mutlisource
1 parent 123fd80 commit d6e6d5c

4 files changed

Lines changed: 60 additions & 24 deletions

File tree

.changeset/rich-singers-act.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@replexica/spec": minor
3+
"@replexica/cli": minor
4+
"@replexica/sdk": minor
5+
"replexica": minor
6+
---
7+
8+
Add support for multisource localization to the CLI

packages/cli/src/cli/i18n.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import { createLockfileProcessor } from './../workers/lockfile';
99
import { createAuthenticator } from './../workers/auth';
1010
import { ReplexicaEngine } from '@replexica/sdk';
1111
import { expandPlaceholderedGlob, createBucketLoader } from '../workers/bucket/v2';
12-
// import fs from 'fs';
13-
// import path from 'path';
1412

1513
export default new Command()
1614
.command('i18n')
@@ -110,15 +108,13 @@ export default new Command()
110108
for (const [bucketType, placeholderedPath] of placeholderedPathsTuples) {
111109
console.log('');
112110
const bucketOra = Ora({});
113-
bucketOra.info(`Processing ${placeholderedPath}`);
114-
// Create the payload processor instance for the current bucket type
115-
const sourceBucketFileLoader = createBucketLoader({
111+
bucketOra.info(`Processing ${placeholderedPath}`);
112+
// Load the source locale payload
113+
const sourcePayload = await createBucketLoader({
116114
bucketType,
117115
placeholderedPath,
118116
locale: i18nConfig.locale.source,
119-
});
120-
// Load the source locale payload
121-
const sourcePayload = await sourceBucketFileLoader.load();
117+
}).load();
122118
// Load saved checksums from the lockfile
123119
const savedChecksums = await lockfileProcessor.loadChecksums(placeholderedPath);
124120
// Calculate current checksums for the source payload
@@ -165,12 +161,27 @@ export default new Command()
165161
try {
166162
// Use the SDK to localize the payload
167163
localeOra.start('AI translation in progress...');
164+
// Calculate reference payload if specified
165+
const referencePayload: any = {};
166+
if (i18nConfig.locale.extraSource) {
167+
let extraSourcePayload = await createBucketLoader({
168+
bucketType,
169+
placeholderedPath,
170+
locale: i18nConfig.locale.extraSource,
171+
}).load();
172+
// leave only the keys that are present in the processable payload
173+
extraSourcePayload = _.pick(extraSourcePayload, Object.keys(processablePayload));
174+
// assign
175+
referencePayload[i18nConfig.locale.extraSource] = extraSourcePayload;
176+
}
177+
168178
processedPayload = await replexicaEngine.localize(
169179
processablePayload,
170180
{
171181
sourceLocale: i18nConfig.locale.source,
172182
targetLocale: targetLocale,
173183
},
184+
referencePayload,
174185
(progress) => {
175186
localeOra.text = `(${progress}%) AI translation in progress...`;
176187
}

packages/sdk/src/index.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Z from 'zod';
2-
import { sourceLocaleSchema, targetLocaleSchema } from '@replexica/spec';
2+
import { sourceLocaleSchema, targetLocaleSchema, allLocalesSchema } from '@replexica/spec';
33
import { createId } from "@paralleldrive/cuid2";
44

5-
const replexicaEngineParamsSchema = Z.object({
5+
const engineParamsSchema = Z.object({
66
apiKey: Z.string(),
77
apiUrl: Z.string().url().default('https://engine.replexica.com'),
88
batchSize: Z.number()
@@ -18,30 +18,35 @@ const replexicaEngineParamsSchema = Z.object({
1818
.default(200),
1919
}).passthrough();
2020

21-
const replexicaLocalizationPayloadSchema = Z.record(
21+
const payloadSchema = Z.record(
2222
Z.string(),
2323
Z.any(),
2424
);
2525

26-
const replexicaLocalizationParamsSchema = Z.object({
26+
const localizationParamsSchema = Z.object({
2727
sourceLocale: sourceLocaleSchema,
2828
targetLocale: targetLocaleSchema,
2929
});
3030

31+
const referenceSchema = Z.record(
32+
allLocalesSchema,
33+
payloadSchema,
34+
);
35+
3136
/**
3237
* ReplexicaEngine class for interacting with the Replexica API
3338
*/
3439
export class ReplexicaEngine {
35-
private config: Z.infer<typeof replexicaEngineParamsSchema>;
40+
private config: Z.infer<typeof engineParamsSchema>;
3641

3742
/**
3843
* Create a new ReplexicaEngine instance
3944
* @param config - Configuration options for the Engine
4045
*/
4146
constructor(
42-
config: Partial<Z.infer<typeof replexicaEngineParamsSchema>>,
47+
config: Partial<Z.infer<typeof engineParamsSchema>>,
4348
) {
44-
this.config = replexicaEngineParamsSchema.parse(config);
49+
this.config = engineParamsSchema.parse(config);
4550
}
4651

4752
/**
@@ -52,12 +57,13 @@ export class ReplexicaEngine {
5257
* @returns Localized content
5358
*/
5459
async localize(
55-
payload: Z.infer<typeof replexicaLocalizationPayloadSchema>,
56-
params: Z.infer<typeof replexicaLocalizationParamsSchema>,
60+
payload: Z.infer<typeof payloadSchema>,
61+
params: Z.infer<typeof localizationParamsSchema>,
62+
reference?: Z.infer<typeof referenceSchema>,
5763
progressCallback?: (progress: number) => void
5864
): Promise<Record<string, string>> {
59-
const finalPayload = replexicaLocalizationPayloadSchema.parse(payload);
60-
const finalParams = replexicaLocalizationParamsSchema.parse(params);
65+
const finalPayload = payloadSchema.parse(payload);
66+
const finalParams = localizationParamsSchema.parse(params);
6167

6268
const chunkedPayload = this.extractPayloadChunks(finalPayload);
6369
const processedPayloadChunks: Record<string, string>[] = [];
@@ -74,7 +80,7 @@ export class ReplexicaEngine {
7480
const processedPayloadChunk = await this.localizeChunk(
7581
finalParams.sourceLocale,
7682
finalParams.targetLocale,
77-
{ meta: {}, data: chunk },
83+
{ data: chunk, reference },
7884
workflowId,
7985
);
8086
processedPayloadChunks.push(processedPayloadChunk);
@@ -93,7 +99,7 @@ export class ReplexicaEngine {
9399
private async localizeChunk(
94100
sourceLocale: string,
95101
targetLocale: string,
96-
payload: { data: any; meta: any; },
102+
payload: { data: any; reference: any; },
97103
workflowId: string,
98104
): Promise<Record<string, string>> {
99105
const res = await fetch(`${this.config.apiUrl}/i18n`, {
@@ -108,8 +114,8 @@ export class ReplexicaEngine {
108114
source: sourceLocale,
109115
target: targetLocale,
110116
},
111-
meta: payload.meta,
112117
data: payload.data,
118+
reference: payload.reference,
113119
}, null, 2),
114120
});
115121

packages/spec/src/config.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Z from 'zod';
2-
import { sourceLocaleSchema, targetLocaleSchema } from './locales';
2+
import { allLocalesSchema, sourceLocaleSchema, targetLocaleSchema } from './locales';
33
import { bucketTypeSchema } from './formats';
44

55
// common
@@ -134,9 +134,20 @@ export const configV1_1Definition = extendConfigDefinition(configV1Definition, {
134134
},
135135
});
136136

137+
// v1.1 -> v1.2
138+
// Changes: Add "extraSource" optional field to the locale node of the config
139+
export const configV1_2Definition = extendConfigDefinition(configV1_1Definition, {
140+
createSchema: (baseSchema) => baseSchema.extend({
141+
locale: localeSchema.extend({
142+
extraSource: allLocalesSchema.optional(),
143+
}),
144+
}),
145+
createDefaultValue: (baseDefaultValue) => baseDefaultValue,
146+
createUpgrader: (oldConfig) => oldConfig,
147+
});
137148

138149
// exports
139-
const LATEST_CONFIG_DEFINITION = configV1_1Definition;
150+
const LATEST_CONFIG_DEFINITION = configV1_2Definition;
140151

141152
export type I18nConfig = Z.infer<typeof LATEST_CONFIG_DEFINITION['schema']>;
142153

0 commit comments

Comments
 (0)