Skip to content

Commit

Permalink
Feature/cli diff (#64)
Browse files Browse the repository at this point in the history
* wip

* wip

* JSON parser

* YAML parser

* XML parser

* xCode parser

* Parsers

* wip

* wip

* wip

* wip

* sync

* commands

* wip

* wip
  • Loading branch information
pontusab authored Jan 19, 2025
1 parent f4ab95c commit ba3cd23
Show file tree
Hide file tree
Showing 87 changed files with 8,369 additions and 1,843 deletions.
19 changes: 18 additions & 1 deletion apps/web/languine.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,24 @@ import { defineConfig } from "languine";
export default defineConfig({
locale: {
source: "en",
targets: ["es"],
targets: [
"es",
"sv",
"de",
"fr",
"fi",
"pt",
"ja",
"zh",
"ko",
"no",
"it",
"ar",
"nl",
"pl",
"tr",
"vi",
],
},
files: {
ts: {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"@radix-ui/react-tooltip": "^1.1.6",
"@react-email/components": "0.0.32",
"@react-email/font": "^0.0.9",
"@tanstack/react-query": "^5.64.1",
"@trigger.dev/sdk": "^3.3.11",
"@tanstack/react-query": "^5.64.2",
"@trigger.dev/sdk": "3.3.11",
"@trpc/client": "11.0.0-rc.700",
"@trpc/react-query": "11.0.0-rc.700",
"@trpc/server": "11.0.0-rc.700",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ export default async function Page({

// If there are no translations, show the onboarding
if (!translations.length) {
return <OnboardingSteps />;
const data = await trpc.project.getBySlug({
slug: project,
organizationId: organization,
});

return <OnboardingSteps projectId={data?.id} />;
}

return (
Expand Down
10 changes: 4 additions & 6 deletions apps/web/src/components/onboarding-steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Loader } from "@/components/ui/loader";
import { cn } from "@/lib/utils";
import { useI18n } from "@/locales/client";
import { useParams } from "next/navigation";
import { parseAsInteger, useQueryState } from "nuqs";

export function OnboardingSteps() {
const { project } = useParams();
export function OnboardingSteps({ projectId }: { projectId: string }) {
const t = useI18n();
const [step, setStep] = useQueryState("step", parseAsInteger.withDefault(1));

Expand All @@ -35,9 +33,9 @@ export function OnboardingSteps() {
{t("onboarding.steps.1.description")}
</p>
<CopyInput
value={`npx languine@latest init --p=${project}`}
value={`npx languine@latest init --p=${projectId}`}
onCopy={() => setStep(2)}
className="border-dashed"
className="border-dashed !text-xs"
/>
</CardContent>
</Card>
Expand Down Expand Up @@ -72,7 +70,7 @@ export function OnboardingSteps() {
</div>
<CopyInput
value="npx languine@latest translate"
className="border-dashed"
className="border-dashed !text-xs"
/>
</CardContent>
</Card>
Expand Down
73 changes: 47 additions & 26 deletions apps/web/src/jobs/translate/translate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { validateJobPermissions } from "@/db/queries/permissions";
import { logger, metadata, schemaTask, wait } from "@trigger.dev/sdk/v3";
import { metadata, schemaTask } from "@trigger.dev/sdk/v3";
import { z } from "zod";
import { translate } from "../utils/translate";

const translationSchema = z.object({
projectId: z.string(),
Expand All @@ -21,42 +22,62 @@ export const translateTask = schemaTask({
schema: translationSchema,
maxDuration: 300,
queue: {
concurrencyLimit: 1,
concurrencyLimit: 10,
},
run: async (payload, { ctx }) => {
// Use the permission utility
// await validateJobPermissions({
// apiKey: payload.apiKey,
// projectId: payload.projectId,
// });

// Simulate translation progress with smaller steps
const totalSteps =
payload.targetLanguages.length * payload.content.length * 10; // 10 mini-steps per translation
let completedSteps = 0;
// Validate permissions
// await validateJobPermissions(payload.projectId, payload.apiKey);

const translations: Record<
string,
Array<{ key: string; translatedText: string }>
> = {};

for (const lang of payload.targetLanguages) {
translations[lang] = [];
const totalTranslations =
payload.targetLanguages.length * payload.content.length;
let completedTranslations = 0;

for (const item of payload.content) {
// Split the 1 second wait into 10 smaller steps
for (let i = 0; i < 10; i++) {
await wait.for({ seconds: 0.1 });
completedSteps++;
metadata.set("progress", completedSteps / totalSteps);
}
metadata.set("progress", 0);

// Run translations in parallel for each target language
await Promise.all(
payload.targetLanguages.map(async (targetLocale) => {
translations[targetLocale] = [];

translations[lang].push({
key: item.key,
translatedText: `Translated: ${item.sourceText}`,
// Update progress before starting each language
metadata.set(
"progress",
Math.round((completedTranslations * 100) / totalTranslations),
);

const translatedContent = await translate(payload.content, {
sourceLocale: payload.sourceLanguage,
targetLocale,
});
}
}

for (let i = 0; i < payload.content.length; i++) {
translations[targetLocale].push({
key: payload.content[i].key,
translatedText: translatedContent[i],
});

completedTranslations++;

// Update progress after each individual translation
const progress = Math.round(
(completedTranslations * 100) / totalTranslations,
);
metadata.set("progress", progress);

// Add micro-progress updates between translations
if (i < payload.content.length - 1) {
metadata.set("progress", progress + 0.5);
}
}
}),
);

metadata.set("progress", 100);

return {
translations,
Expand Down
66 changes: 66 additions & 0 deletions apps/web/src/jobs/utils/prompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { projectSettings } from "@/db/schema";
import type { PromptOptions } from "./types";

const baseRequirements = `
Translation Requirements:
- Maintain exact file structure, indentation, and formatting
- Provide natural, culturally-adapted translations that sound native
- Keep all technical identifiers unchanged
- Keep consistent capitalization, spacing, and line breaks
- Respect existing whitespace and newline patterns
`;

export function createFinalPrompt(
text: string,
options: PromptOptions,
settings?: Partial<typeof projectSettings.$inferSelect>,
) {
const basePrompt = `You are a professional translator working with JSON files.
Task: Translate the content below from ${options.sourceLocale} to ${options.targetLocale}.
${baseRequirements}`;

const tuningInstructions = settings
? [
// Style and tone settings
settings.formality && `- Use ${settings.formality} language style`,
settings.toneOfVoice &&
`- Maintain a ${settings.toneOfVoice} tone of voice`,
settings.emotiveIntent &&
`- Convey a ${settings.emotiveIntent} emotional tone`,

// Brand-specific settings
settings.brandName &&
`- Use "${settings.brandName}" consistently for brand references`,
settings.brandVoice &&
`- Follow brand voice guidelines: ${settings.brandVoice}`,

// Technical settings
settings.lengthControl &&
`- Apply ${settings.lengthControl} length control`,
settings.domainExpertise &&
`- Use terminology appropriate for ${settings.domainExpertise} domain`,
settings.terminology &&
`- Follow specific terminology: ${settings.terminology}`,

// Feature flags
settings.translationMemory &&
"- Maintain consistency with previous translations",
settings.qualityChecks &&
"- Ensure high-quality output with proper grammar and spelling",
settings.contextDetection &&
"- Consider surrounding context for accurate translations",
settings.inclusiveLanguage &&
"- Use inclusive and non-discriminatory language",
settings.idioms && "- Adapt idioms appropriately for target culture",
]
.filter(Boolean)
.join("\n")
: "";

return `${basePrompt}${
tuningInstructions
? `\nAdditional Requirements:\n${tuningInstructions}`
: ""
}\n\n${text}`;
}
38 changes: 38 additions & 0 deletions apps/web/src/jobs/utils/translate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { openai } from "@ai-sdk/openai";
import { generateObject } from "ai";
import { z } from "zod";
import { createFinalPrompt } from "./prompt";
import type { PromptOptions } from "./types";

function getPrompt(
content: Array<{ key: string; sourceText: string }>,
options: PromptOptions,
) {
const codeblocks = content
.map(({ key, sourceText }) => {
return `\`\`\`json
${sourceText}
\`\`\``;
})
.join("\n\n");

return createFinalPrompt(codeblocks, options);
}

export async function translate(
content: Array<{ key: string; sourceText: string }>,
options: PromptOptions,
) {
const prompt = getPrompt(content, options);

const { object } = await generateObject({
model: openai("gpt-4o-mini"),
prompt,
mode: "json",
schema: z.object({
content: z.array(z.string()),
}),
});

return object.content;
}
7 changes: 7 additions & 0 deletions apps/web/src/jobs/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { projectSettings } from "@/db/schema";

export type PromptOptions = {
sourceLocale: string;
targetLocale: string;
settings?: Partial<typeof projectSettings.$inferSelect>;
};
25 changes: 0 additions & 25 deletions apps/web/src/lib/translators/index.ts

This file was deleted.

38 changes: 0 additions & 38 deletions apps/web/src/lib/translators/js.ts

This file was deleted.

37 changes: 0 additions & 37 deletions apps/web/src/lib/translators/md.ts

This file was deleted.

Loading

0 comments on commit ba3cd23

Please sign in to comment.