Skip to content

feat: language-consistent experience — regional pricing, i18n, chatbot (→ staging) - #1656

Closed
rdahis wants to merge 27 commits into
stagingfrom
feat/lang-consistent-experience
Closed

feat: language-consistent experience — regional pricing, i18n, chatbot (→ staging)#1656
rdahis wants to merge 27 commits into
stagingfrom
feat/lang-consistent-experience

Conversation

@rdahis

@rdahis rdahis commented Aug 19, 2026

Copy link
Copy Markdown
Member

Warning

Do not merge until staging has been reset to main.
Merging now conflicts, because staging still carries these same changes under their
original SHAs. After the reset this applies cleanly.

Reconstructs the language-consistent experience work onto main.

These 22 commits were originally pushed straight to staging under the old
workflow. staging is scheduled to be reset to main, so the work has been
cherry-picked onto a branch cut from main per the current Git Flow.

What's here

  • Regional pricing (display): currency by domain, exact Stripe price ids pinned per region (br / latam / intl), interval-aware fallback for the DB Pro card.
  • i18n: /faq help center, dataset usage guides, column descriptions, Discord link, signup terms, "DB" branding across the en site.
  • Chatbot: site locale sent when creating a thread, localized chat UI (en/es), login stays on the originating domain, localized tab title, DB logo on the English site.
  • About us: show only active team roles.

Verification

  • All 22 cherry-picked onto main with zero conflicts.
  • Fidelity checked against staging: the only files differing are ones also touched by
    AldemirLucas's 3 staging-only commits — nothing from this set was dropped.
  • Original authorship and dates preserved; each commit records its (cherry picked from …) provenance.

Deploy order

Merge after the backend PR of the same name. The pinned-price-id display queries
stripePriceId; if this reaches prod first, getPlans errors and every plan falls back to BRL.

rdahis and others added 27 commits August 13, 2026 09:05
- Implemented Meta Pixel code for tracking page views on the production environment.
- Added noscript fallback for Meta Pixel to ensure tracking in cases where JavaScript is disabled.
feat: add Meta Pixel tracking code for analytics integration
…odel

docs: correct CLAUDE.md Git Flow model (branch from main, same commits)
The checkout modal rendered the raw Stripe product name
(checkoutInfos.productName, e.g. "BD Pro") on every locale. English
users saw the Portuguese brand "BD" instead of "DB".

Add a locale-aware helper that swaps the standalone "BD" brand token to
"DB" when the locale is English, applied at both product-name display
sites. Portuguese and Spanish are unchanged (their brand is "BD"), and
non-branded names (e.g. Chatbot products) pass through untouched.

(cherry picked from commit c3459f9)
The /prices page and the account Plans & Payment tab picked plans by matching an
active price on (product, interval, amount). That's fragile once a product has
multiple active prices — legacy tiers, gift prices, and the per-currency BRL/USD
prices from the international rollout. Select each plan by its exact Stripe price
id (verifiable in the Stripe dashboard) instead.

- next/constants/stripePlans.js (new): the price ids the site sells and a shared
  selectPlans(); ids overridable per environment via NEXT_PUBLIC_STRIPE_PRICE_IDS.
- next/pages/api/stripe/getPlans.js: request the new `stripePriceId` field.
- next/pages/prices.js and PlansAndPayment.js: use selectPlans(), dropping the
  duplicated amount-based filters.

Requires the backend `stripePriceId` field (basedosdados/backend#1044) in the
environment the site queries (NEXT_PUBLIC_API_URL). #1044 is on staging; it must
reach main before this ships to production.

(cherry picked from commit 0a2fb5f)
Extends the id-based price selection from #1620 with a currency dimension.
Each plan is still anchored to its BRL price by exact Stripe price id; on the
international domains it is swapped for the sibling price carrying the matching
`region` tag (same product and interval). Currency formatting follows the shown
price's region — BRL on basedosdados.org (pt), USD on data-basis.org (en) and
basedelosdatos.org (es). This mirrors the backend checkout webhook, which
charges the region-correct price from the card's country, so what the visitor
sees is what they are charged.

- stripePlans.js: localeToRegion() maps the domain locale to br/latam/intl;
  selectPlans(edges, region) swaps each anchor to its regional sibling, falling
  back to the BRL anchor when no sibling exists (or region is br); regionCurrency
  and formatCurrency centralize BRL/USD presentation.
- getPlans API: request the new `region` field on each price node.
- prices.js: CardPrice takes the selected price's region and renders its currency
  symbol; the plan cards pass region from the selected node (free card from the
  locale).
- PlansAndPayment.js checkout: formatCheckoutAmount formats in the selected
  plan's currency; all BRL-hardcoded amount displays now route through it.

Degrades safely: if the backend `region` field is absent or a price has no
regional sibling, every plan shows the BRL price, i.e. today's behavior. Ships
as a pair with the backend regional-pricing change (which exposes `region` and
enforces the charge); deploy the backend enforcement no earlier than this, so
the site never shows R$ while charging US$.

(cherry picked from commit 7bcfde3)
…ection

Replaces the region-metadata matching added earlier with an explicit, pinned map
of all 12 Stripe price ids (4 plans × br/latam/intl). Selection is now purely
id-based and deterministic: a new, edited, or mis-tagged price in Stripe is
ignored until its id is added here and the site is redeployed. Nothing is
inferred from `region`, `productSlug`, `interval`, or amount, so metadata drift
(or an extra active seat-tier price on BD Pro) can no longer change what the
storefront shows or sells.

Only the *selection* is pinned — the displayed amount still comes live from the
getPlans query, so a price change in Stripe reflects automatically; only adding
or swapping which price is sold needs a code change and deploy.

- stripePlans.js: DEFAULT_PRICE_IDS becomes region → plan → id; selectPlans(edges,
  region) matches by exact stripePriceId only, no cross-region fallback; env
  override NEXT_PUBLIC_STRIPE_PRICE_IDS is now shaped the same (region-keyed).
- getPlans API: drop the `region` field from the query (no longer used).
- prices.js / PlansAndPayment.js: currency follows the domain locale
  (localeToRegion) rather than the node's region tag; cards fall back to BRL
  formatting while the real price is loading or if a pinned id is missing.

The backend arbitrage webhook is intentionally left metadata-based: it fails
safe (never blocks checkout, at worst skips the currency re-correction), so it
needs no hardcoded ids. Verified with a standalone harness (15/15), including
that stray seat-tier and mis-tagged prices are never selected.

(cherry picked from commit 9d4fd9d)
The DB Pro card fell back to a hardcoded 444 (the annual amount) for BOTH the
monthly and annual toggle, so whenever the plan didn't resolve from getPlans the
monthly view showed "444/month" instead of "47/month". The Chatbot card already
did this right (toggleAnual ? 326 : 30).

Surfaces on the international domains against staging's test-mode Stripe, where
the pinned USD ids aren't present so the plan falls back; also any time getPlans
is unavailable. Not a prod-data issue — when the pinned ids resolve, the real
amount is shown and no fallback runs.

(cherry picked from commit 946cd9d)
Make the English (data-basis.org) and Spanish (basedelosdatos.org) sites
consistent by removing Portuguese/Brazil-specific elements, and restore the
previously-hidden spatial coverage feature.

- Consulting banner: gate the dataset-page ServiceHighlightABTest on
  NEXT_PUBLIC_DOMAIN so it renders only on basedosdados.org. Adds
  getDomain()/isBasedosdadosDomain() helpers (build-time domain, works under
  Docker where the hostname is localhost).
- Generated query alias: localize the backend's `dados` table alias per
  locale (pt->dados, en->data, es->datos) via localizeQueryTableAlias(),
  applied to the SQL/Python/R snippets. Word-boundary replace leaves the
  `basedosdados.<dataset>.<table>` project path untouched.
- Spatial coverage (recovered, gated to non-pt):
  * dataset page shows it side-by-side with temporal coverage
    (getDataset now requests spatialCoverageName{Locale}).
  * search result cards show it.
  * search left-column filter box (above Organization), backed by the
    /search spatial_coverages aggregation; hidden on pt.
- Homepage: drop Brazil-specific phrasing in en/es (largest platform in
  Brazil -> a large platform; socioeconomic data from Brazil; IBGE/Brazilian
  municipalities example generalized). Testimonials left intact.

(cherry picked from commit 9b6e2e1)
- chatbot-lp: remove Brazil mentions from the en/es hero + "why" titles.
- DB Pro: fix the Solutions > Exclusive data menu link (/en/bdpro, /es/bdpro
  -> /bdpro), consistent with the other locale-relative links.
- docs: gate the pt-only "Guia Central de Identidade Verbal" sidebar category
  to locale === 'pt', so en/es no longer show an empty Portuguese header.
- FAQ: add en/es content for the chatbot and DB Pro FAQs (40 markdown files
  under content/{chatbot,bdPro}/FAQ/{en,es}). Previously pt-only, so the
  sections rendered empty on en/es. Brazil-specific dataset examples are
  generalized to themes and internal links are relative so they stay on the
  visitor's domain.

(cherry picked from commit 66d887f)
The English site abbreviates Data Basis as "DB" (menu already uses DB
Free/Pro/Orgs), so every "BD Pro" in en was inconsistent — including the
dataset-page "Enable notifications" upgrade tooltip. Replace "BD Pro" ->
"DB Pro" across the en locale files and the en FAQ content (20 occurrences).
pt and es keep "BD Pro" (Base dos Dados / Base de los Datos).

(cherry picked from commit 1df2848)
Follow-up to the "DB Pro" rename: sweep the remaining standalone "BD" ->
"DB" in en content so the org abbreviation is consistent (menu already used
DB). Covers en locale strings (faq, chatbot, transparency, user, blog,
dataset) and en markdown (docs, FAQ, case studies, services), 36
occurrences. Word-boundary match leaves cookie names (userBD, tourBD),
lowercase URLs (bd-pro), and es/pt untouched.

(cherry picked from commit ef84a55)
Each interface language has its own Discord server. Add getDiscordUrl(locale)
(pt -> huKWpsVYx4, en -> tx57ek6zqQ, es -> nNfQYcmrvM) and use it in the
shared Footer, Menu, contact and about-us components, and swap the invite
code in the en/es docs and FAQ markdown. pt links are unchanged.

(cherry picked from commit f6e5e8b)
Add English and Spanish versions of all 39 main FAQ articles (content/FAQ/{en,es}),
preserving frontmatter — question and keywords translated, categories/id/order kept
verbatim as canonical filter keys. English "DB" / Spanish "BD" brand short-forms.

Also localize the /faq category chips: they previously rendered hardcoded
Portuguese labels (incl. "BD Lab"/"BD Edu") in every locale. Wire them to the
faq.json `categories` translations and normalize the en/es label sets.

(cherry picked from commit 40239bd)
Add English and Spanish versions of all 10 dataset usage guides
(content/userGuide/{en,es}), keyed by the same dataset.usageGuide slug so the
dataset-page "User guide" tab now resolves per locale instead of falling back
to null. Frontmatter title/description translated; date/thumbnail/categories/
authors preserved. SQL blocks, BigQuery table paths, and column identifiers
kept verbatim; brand short-forms en "DB" / es "BD".

(cherry picked from commit 94e9250)
- MDXRemote link component now locale-prefixes root-relative internal links
  (e.g. the usage-guide FAQ banner /faq -> /en/faq, /es/faq) instead of always
  resolving to the default (pt) locale. External/anchor/already-prefixed links
  are untouched. Also benefits blog, which shares mdxComponents.
- Move the dataset-header "Spatial coverage" block left (temporal colSpan 2->1,
  spatial 3->4) so it sits closer to "Temporal coverage".

(cherry picked from commit c790fac)
Rewrite 27 usage-guide links from the retired queries-basedosdados and
queries-basedosdados-dev repos to the current basedosdados/pipelines repo
(paths preserved), across pt/en/es.

(cherry picked from commit 4e6d07f)
…ed development branch

(cherry picked from commit 8f3a5ed)
The columns table sourced descriptions from the REST endpoint
/tables/{id}/columns/, which only exposes the Portuguese `description`
and `observations`. The frontend already reads `description${Locale}`
with a fallback to `description`, but those localized keys were never
present, so English and Spanish pages always showed Portuguese.

Enrich the REST payload with `descriptionPt/En/Es` and
`observationsPt/En/Es` fetched from GraphQL (allColumn), merged by
column id/name. ~92% of columns have English/Spanish descriptions in
the backend, which now surface on data-basis.org and
basedelosdatos.org; columns lacking a translation fall back to
Portuguese. GraphQL failures degrade gracefully to the REST payload.

(cherry picked from commit 265ab47)
The English signup consent line read "...of Base dos Dados." Replace
with the established English brand name "Data Basis", consistent with
the rest of the en locale (menu, footer, dataset labels).

(cherry picked from commit 45e7de2)
Include the active i18n locale (pt/en/es, derived from the domain) in the
POST /chatbot/threads body so the chatbot backend persists the thread's
language and answers in it. The Next proxy already forwards the request body,
so no proxy change is needed.

Pairs with basedosdados/chatbot#58 (Thread.language). Backward-compatible: the
current chatbot backend ignores the extra field until that ships.

(cherry picked from commit 7479b99)
…ting domain

The chat interface (pages/chatbot.js + components/organisms/chatbot/*) was
hardcoded Portuguese with no i18n wiring, so it stayed pt on data-basis.org and
basedelosdatos.org. Add getStaticProps + a chatbot 'ui' namespace (pt/en/es) and
route every user-facing string through t(). Also pass window.location.origin as
redirect_origin to the Google login endpoint so OAuth returns to the same
domain (backend change is separate).

(cherry picked from commit c71d655)
The chatbot sidebar and mobile header hardcoded the Base dos Dados (BD)
wordmark, so data-basis.org showed 'BD'. Add a locale-aware BrandLogo
(DB for en, BD otherwise), mirroring the main nav (Menu.js), and use it
in the sidebar (expanded + collapsed) and the mobile header.

(cherry picked from commit b7866bd)
Both chatbot pages now show "Chatbot - <brand>" localized per domain, instead of
the hardcoded "Chatbot - Basedosdados" (app page) and "Chatbot LP - <brand>"
(landing page):
  en → Chatbot - Data Basis
  pt → Chatbot - Base dos Dados
  es → Chatbot - Base de los Datos

- chatbot.json (en/pt/es): head.pageTitle / head.metaTitle change from
  "Chatbot LP - <brand>" to "Chatbot - <brand>".
- chatbot.js: render t("head.pageTitle") in the Head (title + og:title +
  og:description) instead of the literal "Chatbot - Basedosdados".
- chatbot-lp.js already reads head.pageTitle / head.metaTitle, so its title and
  og:title pick up the change with no code edit.

(cherry picked from commit 4bc1783)
A career now counts as active only when it has no end date. On the team
page this means:
- role display lists only active roles (ended roles are hidden);
- people with no active role are dropped from the list;
- sorting ranks people by their active roles only;
- the team filter lists only people with an active career in the
  selected team.

Fixes outdated roles showing on /about-us (e.g. an ended "Líder de
Plataforma" no longer appears).

(cherry picked from commit 9dbcf92)
@mergify

mergify Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

⚠️ The sha of the head commit of this PR conflicts with #1655. Mergify cannot evaluate rules on this PR. Once #1655 is merged or closed, Mergify will resume processing this PR. ⚠️

@rdahis

rdahis commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

Closing — not needed.

These changes are already present in staging under their original SHAs; this branch is a
reconstruction of that same work onto main for the new Git Flow. Merging here would have
produced 8 conflicts, purely because both sides carry the same logical edits under different
SHAs — not because anything is wrong with the branch.

staging is being reset to main shortly and will pick these up from main at that point.

#1655 (→ main) stays open and is the one that matters. The branch is not deleted by
closing this.

@rdahis rdahis closed this Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants