Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

タイトルも翻訳できるように #168

Merged
merged 1 commit into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { JSDOM } from "jsdom";

export function extractNumberedElements(
content: string,
title: string,
): Array<{ number: number; text: string }> {
const doc = new JSDOM(content);
const numberedElements: Array<{ number: number; text: string }> = [];
const numberedElements: Array<{ number: number; text: string }> = [
{ number: 0, text: title },
];
// <br>のみを改行とする
doc.window.document.body.innerHTML = doc.window.document.body.innerHTML
.replace(/\n/g, "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import type { SourceTextWithTranslations } from "../types";
import { Translation } from "./Translation";

interface ContentWithTranslationsProps {
title: string;
content: string;
sourceTextWithTranslations: SourceTextWithTranslations[];
userId: number | null;
}

export const ContentWithTranslations = memo(function ContentWithTranslations({
title,
content,
sourceTextWithTranslations,
userId,
}: ContentWithTranslationsProps) {
const titleTranslation = useMemo(() => {
return sourceTextWithTranslations.find((info) => info.number === 0);
}, [sourceTextWithTranslations]);

const parsedContent = useMemo(() => {
if (typeof window === "undefined") {
return null;
Expand Down Expand Up @@ -63,5 +69,19 @@ export const ContentWithTranslations = memo(function ContentWithTranslations({
return <div>Loading...</div>;
}

return <>{parsedContent}</>;
return (
<>
<h1>
{title}
{titleTranslation && (
<Translation
translationsWithVotes={titleTranslation.translationsWithVotes}
userId={userId}
sourceTextId={titleTranslation.sourceTextId}
/>
)}
</h1>
{parsedContent}
</>
);
});
16 changes: 12 additions & 4 deletions web/app/routes/$userName+/page+/$slug+/components/Translation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,22 @@ export function Translation({

const alternativeTranslationsWithVotes = useMemo(
() =>
translationsWithVotes.filter((t) => t.id !== bestTranslationWithVote.id),
bestTranslationWithVote
? translationsWithVotes.filter(
(t) => t.id !== bestTranslationWithVote.id,
)
: [],
[translationsWithVotes, bestTranslationWithVote],
);

const sanitizedAndParsedText = useMemo(() => {
const sanitized = sanitizeAndParseText(bestTranslationWithVote.text);
return sanitized;
}, [bestTranslationWithVote.text]);
if (!bestTranslationWithVote) return null;
return sanitizeAndParseText(bestTranslationWithVote.text);
}, [bestTranslationWithVote]);

if (!bestTranslationWithVote || !sanitizedAndParsedText) {
return null;
}

return (
<div className="group relative">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function UserAITranslationStatus({
return (
<Card className="mt-6">
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>Translation Status</CardTitle>
<CardTitle>Your AI Translation Status</CardTitle>
<Button
variant="ghost"
size="sm"
Expand Down
7 changes: 3 additions & 4 deletions web/app/routes/$userName+/page+/$slug+/edit/_edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import { ArrowDownToLine } from "lucide-react";
import { z } from "zod";
import { Button } from "~/components/ui/button";
import { authenticator } from "~/utils/auth.server";
import { addNumbersToContent } from "../utils/addNumbersToContent";
import { extractNumberedElements } from "../utils/extractNumberedElements";
import { Header } from "./components/Header";
import { createOrUpdateSourceTexts } from "./functions/mutations.server";
import { getOrCreatePage } from "./functions/mutations.server";
import { getPageBySlug } from "./functions/queries.server";
import { addNumbersToContent } from "./utils/addNumbersToContent";
import { extractNumberedElements } from "./utils/extractNumberedElements";

const schema = z.object({
title: z.string().min(1, "Required"),
Expand Down Expand Up @@ -67,9 +67,8 @@ export const action: ActionFunction = async ({ request, params }) => {

const { title, pageContent } = submission.value;
const numberedContent = addNumbersToContent(pageContent);
console.log(numberedContent);
const page = await getOrCreatePage(safeUser.id, slug, title, numberedContent);
const numberedElements = extractNumberedElements(numberedContent);
const numberedElements = extractNumberedElements(numberedContent, title);
await createOrUpdateSourceTexts(numberedElements, page.id);

return redirect(`/${userName}/page/${slug}`);
Expand Down
48 changes: 36 additions & 12 deletions web/app/routes/$userName+/page+/$slug+/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Languages } from "lucide-react";
import { useState } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { LoadingSpinner } from "~/components/LoadingSpinner";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
import { Button } from "~/components/ui/button";
import { extractNumberedElements } from "~/features/prepare-html-for-translate/utils/extractNumberedElements";
import { AIModelSelector } from "~/features/translate/components/AIModelSelector";
import { getTranslateUserQueue } from "~/features/translate/translate-user-queue";
import { GeminiApiKeyDialog } from "~/routes/resources+/gemini-api-key-dialog";
Expand All @@ -31,7 +31,7 @@ import {
getDbUser,
} from "./functions/queries.server";
import { actionSchema } from "./types";

import { extractNumberedElements } from "./utils/extractNumberedElements";
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const { slug } = params;

Expand Down Expand Up @@ -77,10 +77,11 @@ export const action = async ({ request }: ActionFunctionArgs) => {
return {
intent: null,
lastResult: submission.reply({ formErrors: ["User not authenticated"] }),
slug: null,
};
}
if (submission.status !== "success") {
return { intent: null, lastResult: submission.reply() };
return { intent: null, lastResult: submission.reply(), slug: null };
}
const { intent } = submission.value;
switch (intent) {
Expand All @@ -90,22 +91,31 @@ export const action = async ({ request }: ActionFunctionArgs) => {
submission.value.isUpvote,
safeUserId,
);
return { intent, lastResult: submission.reply({ resetForm: true }) };
return {
intent,
lastResult: submission.reply({ resetForm: true }),
slug: null,
};
case "add":
handleAddTranslationAction(
submission.value.sourceTextId,
submission.value.text,
safeUserId,
targetLanguage,
);
return { intent, lastResult: submission.reply({ resetForm: true }) };
return {
intent,
lastResult: submission.reply({ resetForm: true }),
slug: null,
};
case "translate": {
const dbUser = await getDbUser(safeUser.id);
if (!dbUser?.geminiApiKey) {
return {
lastResult: submission.reply({
formErrors: ["Gemini API key is not set"],
}),
intent: null,
slug: null,
};
}
Expand All @@ -115,6 +125,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
lastResult: submission.reply({
formErrors: ["Page not found"],
}),
intent: null,
slug: null,
};
}
Expand All @@ -125,7 +136,10 @@ export const action = async ({ request }: ActionFunctionArgs) => {
targetLanguage,
);

const numberedElements = extractNumberedElements(page.content);
const numberedElements = extractNumberedElements(
page.content,
page.title,
);
const queue = getTranslateUserQueue(safeUser.id);
const job = await queue.add(`translate-${safeUser.id}`, {
userAITranslationInfoId: userAITranslationInfo.id,
Expand All @@ -138,7 +152,11 @@ export const action = async ({ request }: ActionFunctionArgs) => {
numberedContent: page.content,
numberedElements,
});
return { intent, lastResult: submission.reply({ resetForm: true }) };
return {
intent,
lastResult: submission.reply({ resetForm: true }),
slug: page.slug,
};
}
default:
throw new Error("Invalid Intent");
Expand Down Expand Up @@ -205,17 +223,23 @@ export default function ReaderView() {
)}
</div>
</Form>
{actionData?.slug && (
<Alert className="bg-blue-50 border-blue-200 text-blue-800 animate-in fade-in duration-300">
<AlertTitle className="text-center">
Translation Job Started
</AlertTitle>
<AlertDescription className="text-center">
<strong className="font-semibold ">{actionData.slug}</strong>
</AlertDescription>
</Alert>
)}
<UserAITranslationStatus
userAITranslationInfo={userAITranslationInfo}
/>
</div>
<article className="prose dark:prose-invert lg:prose-xl">
<h1>
{pageData.title}
<div>{pageData.translationTitle}</div>
</h1>
<hr />
<ContentWithTranslations
title={pageData.title}
content={pageData.content}
sourceTextWithTranslations={pageData.sourceTextWithTranslations}
userId={safeUser?.id ?? null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { TranslationWithVote } from "../types";

export function getBestTranslation(
translationsWithVotes: TranslationWithVote[],
): TranslationWithVote {
): TranslationWithVote | null {
if (translationsWithVotes.length === 0) {
return null;
}
const upvotedTranslations = translationsWithVotes.filter(
(t) => t.userVote?.isUpvote,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { JSDOM } from "jsdom";

export function extractNumberedElements(
content: string,
title: string,
): Array<{ number: number; text: string }> {
const doc = new JSDOM(content);
const numberedElements: Array<{ number: number; text: string }> = [];
const numberedElements: Array<{ number: number; text: string }> = [
{ number: 0, text: title },
];
// <br>のみを改行とする
doc.window.document.body.innerHTML = doc.window.document.body.innerHTML
.replace(/\n/g, "")
Expand Down
91 changes: 47 additions & 44 deletions web/app/routes/resources+/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,50 +42,36 @@ export function Footer({ safeUser }: FooterProps) {
return (
<footer className="bottom-0 left-0 right-0 border-t border-gray-200 dark:border-gray-700">
<div className="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center">
<div className="flex items-center">
<Link to="/">
<img
src="/title-logo-dark.png"
alt="::COMPANY_NAME::"
className="w-32"
/>
</Link>
</div>
<Link to={`/${safeUser?.userName}`}>
<Button variant="outline">{safeUser?.userName}</Button>
</Link>

<div className="flex items-center space-x-4 text-sm text-gray-6000 dark:text-gray-300">
<Link
to="/privacy"
className="hover:text-gray-900 dark:hover:text-white"
>
Privacy Policy
</Link>
<Link
to="/terms"
className="hover:text-gray-900 dark:hover:text-white"
>
Terms of Service
</Link>
<p>© {currentYear} EveEve</p>
</div>

<div>
{safeUser ? (
<Form method="post" action="/resources/footer">
<Button
type="submit"
name="intent"
value="logout"
variant="outline"
>
<LogOut className="w-4 h-4 mr-2" />
Log out
</Button>
</Form>
) : (
<div className="flex flex-col space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<Link to="/">
<img
src="/title-logo-dark.png"
alt="::COMPANY_NAME::"
className="w-32"
/>
</Link>
{safeUser && (
<>
<Link to={`/${safeUser.userName}`}>
<Button variant="outline">{safeUser.userName}</Button>
</Link>
<Form method="post" action="/resources/footer">
<Button
type="submit"
name="intent"
value="logout"
variant="outline"
>
<LogOut className="w-4 h-4 mr-2" />
Log out
</Button>
</Form>
</>
)}
</div>
{!safeUser && (
<Form method="post" action="/resources/footer">
<Button
type="submit"
Expand All @@ -99,6 +85,23 @@ export function Footer({ safeUser }: FooterProps) {
</Form>
)}
</div>
<div className="flex justify-between items-center text-sm text-gray-600 dark:text-gray-300 mt-8">
<div className="flex space-x-4">
<Link
to="/privacy"
className="hover:text-gray-900 dark:hover:text-white"
>
Privacy Policy
</Link>
<Link
to="/terms"
className="hover:text-gray-900 dark:hover:text-white"
>
Terms of Service
</Link>
</div>
<p>© {currentYear} EveEve</p>
</div>
</div>
</div>
</footer>
Expand Down
5 changes: 4 additions & 1 deletion web/app/routes/translator/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ export async function action({ request }: ActionFunctionArgs) {
targetLanguage,
);

const numberedElements = extractNumberedElements(numberedContent);
const numberedElements = extractNumberedElements(
numberedContent,
title,
);
await createOrUpdateSourceTexts(numberedElements, page.id);
// ファイルの翻訳ジョブをキューに追加
await queue.add(`translate-${safeUser.id}`, {
Expand Down
Loading