Skip to content

Commit 3005746

Browse files
committed
feat(client,extension): improve i18n moment locale and page context handling
- Move moment locale init into i18n module; bind locale to language changes - Make SmartMoment respect current i18n language instead of hardcoded zh-cn - Track tabTitle and articleTitle separately in extension page context - Expose both titles to the model via formatPageContextForModel - Refactor ComposerContextBar: entire pill is clickable when context unattached
1 parent f3d761b commit 3005746

6 files changed

Lines changed: 80 additions & 51 deletions

File tree

app/client/src/components/SmartMoment.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
import moment from "moment";
22
import Tooltip from "@mui/material/Tooltip";
3+
import { useTranslation } from "react-i18next";
34

45
type SmartMomentProps = {
56
dt: string | Date | number;
67
timeTypeLabel?: string;
78
}
89

910
const SmartMoment = ({dt, timeTypeLabel}: SmartMomentProps) => {
11+
const { i18n } = useTranslation();
12+
const momentLocale = i18n.language?.startsWith('zh') ? 'zh-cn' : 'en';
13+
const localized = moment(dt).locale(momentLocale);
14+
1015
function getMoment() {
1116
let text = "";
12-
if (moment(dt).isAfter(moment().add(-1, "d"))) {
13-
text = moment(dt).fromNow(true);
14-
} else if (moment(dt).isBefore(moment().startOf("year"))) {
15-
text = moment(dt).format('ll');
17+
if (localized.isAfter(moment().add(-1, "d"))) {
18+
text = localized.fromNow(true);
19+
} else if (localized.isBefore(moment().startOf("year"))) {
20+
text = localized.format('ll');
1621
} else {
17-
text = moment(dt).format('M-D HH:mm');
22+
text = localized.format('M-D HH:mm');
1823
}
1924
return text;
2025
}
2126

2227
const tooltipText = timeTypeLabel
23-
? `${timeTypeLabel}: ${moment(dt).format('a h:mm ll')}`
24-
: moment(dt).format('a h:mm ll');
28+
? `${timeTypeLabel}: ${localized.format('a h:mm ll')}`
29+
: localized.format('a h:mm ll');
2530

2631
return (
2732
<Tooltip title={tooltipText} arrow>

app/client/src/i18n/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import i18n from 'i18next';
22
import { initReactI18next } from 'react-i18next';
33
import LanguageDetector from 'i18next-browser-languagedetector';
4+
import moment from 'moment';
5+
import 'moment/locale/zh-cn';
46

57
import enCommon from './locales/en/common.json';
68
import enNavigation from './locales/en/navigation.json';
@@ -60,4 +62,11 @@ i18n
6062
},
6163
});
6264

65+
const toMomentLocale = (lng?: string) => (lng?.startsWith('zh') ? 'zh-cn' : 'en');
66+
67+
moment.locale(toMomentLocale(i18n.language));
68+
i18n.on('languageChanged', (lng) => {
69+
moment.locale(toMomentLocale(lng));
70+
});
71+
6372
export default i18n;

app/client/src/index.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import reportWebVitals from "./reportWebVitals";
66
import "./styles/globals.css";
77
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
88
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
9-
import moment from "moment";
10-
import 'moment/locale/zh-cn'
119
import {SnackbarProvider} from "notistack";
1210

1311
const queryClient = new QueryClient({
@@ -21,7 +19,6 @@ const queryClient = new QueryClient({
2119
}
2220
},
2321
});
24-
moment.locale('zh-cn');
2522

2623
const root = ReactDOM.createRoot(
2724
document.getElementById("root") as HTMLElement

app/extension/src/sidepanel/SidepanelApp.tsx

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,20 @@ async function createCurrentPageContextPart(): Promise<ChatPart> {
212212
throw new Error("Could not extract page content.");
213213
}
214214

215+
const tabTitle = tab.title || page.title || "Current tab";
216+
const articleTitle = page.title;
215217
const pageContext: ParsedPageContext = {
216218
...page,
217-
title: page.title || tab.title || "Current tab",
219+
title: articleTitle || tabTitle,
218220
url: page.url || tab.url,
219221
faviconUrl: page.faviconUrl || tab.favIconUrl,
220222
};
221223

222224
return {
223225
id: generateId(),
224226
type: "page-context",
225-
title: pageContext.title,
227+
title: tabTitle,
228+
articleTitle,
226229
url: pageContext.url,
227230
faviconUrl: pageContext.faviconUrl,
228231
content: buildPageContextMarkdown(pageContext),
@@ -862,44 +865,14 @@ const ComposerContextBar: FC<ComposerContextBarProps> = ({
862865
onDetachContext,
863866
}) => {
864867
const label = contextError || tabContext?.title || "Current tab";
865-
866-
return (
867-
<div
868-
className={[
869-
"mr-3 flex min-w-0 max-w-[min(360px,calc(100%-120px))] items-center gap-1.5 rounded-lg bg-[#fffaf4]/80 px-1.5 py-1 pr-3 shadow-[0_6px_18px_rgba(64,48,31,0.06)] transition-colors",
870-
contextAttached
871-
? "border border-solid border-[#d8cfbf]"
872-
: "border border-dashed border-[#d8b18d]",
873-
].join(" ")}
874-
>
875-
<div className="flex h-6 w-6 shrink-0 items-center justify-center">
876-
{contextAttached ? (
877-
<button
878-
type="button"
879-
aria-label="Remove attached context"
880-
title="Remove attached context"
881-
className="flex h-6 w-6 items-center justify-center rounded-md text-[#75695b] transition-colors hover:bg-[#f1e8da] hover:text-[#2f261f]"
882-
onClick={onDetachContext}
883-
>
884-
<X className="size-3.5" />
885-
</button>
886-
) : (
887-
<button
888-
type="button"
889-
aria-label="Add current tab context"
890-
title="Add current tab context"
891-
disabled={contextLoading || !tabContext}
892-
className="flex h-6 w-6 items-center justify-center rounded-md text-[#8a7e70] transition-colors hover:bg-[#f1e8da] hover:text-[#2f261f] disabled:cursor-not-allowed disabled:opacity-60"
893-
onClick={onAttachContext}
894-
>
895-
{contextLoading ? (
896-
<Loader2 className="size-3.5 animate-spin" />
897-
) : (
898-
<Plus className="size-3.5" />
899-
)}
900-
</button>
901-
)}
902-
</div>
868+
const containerClassName = [
869+
"mr-3 flex min-w-0 max-w-[min(360px,calc(100%-120px))] items-center gap-1.5 rounded-lg bg-[#fffaf4]/80 px-1.5 py-1 pr-3 shadow-[0_6px_18px_rgba(64,48,31,0.06)] transition-colors",
870+
contextAttached
871+
? "border border-solid border-[#d8cfbf]"
872+
: "border border-dashed border-[#d8b18d]",
873+
].join(" ");
874+
const tabContextDetails = (
875+
<>
903876
<TabFavicon
904877
faviconUrl={tabContext?.faviconUrl}
905878
muted={!contextAttached}
@@ -917,6 +890,48 @@ const ComposerContextBar: FC<ComposerContextBarProps> = ({
917890
>
918891
{label}
919892
</span>
893+
</>
894+
);
895+
896+
if (!contextAttached) {
897+
return (
898+
<button
899+
type="button"
900+
aria-label="Add current tab context"
901+
title="Add current tab context"
902+
disabled={contextLoading || !tabContext}
903+
className={[
904+
containerClassName,
905+
"cursor-pointer text-left hover:bg-[#fff5e8] disabled:cursor-not-allowed disabled:opacity-70",
906+
].join(" ")}
907+
onClick={onAttachContext}
908+
>
909+
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-[#8a7e70]">
910+
{contextLoading ? (
911+
<Loader2 className="size-3.5 animate-spin" />
912+
) : (
913+
<Plus className="size-3.5" />
914+
)}
915+
</div>
916+
{tabContextDetails}
917+
</button>
918+
);
919+
}
920+
921+
return (
922+
<div className={containerClassName}>
923+
<div className="flex h-6 w-6 shrink-0 items-center justify-center">
924+
<button
925+
type="button"
926+
aria-label="Remove attached context"
927+
title="Remove attached context"
928+
className="flex h-6 w-6 items-center justify-center rounded-md text-[#75695b] transition-colors hover:bg-[#f1e8da] hover:text-[#2f261f]"
929+
onClick={onDetachContext}
930+
>
931+
<X className="size-3.5" />
932+
</button>
933+
</div>
934+
{tabContextDetails}
920935
</div>
921936
);
922937
};

app/extension/src/sidepanel/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface ChatPart {
2727
id?: string;
2828
text?: string;
2929
title?: string;
30+
articleTitle?: string;
3031
url?: string;
3132
faviconUrl?: string;
3233
content?: string;

app/extension/src/sidepanel/useHuntlyChat.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ function safeStringify(value: unknown): string {
188188

189189
function formatPageContextForModel(part: ChatPart): string {
190190
const metadata: string[] = [];
191-
if (part.title) metadata.push(`Title: ${part.title}`);
191+
if (part.title) metadata.push(`Original page title: ${part.title}`);
192+
if (part.articleTitle)
193+
metadata.push(`Parsed article title: ${part.articleTitle}`);
192194
if (part.url) metadata.push(`URL: ${part.url}`);
193195

194196
return [

0 commit comments

Comments
 (0)