Skip to content

Commit

Permalink
Merge pull request fatwang2#12 from 6drf21e/feature/add-multilingual-…
Browse files Browse the repository at this point in the history
…support
  • Loading branch information
fatwang2 authored Apr 3, 2024
2 parents 075fdce + fae8614 commit 3345e72
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 14 deletions.
3 changes: 2 additions & 1 deletion web/src/app/components/answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BookOpenText } from "lucide-react";
import { FC } from "react";
import Markdown from "react-markdown";
import Image from "next/image";
import Locale from "../locales";

export const Answer: FC<{ markdown: string; sources: Source[] }> = ({
markdown,
Expand All @@ -19,7 +20,7 @@ export const Answer: FC<{ markdown: string; sources: Source[] }> = ({
<Wrapper
title={
<>
<BookOpenText></BookOpenText> Answer
<BookOpenText></BookOpenText> {Locale.Answer.answer}
</>
}
content={
Expand Down
7 changes: 2 additions & 5 deletions web/src/app/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Mails } from "lucide-react";
import { FC } from "react";

import Locale from "../locales";
export const Footer: FC = () => {
return (
<div className="text-center flex flex-col items-center text-xs text-zinc-700 gap-1">
<div className="text-zinc-400">
Answer generated by large language models, plz double check for
correctness.
</div>
<div className="text-zinc-400">{Locale.Footer.statement}</div>
<div className="flex gap-2 justify-center">
<div>
<a
Expand Down
7 changes: 5 additions & 2 deletions web/src/app/components/relates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { Wrapper } from "@/app/components/wrapper";
import { Relate } from "@/app/interfaces/relate";
import { MessageSquareQuote } from "lucide-react";
import React, { FC } from "react";
import Locale from "../locales";

export const Relates: FC<{ relates: Relate[] | null }> = ({ relates }) => {
return (
<Wrapper
title={
<>
<MessageSquareQuote></MessageSquareQuote> Related
<MessageSquareQuote></MessageSquareQuote> {Locale.Relates.related}
</>
}
content={
Expand All @@ -21,7 +22,9 @@ export const Relates: FC<{ relates: Relate[] | null }> = ({ relates }) => {
<PresetQuery key={question} query={question}></PresetQuery>
))
) : (
<div className="text-sm">No related questions.</div>
<div className="text-sm">
{Locale.Relates.no_related_questions}
</div>
)
) : (
<>
Expand Down
6 changes: 3 additions & 3 deletions web/src/app/components/result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Source } from "@/app/interfaces/source";
import { parseStreaming } from "@/app/utils/parse-streaming";
import { Annoyed } from "lucide-react";
import { FC, useEffect, useState } from "react";
import Locale, { getLang } from "../locales";

export const Result: FC<{ query: string; rid: string }> = ({ query, rid }) => {
const [sources, setSources] = useState<Source[]>([]);
Expand All @@ -19,6 +20,7 @@ export const Result: FC<{ query: string; rid: string }> = ({ query, rid }) => {
controller,
query,
rid,
getLang(),
setSources,
setMarkdown,
setRelates,
Expand All @@ -37,9 +39,7 @@ export const Result: FC<{ query: string; rid: string }> = ({ query, rid }) => {
<div className="absolute inset-4 flex items-center justify-center bg-white/40 backdrop-blur-sm">
<div className="p-4 bg-white shadow-2xl rounded text-blue-500 font-medium flex gap-4">
<Annoyed></Annoyed>
{error === 429
? "Sorry, you have made too many requests recently, try again later."
: "Sorry, we might be overloaded, try again later."}
{error === 429 ? Locale.Err[429] : Locale.Err[500]}
</div>
</div>
)}
Expand Down
3 changes: 2 additions & 1 deletion web/src/app/components/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { nanoid } from "nanoid";
import { useRouter } from "next/navigation";
import React, { FC, useState } from "react";
import { useSearchParams } from "next/navigation";
import Locale from "../locales";

interface SearchProps {
useContinueButton?: boolean; // true: 使用“继续对话”按钮; false: 使用“新的搜索”按钮
Expand Down Expand Up @@ -41,7 +42,7 @@ export const Search: FC<SearchProps> = ({ useContinueButton = false }) => {
value={value}
onChange={(e) => setValue(e.target.value)}
autoFocus
placeholder="Ask me anything ..."
placeholder={Locale.Search.placeholder}
className="px-2 pr-6 w-full rounded-md flex-1 outline-none bg-white"
/>
<button
Expand Down
3 changes: 2 additions & 1 deletion web/src/app/components/sources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Wrapper } from "@/app/components/wrapper";
import { Source } from "@/app/interfaces/source";
import { BookText } from "lucide-react";
import { FC } from "react";
import Locale from "../locales";

const SourceItem: FC<{ source: Source; index: number }> = ({
source,
Expand Down Expand Up @@ -42,7 +43,7 @@ export const Sources: FC<{ sources: Source[] }> = ({ sources }) => {
<Wrapper
title={
<>
<BookText></BookText> Sources
<BookText></BookText> {Locale.Sources.sources}
</>
}
content={
Expand Down
4 changes: 3 additions & 1 deletion web/src/app/components/title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getSearchUrl } from "@/app/utils/get-search-url";
import { RefreshCcw } from "lucide-react";
import { nanoid } from "nanoid";
import { useRouter } from "next/navigation";
import Locale from "../locales";

export const Title = ({ query }: { query: string }) => {
const router = useRouter();
Expand All @@ -22,7 +23,8 @@ export const Title = ({ query }: { query: string }) => {
type="button"
className="rounded flex gap-2 items-center bg-transparent px-2 py-1 text-xs font-semibold text-blue-500 hover:bg-zinc-100"
>
<RefreshCcw size={12}></RefreshCcw>Rewrite
<RefreshCcw size={12}></RefreshCcw>
{Locale.Title.rewrite}
</button>
</div>
</div>
Expand Down
36 changes: 36 additions & 0 deletions web/src/app/locales/cn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const cn = {
Err: {
429: "请求过于频繁,请稍后再试",
500: "抱歉,我们可能负载过重,请稍后再试。",
},
Title: {
rewrite: "重写",
},
Search: {
placeholder: "有问必答...",
},
Footer: {
statement: "这是由大语言模型提供的答案, 请务必核实正确性。",
},
Relates: {
no_related_questions: "没有相关问题。",
related: "相关",
},
Answer: {
answer: "答案",
},
Sources: {
sources: "来源",
},
};

type DeepPartial<T> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;

export type LocaleType = typeof cn;
export type PartialLocaleType = DeepPartial<typeof cn>;

export default cn;
30 changes: 30 additions & 0 deletions web/src/app/locales/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LocaleType } from "./index";

const en: LocaleType = {
Err: {
429: "Sorry, you have made too many requests recently, try again later.",
500: "Sorry, we might be overloaded, try again later.",
},
Title: {
rewrite: "Rewrite",
},
Search: {
placeholder: "Ask me anything ...",
},
Footer: {
statement:
"Answer generated by large language models, plz double check for correctness.",
},
Relates: {
no_related_questions: "No related questions.",
related: "Related",
},
Answer: {
answer: "Answer",
},
Sources: {
sources: "Sources",
},
};

export default en;
75 changes: 75 additions & 0 deletions web/src/app/locales/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// reference source: https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/blob/main/app/locales/index.ts
import cn from "./cn";
import en from "./en";
import jp from "./jp";

import type { LocaleType } from "./cn";
export type { LocaleType, PartialLocaleType } from "./cn";

const ALL_LANGS = {
en,
cn,
jp,
};

export type Lang = keyof typeof ALL_LANGS;

export const AllLangs = Object.keys(ALL_LANGS) as Lang[];

export const ALL_LANG_OPTIONS: Record<Lang, string> = {
en: "English",
cn: "简体中文",
jp: "日本語",
};

const LANG_KEY = "lang";
const DEFAULT_LANG = "en";

const targetLang = ALL_LANGS[getLang()] as LocaleType;

export default targetLang as LocaleType;

function getItem(key: string) {
try {
return localStorage.getItem(key);
} catch {
return null;
}
}

function setItem(key: string, value: string) {
try {
localStorage.setItem(key, value);
} catch {}
}

function getLanguage() {
try {
return navigator.language.toLowerCase();
} catch {
return DEFAULT_LANG;
}
}

export function getLang(): Lang {
const savedLang = getItem(LANG_KEY);

if (AllLangs.includes((savedLang ?? "") as Lang)) {
return savedLang as Lang;
}

const lang = getLanguage();

for (const option of AllLangs) {
if (lang.includes(option)) {
return option;
}
}

return DEFAULT_LANG;
}

export function changeLang(lang: Lang) {
setItem(LANG_KEY, lang);
location.reload();
}
30 changes: 30 additions & 0 deletions web/src/app/locales/jp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LocaleType } from "./index";

const jp: LocaleType = {
Err: {
429: "申し訳ありませんが、最近のリクエストが多すぎます。後ほど再試行してください。",
500: "申し訳ありませんが、サーバーが過負荷の可能性があります。後ほど再試行してください。",
},
Title: {
rewrite: "書き直し",
},
Search: {
placeholder: "何でも聞いてください...",
},
Footer: {
statement:
"回答は大規模な言語モデルによって生成されます。正確性を再確認してください。",
},
Relates: {
no_related_questions: "関連する質問はありません。",
related: "関連性",
},
Answer: {
answer: "回答",
},
Sources: {
sources: "情報源",
},
};

export default jp;
18 changes: 18 additions & 0 deletions web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,28 @@ import { Logo } from "@/app/components/logo";
import { PresetQuery } from "@/app/components/preset-query";
import { Search } from "@/app/components/search";
import React from "react";
import { AllLangs, ALL_LANG_OPTIONS, changeLang, getLang } from "./locales";

export default function Home() {
return (
<div className="absolute inset-0 min-h-[500px] flex items-center justify-center">
{/* 语言下拉菜单 */}
<div className="absolute top-0 right-0 m-4">
<select
className="rounded border border-gray-300 text-zinc-400 bg-white p-1 shadow-sm text-sm"
value={getLang()}
onChange={(e) => {
changeLang(e.target.value as any);
}}
>
{AllLangs.map((lang) => (
<option value={lang} key={lang}>
{" "}
{ALL_LANG_OPTIONS[lang]}{" "}
</option>
))}
</select>
</div>
<div className="relative flex flex-col gap-8 px-4 -mt-24">
<Logo></Logo>
<Search></Search>
Expand Down
2 changes: 2 additions & 0 deletions web/src/app/utils/parse-streaming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const parseStreaming = async (
controller: AbortController,
query: string,
search_uuid: string,
lang: string,
onSources: (value: Source[]) => void,
onMarkdown: (value: string) => void,
onRelates: (value: Relate[]) => void,
Expand All @@ -28,6 +29,7 @@ export const parseStreaming = async (
body: JSON.stringify({
query,
search_uuid,
lang,
}),
});
if (response.status !== 200) {
Expand Down

0 comments on commit 3345e72

Please sign in to comment.