Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cbb5b17
Merge pull request #399 from wri/develop
kamicut Jan 19, 2026
5fff746
Merge pull request #407 from wri/develop
kamicut Jan 29, 2026
f4eb246
Merge pull request #413 from wri/develop
kamicut Feb 5, 2026
45d8f98
chore: add PRD files to gitignore
01painadam Feb 22, 2026
328a04b
chore: install next-intl
01painadam Feb 22, 2026
e37f769
feat(i18n): add i18n config, English locale files, and message loader
01painadam Feb 22, 2026
e43bf23
feat(i18n): port language infrastructure from language-accessibility
01painadam Feb 22, 2026
2bcc7d7
feat(i18n): add I18nProvider with reactive locale loading
01painadam Feb 22, 2026
b67d47c
feat(i18n): migrate ContextButton to useChatContextOptions hook
01painadam Feb 22, 2026
235e029
feat(i18n): migrate ChatInput strings to useTranslations
01painadam Feb 22, 2026
1305152
feat(i18n): migrate ChatPanelHeader strings to useTranslations
01painadam Feb 22, 2026
9346645
feat(i18n): migrate PageHeader strings + add LanguageSelector to header
01painadam Feb 22, 2026
69b5c90
feat(i18n): migrate sidebar strings to useTranslations
01painadam Feb 22, 2026
be33ec2
feat(i18n): Phase 3 - migrate all chat components to useTranslations
01painadam Feb 22, 2026
d41770b
feat(i18n): Phase 4 - migrate onboarding form and dashboard settings
01painadam Feb 22, 2026
eb2f472
feat(i18n): Phase 5 - migrate error/status pages (not-found, maintena…
01painadam Feb 22, 2026
cdd6c50
feat(i18n): Phase 6 - migrate landing page components (GlobalHeader +…
01painadam Feb 22, 2026
50a45f4
feat(i18n): Phase 7 - create 28 non-English locale files (en copies f…
01painadam Feb 22, 2026
98e5b06
feat(i18n): enhance LanguageSelector with section label, 'Other Langu…
01painadam Feb 22, 2026
8668ea0
fix(i18n): add timeZone='UTC' to NextIntlClientProvider to suppress E…
01painadam Feb 22, 2026
34eb95d
fix(i18n): persist header language selection to user profile API
01painadam Feb 22, 2026
97fb415
Revert "fix(i18n): persist header language selection to user profile …
01painadam Feb 22, 2026
1b9b6be
fix(i18n): fix LanguageSelector dropdown text visibility
01painadam Feb 22, 2026
ee172cb
feat(i18n): add real translations for fr/es/pt/id (excluding landing)
01painadam Feb 22, 2026
e4609bc
docs: add i18n translation verification guide
01painadam Feb 22, 2026
c7e7c1c
feat(i18n): translate preview disclaimer banner + update verification…
01painadam Feb 22, 2026
982cc8c
style(i18n): swap to TranslateIcon and fix header spacing
01painadam Feb 22, 2026
f553387
Add AI translation disclaimer to language selector dropdown
01painadam Feb 22, 2026
0f040a2
fix: validate prompt arrays — reject empty and filter non-string elem…
01painadam Feb 23, 2026
c8330ac
fix: prevent race condition in prompt language switching
01painadam Feb 23, 2026
f8214ad
Fix test
01painadam Feb 23, 2026
60341fb
Fix welcome prompts on change, add test
01painadam Feb 23, 2026
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts

.prompts/*
.prompts/*PRD.md
PRD.md
PRD-*.md
4 changes: 3 additions & 1 deletion app/(home)/sections/10_NewEraQuote.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useTranslations } from "next-intl";
import { Box, Container, Text, Heading } from "@chakra-ui/react";

export default function NewEraQuoteSection() {
const t = useTranslations("landing");
return (
<Box
py={{ base: 14, md: 24 }}
Expand All @@ -10,7 +12,7 @@ export default function NewEraQuoteSection() {
>
<Container maxW="2xl">
<Heading textAlign="center" size={{ base: "3xl", md: "4xl" }}>
The marker of a new era
{t("newEraQuote.attribution")}
</Heading>
<Text fontSize="lg" mb="4">
World Resources Institute tools have been transforming global
Expand Down
10 changes: 6 additions & 4 deletions app/(home)/sections/11_CTA.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Box, Button, Container, Heading, Text } from "@chakra-ui/react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { CaretRightIcon } from "@phosphor-icons/react";

const LANDING_PAGE_VERSION = process.env.NEXT_PUBLIC_LANDING_PAGE_VERSION;

export default function CTASection() {
const t = useTranslations("landing");
const tc = useTranslations("common");
return (
<Box
py={{ base: 10, md: 24 }}
Expand All @@ -28,16 +31,15 @@ export default function CTASection() {
>
<Box display="flex" flexDir="column" gap="2">
<Heading size="md" as="p">
How will you use monitoring intelligence?{" "}
{t("cta.heading")}
</Heading>
<Text fontSize="sm" color="fg.muted">
Join the future of ecosystem monitoring and help us shape what
comes next.
{t("cta.description")}
</Text>
</Box>
<Button asChild variant="solid" colorPalette="primary" rounded="lg">
<Link href="/app">
{LANDING_PAGE_VERSION === "public" ? "Explore the preview" : "Try the preview"}
{LANDING_PAGE_VERSION === "public" ? tc("nav.explorePreview") : t("cta.tryPreview")}
<CaretRightIcon weight="bold" />
</Link>
</Button>
Expand Down
6 changes: 4 additions & 2 deletions app/(home)/sections/12_Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
Text,
Link as ChakraLink,
} from "@chakra-ui/react";
import { useTranslations } from "next-intl";
import LclLogo from "../../components/LclLogo";

export default function FooterSection() {
const t = useTranslations("common");
return (
<Box
as="footer"
Expand All @@ -30,7 +32,7 @@ export default function FooterSection() {
<Flex alignItems="center" gap={4}>
<LclLogo width={32} avatarOnly />
<Text fontSize={{ base: "3xl", md: "5xl" }} fontWeight="semibold" lineHeight="1">
Global Nature Watch
{t("appName")}
</Text>
</Flex>
</Flex>
Expand All @@ -50,7 +52,7 @@ export default function FooterSection() {
w={{ base: "full", md: "auto" }}
flex={{ md: 2 }}
>
<Text>{new Date().getFullYear()} Global Nature Watch</Text>
<Text>{t("footer.copyrightLong", { year: new Date().getFullYear() })}</Text>
<ChakraLink
textDecoration="underline"
textDecorationStyle="dotted"
Expand Down
29 changes: 15 additions & 14 deletions app/(home)/sections/1_Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslations } from "next-intl";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import {
Expand Down Expand Up @@ -30,6 +31,8 @@ export default function LandingHero({
promptIndex,
setPromptIndex,
}: PromptMarqueeProps) {
const t = useTranslations("landing");
const tc = useTranslations("common");
const router = useRouter();
const [promptTimer, setPromptTimer] = useState(10);
const [animationKey, setAnimationKey] = useState(0);
Expand Down Expand Up @@ -158,15 +161,13 @@ export default function LandingHero({
color="fg.inverted"
mb={0}
>
Tackle nature&rsquo;s toughest monitoring challenges
{t("hero.heading")}
</Heading>
<Text
fontSize="lg"
textShadow="2px 2px 5px hsla(225, 52%, 11%, 0.75)"
>
Global Nature Watch is an open, AI-powered system that transforms groundbreaking
land monitoring data into intelligence to understand Earth&rsquo;s landscapes.
Test the preview and help shape the future of land monitoring.
{t("hero.description")}
</Text>
</Container>
<Container
Expand Down Expand Up @@ -244,10 +245,10 @@ export default function LandingHero({
}}
>
<ArrowsClockwiseIcon />
New Suggestion
{t("hero.newSuggestion")}
</Button>
<Text fontSize="xs" color="fg.subtle">
Automatically updating in {promptTimer}s
{t("hero.autoUpdating", { seconds: promptTimer })}
</Text>
</Flex>
<Button
Expand All @@ -259,7 +260,7 @@ export default function LandingHero({
disabled={sendingPrompt}
>
{sendingPrompt ? <Spinner size="sm" mr={2} /> : null}
{sendingPrompt ? "Sending" : "Go"}
{sendingPrompt ? t("hero.sending") : t("hero.go")}
<CaretRightIcon weight="bold" />
</Button>
</Flex>
Expand All @@ -282,19 +283,19 @@ export default function LandingHero({
>
<Text>
<Badge size="xs" fontSize="8px" rounded="4px" mr="1">
PREVIEW
{tc("preview")}
</Badge>
Global Nature Watch is
{t("hero.previewBadgePrefix")}
{LANDING_PAGE_VERSION === "closed"
? " in closed preview."
? t("hero.previewBadgeClosed")
: LANDING_PAGE_VERSION === "limited"
? " in limited preview."
: " in preview."}
? t("hero.previewBadgeLimited")
: t("hero.previewBadgePublic")}
</Text>
<Tooltip
openDelay={100}
closeDelay={300}
content="This is an early version of Global Nature Watch. Our agent may make mistakes and some features are still in development. Your usage and feedback will help us enhance it!"
content={t("hero.previewTooltip")}
>
<Box
color="fg.inverted"
Expand All @@ -306,7 +307,7 @@ export default function LandingHero({
alignItems="center"
>
<QuestionIcon />
What does preview mean?
{t("hero.whatDoesPreviewMean")}
</Box>
</Tooltip>
</Box>
Expand Down
5 changes: 3 additions & 2 deletions app/(home)/sections/3_TrustedPlatforms.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslations } from "next-intl";
import NextImage from "next/image";
import {
Box,
Expand Down Expand Up @@ -45,6 +46,7 @@ const PARTNER_ORGS = [
];

export default function TrustedPlatformsSection() {
const t = useTranslations("landing");
return (
<Box
py={{ base: 14, md: 24 }}
Expand All @@ -55,8 +57,7 @@ export default function TrustedPlatformsSection() {
<Container id="testimonials">
<Container textAlign="center" maxW="3xl">
<Heading size={{ base: "3xl", md: "4xl" }}>
Building upon the legacy of World Resources Institute&rsquo;s
trusted platforms
{t("trustedPlatforms.heading")}
</Heading>
<Text fontSize="lg">
Global Nature Watch is built on the trusted data and research of Global
Expand Down
55 changes: 21 additions & 34 deletions app/(home)/sections/4_FeaturesTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,29 @@ import {
Image,
} from "@chakra-ui/react";
import { CaretRightIcon } from "@phosphor-icons/react";
import { useTranslations } from "next-intl";
import Link from "next/link";

const LANDING_PAGE_VERSION = process.env.NEXT_PUBLIC_LANDING_PAGE_VERSION;

const FEATURE_TABS = [
{
value: "feature-tab-1",
label: "Explore trusted data with an AI assistant",
description:
"Ask a question in plain language and our assistant will suggest the most useful available datasets and analyses for your work.",
caption:
"Quickly find the most relevant data for your work.",
image: "/feature-tab-1.webp",
},
{
value: "feature-tab-2",
label: "Tailored answers to your context",
description:
"Explore how Global Nature Watch's assistant can shape responses to your needs, from comparing regions to highlighting local patterns that may be most relevant to your work.",
caption:
"Shape responses to your needs.",
image: "/feature-tab-2.webp",
},
{
value: "feature-tab-3",
label: "Insights you can act on",
description:
"Global Nature Watch's assistant helps translate analyses into clear takeaways. It offers a starting point for reports, policies or field decisions while opening the door to dive deeper.",
caption:
"Generate clear takeaways from complex data.",
image: "/feature-tab-3.webp",
},
const TAB_IMAGES = [
"/feature-tab-1.webp",
"/feature-tab-2.webp",
"/feature-tab-3.webp",
];

export default function FeaturesTabsSection() {
const t = useTranslations("landing");
const tc = useTranslations("common");

const tabs = TAB_IMAGES.map((image, i) => ({
value: `feature-tab-${i + 1}`,
label: t(`features.tabs.${i}.label`),
description: t(`features.tabs.${i}.description`),
caption: t(`features.tabs.${i}.caption`),
image,
}));

return (
<Box
py={{ base: 14, md: 24 }}
Expand All @@ -53,11 +41,10 @@ export default function FeaturesTabsSection() {
<Container id="use-cases">
<Container textAlign="center" maxW="3xl" px={0}>
<Heading size={{ base: "3xl", md: "4xl" }}>
Get answers to your toughest questions about landscapes, backed by data
{t("features.heading")}
</Heading>
<Text fontSize="lg">
Global Nature Watch is testing new ways to make geospatial information easier to use.
Try asking in plain language and explore the insights it can provide.
{t("features.description")}
</Text>
{LANDING_PAGE_VERSION !== "closed" && (
<Button
Expand All @@ -68,7 +55,7 @@ export default function FeaturesTabsSection() {
rounded="lg"
>
<Link href="/app">
Explore the preview
{tc("nav.explorePreview")}
<CaretRightIcon weight="bold" />
</Link>
</Button>
Expand All @@ -82,7 +69,7 @@ export default function FeaturesTabsSection() {
defaultValue="feature-tab-1"
>
<Tabs.List borderEndWidth={0} gap={6}>
{FEATURE_TABS.map((tab) => (
{tabs.map((tab) => (
<Tabs.Trigger
key={tab.value}
value={tab.value}
Expand Down Expand Up @@ -124,7 +111,7 @@ export default function FeaturesTabsSection() {
</Tabs.Trigger>
))}
</Tabs.List>
{FEATURE_TABS.map((tab) => (
{tabs.map((tab) => (
<Tabs.Content
key={tab.value}
value={tab.value}
Expand Down
4 changes: 3 additions & 1 deletion app/(home)/sections/5_SupportWorkTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslations } from "next-intl";
import {
Box,
Button,
Expand Down Expand Up @@ -44,6 +45,7 @@ const SUPPORT_TABS = [
},
];
export default function SupportWorkTabsSection() {
const t = useTranslations("landing");
return (
<Box
id="about"
Expand All @@ -64,7 +66,7 @@ export default function SupportWorkTabsSection() {
px={0}
>
<Heading size={{ base: "3xl", md: "4xl" }} color="fg.inverted">
See how monitoring intelligence can support your work
{t("supportWork.heading")}
</Heading>
<Text fontSize="md" mb="4">
Global Nature Watch makes advanced monitoring data easy to use,
Expand Down
Loading