diff --git a/src/constants/faq.consts.ts b/src/constants/faq.consts.ts index 327cd1c937..0505404c78 100644 --- a/src/constants/faq.consts.ts +++ b/src/constants/faq.consts.ts @@ -1,4 +1,6 @@ import { OTHER_SUPPORTED_CHAINS, SUPPORTED_EVM_CHAINS } from '@/constants/rhino.consts' +import { getTranslations, t } from '@/i18n' +import { type Locale } from '@/i18n/types' import { chainDisplayName } from '@/utils/chain-display.utils' /** @@ -21,12 +23,17 @@ export const FIAT_RAILS = [ ] as const const EVM_CHAIN_LIST = SUPPORTED_EVM_CHAINS.map(chainDisplayName).join(', ') -const OTHER_CHAIN_LIST = OTHER_SUPPORTED_CHAINS.map(chainDisplayName).join(' and ') const FIAT_RAIL_LIST = FIAT_RAILS.map((rail) => `${rail.name} (${rail.currency}, ${rail.region})`).join(', ') -export const SUPPORTED_RAILS_FAQ_QUESTION = 'Which networks, tokens and banks does Peanut support?' - -export const SUPPORTED_RAILS_FAQ_ANSWER = - `Crypto: deposit and withdraw USDC and USDT on ${SUPPORTED_EVM_CHAINS.length} EVM networks with a single address (${EVM_CHAIN_LIST}), plus ${OTHER_CHAIN_LIST}. ETH is also supported on EVM networks; Tron is USDT-only. ` + - `Banks & local payment apps: ${FIAT_RAIL_LIST}. ` + - 'Deposits are free — Peanut covers the gas.' +export function supportedRailsFaq(locale: Locale): { question: string; answer: string } { + const translations = getTranslations(locale) + return { + question: translations.landingSupportedRailsFaqQuestion, + answer: t(translations.landingSupportedRailsFaqAnswer, { + evmCount: String(SUPPORTED_EVM_CHAINS.length), + evmList: EVM_CHAIN_LIST, + otherList: OTHER_SUPPORTED_CHAINS.map(chainDisplayName).join(` ${translations.listJoinAnd} `), + railList: FIAT_RAIL_LIST, + }), + } +} diff --git a/src/i18n/en.json b/src/i18n/en.json index fcd16187a3..48880d77b8 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -117,6 +117,9 @@ "landingCardCta": "GET MY CARD", "landingWallOfLove": "WALL OF LOVE", "landingWallOfLoveBody": "See what people are saying about Peanut on", + "landingSupportedRailsFaqQuestion": "Which networks, tokens and banks does Peanut support?", + "landingSupportedRailsFaqAnswer": "Crypto: deposit and withdraw USDC and USDT on {evmCount} EVM networks with a single address ({evmList}), plus {otherList}. ETH is also supported on EVM networks; Tron is USDT-only. Banks & local payment apps: {railList}. Deposits are free — Peanut covers the gas.", + "listJoinAnd": "and", "faqTitle": "FAQ", "heroMarqueeNoFees": "No fees", "heroMarqueeInstant": "Instant", diff --git a/src/i18n/es-419.json b/src/i18n/es-419.json index ae597625e4..9fad38b40f 100644 --- a/src/i18n/es-419.json +++ b/src/i18n/es-419.json @@ -117,6 +117,9 @@ "landingCardCta": "QUIERO MI TARJETA", "landingWallOfLove": "MURO DE AMOR", "landingWallOfLoveBody": "Mira lo que dicen de Peanut en", + "landingSupportedRailsFaqQuestion": "¿Con qué redes, tokens y bancos funciona Peanut?", + "landingSupportedRailsFaqAnswer": "Cripto: deposita y retira USDC y USDT en {evmCount} redes EVM con una sola dirección ({evmList}), además de {otherList}. ETH también está disponible en las redes EVM; Tron solo admite USDT. Bancos y apps de pago locales: {railList}. Los depósitos son gratis: Peanut cubre el gas.", + "listJoinAnd": "y", "faqTitle": "Preguntas frecuentes", "heroMarqueeNoFees": "Sin comisiones", "heroMarqueeInstant": "Al instante", diff --git a/src/i18n/es-ar.json b/src/i18n/es-ar.json index a410b0f5bc..093d9fedfa 100644 --- a/src/i18n/es-ar.json +++ b/src/i18n/es-ar.json @@ -117,6 +117,9 @@ "landingCardCta": "QUIERO MI TARJETA", "landingWallOfLove": "MURO DE AMOR", "landingWallOfLoveBody": "Mirá lo que dicen de Peanut en", + "landingSupportedRailsFaqQuestion": "¿Con qué redes, tokens y bancos funciona Peanut?", + "landingSupportedRailsFaqAnswer": "Cripto: depositá y retirá USDC y USDT en {evmCount} redes EVM con una sola dirección ({evmList}), además de {otherList}. ETH también está disponible en las redes EVM; Tron solo admite USDT. Bancos y apps de pago locales: {railList}. Los depósitos son gratis: Peanut cubre el gas.", + "listJoinAnd": "y", "faqTitle": "Preguntas frecuentes", "heroMarqueeNoFees": "Sin comisiones", "heroMarqueeInstant": "Al instante", diff --git a/src/i18n/pt-br.json b/src/i18n/pt-br.json index d4ea0c27cb..0b960b02f7 100644 --- a/src/i18n/pt-br.json +++ b/src/i18n/pt-br.json @@ -117,6 +117,9 @@ "landingCardCta": "QUERO MEU CARTÃO", "landingWallOfLove": "MURAL DE AMOR", "landingWallOfLoveBody": "Veja o que as pessoas dizem do Peanut no", + "landingSupportedRailsFaqQuestion": "Com quais redes, tokens e bancos o Peanut funciona?", + "landingSupportedRailsFaqAnswer": "Cripto: deposite e saque USDC e USDT em {evmCount} redes EVM com um único endereço ({evmList}), além de {otherList}. ETH também está disponível nas redes EVM; a Tron aceita apenas USDT. Bancos e apps de pagamento locais: {railList}. Os depósitos são grátis — o Peanut paga o gas.", + "listJoinAnd": "e", "faqTitle": "Perguntas frequentes", "heroMarqueeNoFees": "Sem taxas", "heroMarqueeInstant": "Na hora", diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 8cd2d20bf2..c30a82563c 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -149,6 +149,11 @@ export interface Translations { landingWallOfLove: string landingWallOfLoveBody: string + // Landing page — code-injected supported-rails FAQ (fact lists interpolated from rhino.consts) + landingSupportedRailsFaqQuestion: string + landingSupportedRailsFaqAnswer: string // "… {evmCount} … ({evmList}), plus {otherList} … {railList} …" + listJoinAnd: string // conjunction used when joining name lists ("Solana and Tron") + // Legal page headers (privacy/terms carry no in their verbatim markdown) legalHeroSubtitlePrivacy: string legalHeroSubtitleTerms: string diff --git a/src/lib/landingContent.ts b/src/lib/landingContent.ts index 95c77a8539..3ce0f2a1fa 100644 --- a/src/lib/landingContent.ts +++ b/src/lib/landingContent.ts @@ -5,11 +5,7 @@ // component or route handler, never inside a 'use client' file. import { readSingletonContentLocalized } from '@/lib/content' -import { - SUPPORTED_RAILS_FAQ_ANSWER, - SUPPORTED_RAILS_FAQ_ID, - SUPPORTED_RAILS_FAQ_QUESTION, -} from '@/constants/faq.consts' +import { SUPPORTED_RAILS_FAQ_ID, supportedRailsFaq } from '@/constants/faq.consts' import type { Locale } from '@/i18n/types' interface LandingFrontmatter { @@ -45,27 +41,25 @@ const DEFAULTS: LandingContent = { marqueeMessages: [], } -// Code-defined FAQ item advertising supported networks/tokens/bank rails. -// Lives in code (not the content MD) because its facts derive from -// rhino.consts — the same constants the add-money flow renders — so the -// public answer can't drift from what the app actually supports. -// LandingPageClient swaps in the rich chip UI by this id. -const SUPPORTED_RAILS_QUESTION = { - id: SUPPORTED_RAILS_FAQ_ID, - question: SUPPORTED_RAILS_FAQ_QUESTION, - answer: SUPPORTED_RAILS_FAQ_ANSWER, -} +// Anchor for the code-injected rails FAQ: the landing content files keep FAQ +// ids aligned across locales, and "1" is "What is Peanut?" in every language. +const WHAT_IS_PEANUT_FAQ_ID = '1' // Insert right after the "What is Peanut?" question (falls back to the end). -function withSupportedRails(questions: LandingContent['faqData']['questions']) { - const idx = questions.findIndex((q) => /what is peanut\??/i.test(q.question)) +// The rails item lives in code (not the content MD) because its facts derive +// from rhino.consts — the same constants the add-money flow renders — so the +// public answer can't drift from what the app actually supports. +// LandingPageClient swaps in the rich chip UI by this id. +function withSupportedRails(questions: LandingContent['faqData']['questions'], locale: Locale) { + const idx = questions.findIndex((q) => q.id === WHAT_IS_PEANUT_FAQ_ID) const at = idx === -1 ? questions.length : idx + 1 - return [...questions.slice(0, at), SUPPORTED_RAILS_QUESTION, ...questions.slice(at)] + const rails = { id: SUPPORTED_RAILS_FAQ_ID, ...supportedRailsFaq(locale) } + return [...questions.slice(0, at), rails, ...questions.slice(at)] } export function getLandingContent(locale: Locale = 'en'): LandingContent { const base = readLandingContent(locale) - return { ...base, faqData: { ...base.faqData, questions: withSupportedRails(base.faqData.questions) } } + return { ...base, faqData: { ...base.faqData, questions: withSupportedRails(base.faqData.questions, locale) } } } function readLandingContent(locale: Locale): LandingContent {