Repository navigation
feat(i18n): Simplified Chinese across the site - #97
Open
Vasanthdev2004 wants to merge 19 commits into
Open
Vasanthdev2004 wants to merge 19 commits into
Vasanthdev2004 wants to merge 19 commits into
Conversation
The site can now read in Simplified Chinese (zh-CN). English stays the source text and the fallback: a Chinese message that is missing reads in English, never as a key. - Locale: the NEXT_LOCALE cookie the new language switch sets, else the browser's Accept-Language (any Chinese tag reads Simplified Chinese). The URL is the same in every language, and <html lang> follows the locale. - Library: use-intl (the core of next-intl) and its 8 small pure-JS dependencies. next-intl itself pulls in @swc/core and @parcel/watcher native binaries for every platform for its extraction tooling, which the site does not need. useTranslations in client components; getTranslations from @/i18n/server in server components. - Messages: one JSON file per area and language under src/i18n/messages, typed from the English files, so a key missing from English fails tsc. messages.test.ts checks every Chinese key exists in English with the same placeholders, that the translated areas are complete, and that no message uses an em dash. - Translated: header and phone menu (navigation, search, bridge, notifications, theme, language, live visits), wallet chip and account menu, the search dialog (Chinese and English queries both find pages and actions), and the footer. - Fonts: Chinese falls back to the platform's CJK face after Geist. - src/i18n/README.md: how to add text, Chinese style, and the glossary. Tests that read English text from component source now check the message key in the source and the text in the English messages.
…weight Token and profile share cards render with Geist, which has no CJK glyphs. next/og already falls back to a font it fetches itself, but in a regular weight, so a Chinese name sat thin beside the bold Latin title. The cards now load a Noto Sans SC (Han, kana) or Noto Sans KR (Hangul) subset at the title's weight, holding only the characters the card draws (Google Fonts text= subsets, a few KB, cached for a day). Latin names fetch nothing new.
The switch used to set the cookie and refresh the page, so nothing changed until the server had rendered the whole page again. Now every client component re-renders in the new language at once, and the refresh only brings the server-rendered parts (the footer, page headings) along after. - English ships with the client code (messages/en.ts); a Chinese page sends only the Chinese translation, never the English text again. Switching to Chinese loads that translation as one chunk the first time. - clientTranslator (src/i18n/client.ts) words text built outside a component, such as errors from lib functions, in the reader's language. - Message areas registered for the rest of the site: token, launch, info, account, bridge, errors.
Chinese pages get a Chinese tab title and meta description. OpenGraph and X previews stay English: link crawlers send no language, so every share shows English anyway. A test keeps the English messages equal to SITE_TITLE and SITE_DESCRIPTION.
Translated, in the "hero" and "posts" namespaces: - Home hero: chain chip, headline, copy (the brand stays a linked plain word), both ways in, the proof chips and the not-configured note. - The "Why it's free" card and the home sidebar's aria-label. - Posts: the token page's comments (composer, reply and report controls, report reasons, toasts, empty and muted states), the home posts widget (title, tags, relative times through useAgo, empty state, show/hide) and the /feed intro. - Failure reasons from validateBody, friendlyError and the posts API are mapped to messages in Posts.tsx; the libs keep their English, and any other text, such as a wallet's own error, reads as it came. posts-i18n.test.ts keeps the map in step with the reasons the code gives. English reads as before. Judgment calls: the headline is two whole messages with the highlight as a rich tag; on the token page a post is 评论 (the section is "Comments"), in the feed 动态; "on $PEPE" reads 关于 $PEPE; Whale is 巨鲸; the /feed title reads 每个代币背后的声音. The token-page tag chip now shows the same sentence-case labels as the home badge (CSS uppercases it, so it looks the same). Left in English: page metadata, wallet and library error text, the signed messages and the report reason codes they carry, and components/sections/CommunityFeed.tsx (the body of /feed), which is outside this change.
The home page's live panel, the Just launched rail, the phone's Launch
pill and the live activity toasts now read in Simplified Chinese.
English reads exactly as before: rendered side by side with the
previous components, every view and hover card gives the same HTML.
- Translated: the live panel's heading, summary line, view switch,
legends, time axis, empty states, flow meter and hover cards in the
bubbles, river and list views; the Latest column; Just launched; the
Launch pill (common.launchToken); activity toasts and the toast
card's status, link, dismiss button and queue count.
- riverSummary, bubbleSummary and bubbleWindowLabel take the reader's
words (a translator over activity.river.summary or
activity.bubbles.summary) instead of building English. Each count is
a message of its own, so Chinese orders the sentence its own way.
Their tests assert the English and the Chinese.
- Relative times use useAgo. Hand-made English plurals are ICU plurals;
counts that were grouped (toLocaleString) stay grouped in Chinese.
- An activity toast is worded when it arrives, through an Effect Event,
so a language switch never re-subscribes the feed.
Judgment calls: "River" keeps its picture (河流); the river legend says
中线 (centre line) because 线上 alone also reads as "online"; "in this
window" is 此时段内; "Buy {amount} of {symbol}" is
"买入价值 {amount} 的 {symbol}". The exact UTC timestamps in tooltips
stay English (toUTCString), as on every other page.
The home page's market list now reads in Simplified Chinese: the launch
list (sorts, chain and quick filters, layout, status line, empty and
error states, paging), its rows and cards, the live chip, the watchlist
tab and star button, the change chip's tooltip, and the Trending board
(headings, ranking rule, leader and runner cards, tapes, streaks,
lift-off chips, screen-reader sentences). English is unchanged: every
English case rendered with the original components reads the same.
- Messages live in market.json, nested by component. Sentences around a
figure or a screen-reader span are whole messages with tags (t.rich),
and English plurals are ICU plurals.
- Lib text is translated in the components; no lib file changed: quick
filter labels and titles by key, capDisplay's "no USD price"
(localizedCap), the live chip from liveChipParts' tier and count
(useLiveWords), changeWords, and the lift-off chips, reworded from
each hit's counts after applyPlan.
- ChangeChip is now a client component, so its tooltip follows the
language on the server-rendered token page too. Chinese puts the
context first ("自发行以来 12%").
- The board's screen-reader separators are messages, so Chinese gets
full-width "," and "。".
- Judgment calls: Age 时长, Quiet launches 冷清的发行, launcher 发行者,
"3 wallets this hour" 本小时 3 个钱包交易, all-time volume 累计,
Watch/Watching 加自选/已自选, unlisted asset 未收录资产, website
的网站 (not 官网, which claims "official").
- Still English: the UTC date tooltips (toUTCString), the quote and
unlisted-pair badges, and context strings passed in by other areas.
Tests that read English from these components now check the key in the
source and the text in the English messages; the change chip, the
unpriced-cap mark and the lift-off chip are also checked in Chinese.
The token page's trading surfaces read in Simplified Chinese; English
reads exactly as before. Messages live in messages/{en,zh-CN}/trade.json,
nested by component (panel, chart, source, mobileBar).
- Trade panel: buy/sell and pay-with toggles, the ETH route line, the
unlisted-pair warning, pay and receive wells, amount presets, quote
details (price impact, fees, minimum received, slippage), every button
and progress state, the price-impact confirmation, the TWIG hint, the
done line, the sell-it-all summary, the custody note, the revert error
and the success toast. Every value sent to the chain is unchanged.
- Chart: toolbar, view modes, fullscreen, the USD note, readout and
range context, OHLC/volume/trades legends, empty and error overlays,
volume and my-trades chips, keyboard help and announcements, and the
reference-line labels and trade markers drawn on the canvas. Dates
(readout, crosshair label, time axis, announcements) follow the
reader's language ("10月04日 08:05 UTC"); numbers keep en-US formatting.
- Chart source toggle (on-chain / GeckoTerminal) with its fallback
reasons, and the phone trade bar.
- chart-terminal: chartSpanParts replaces chartSpanLabel, so the
"45m in view" span is worded through common.time.
Judgment calls: interval chips stay 1m/5m/15m/1h/4h/1D (compact codes,
as Chinese DEX tools show them); the OHLC legend reads 开/高/低/收;
K线 is written without inner spaces, per the glossary; the connect
button reuses common.connectWallet. Left to their own areas: the
trading-fee value (worded by the token page) and wallet and contract
errors from lib/errors.ts.
Tests that read English from these files now check the message key in
the source and the text in the English messages.
The bridge dialog and the wallet/contract error wording the whole site
shares now read in Simplified Chinese. English output is unchanged.
- Bridge dialog: every step, field, quote line, fee-limit notice,
approval and transfer status, warning, aria-label and screen-reader
text ("bridge" messages).
- Errors raised outside render (lib/bridge client, approval, storage,
preflight and types, plus useBridge's handlers and effects) are worded
with clientTranslator when raised; the hook's render-time text uses
useTranslations. Callers, the hook's return shape and every effect and
callback dependency list are unchanged.
- Approval health advice is worded when read, so advice already on
screen, including the shared "unavailable" advice, follows a language
switch.
- The bridge API keeps answering in English (agents read it too); the
dialog shows its known messages in the reader's language through
bridgeServiceMessage, and a test keeps bridge.service in step with
the API's sentences.
- friendlyError words its own messages through the "errors" messages
with the same signature and English, so every caller reads Chinese.
Judgment calls: useBridge.ts was not on this area's file list but holds
about 50 of the dialog's errors, so its wording is translated in place
with no logic change. Numbers inside messages are passed as strings so
English keeps its exact digits. viem/RPC library text and provider
details pass through unchanged, as data.
Translated, in the "account" namespace:
- /me: metadata, page intro, its loading label, and the dashboard: the
connect prompt, figures, launches, holdings and trades tabs, empty
states, errors, and the collect and edit toasts (relative times through
useAgo).
- /u/<username>: metadata, joined date, stats, season points, the chain
note and recent trades (relative times through common.time).
- /leaderboard: metadata, the teaser, the boards, days left and end dates;
the season card on /me and the your-standing strip.
- Profiles: the profile chip on /me, the create, edit and verify sheet
with its delete confirmation, and the verified tick and explorer labels.
- The connect-wallet dialog: wallet hints, the no-wallet note and connect
errors, through clientTranslator in lib/wallet-connectors.ts (the chain
list in the wrong-network error is joined the reader's way).
- Sheet's close button, the theme toggle's sr-only text, and the unused
navigation-shell helpers (they reuse the header's labels).
Failure reasons from validateProfile, xHandle, the profile API,
friendlyError and the sheet's own fallbacks stay English in the libs and
the API; the sheet shows each through its message (profile/reasons.ts),
and profile-i18n.test.ts keeps the two in step. The points "why" line,
also English in /api/points, is read back part by part (points/why.ts,
why.test.ts). Signed messages and the X post text are untouched. Dates on
the server pages format in the reader's language ("en" reads as "en-US"
did).
Judgment calls: collecting fees is 收取; Scout is 发掘者; "Me" is
我的面板; ProfileQueue stays English with the rest of /admin; the
username and handle placeholders stay as format examples.
The token page reads in Simplified Chinese: the hero (back link, pair, age, market cap, holders and trades, share), the stats row, the Proof panel and its links, the Trades, Holders and About & contracts tabs with every pair note, the About card, fee collection (with its toasts and errors), the creator's edit sheet, the pair and fee badges, the copy chip, the home hero's live line, the loading state and the page metadata. English reads as before. - Trades, holders, proof and about are async server components on getTranslations; relative times use common.time. - proofFacts takes the reader's locale. trustNoteFacts gives the holder notes as data so the panel words them; the holders API keeps its English text. - The pair badges and FeeChip are client components now (they render on the server page and in client lists), so feeModeOf moves to lib/launchpad/creator.ts, where the page reads it on the server. - validateEdit words its errors with clientTranslator: the edit sheet shows them in the reader's language, the edit API still answers in English. The signed edit message is unchanged. - The page words capDisplay's "no USD price" mark itself. Judgment calls: the prefilled X share text is translated; UTC dates (toUTCString) are left as they are, like other figures.
Translated, in the "info" namespace: - How it works (/rules): every section, the points rules (their numbers stay placeholders filled from RULES), the risk questions, the contract registry and its labels, and the page metadata. - About and Agents with their metadata. On Agents only the prose moves: code blocks, endpoints, parameter names and addresses stay in the page, and the identifiers inside <code> read the same in both languages (agents.test.ts checks every span). The code block's copy controls too. - The /feed body (CommunityFeed): filters, counts, empty and error states, role chips, times through useAgo. - /base, /robinhood and /arc with their metadata. Their words are messages now; chainLanding.test.ts keeps the English equal to CHAIN_LANDING, the copy it sizes for search. - The 404, route error and root error pages, and the loading labels. The root error page renders outside the providers, so it reads NEXT_LOCALE from document.cookie (English on the server and while hydrating, and without the cookie) and loads the Chinese chunk only for a Chinese reader, with <html lang> to match. English renders as before (every page compared before and after), with one exception: the two error bodies joined their clauses with an em dash, which messages.test.ts forbids in any message, so they now read "Your funds are on-chain; this is only the site." Judgment calls: "live" and "quiet launches" in the ranking question are 活跃 and 冷清的发行, the board's own labels; the token page's Collect button is 领取 and its Conversation tab 讨论; the screen-reader "Post by" is 发布者:; Regulation S, Robinhood Stock Tokens and "Shell · cast" stay as named.
Community posts read 帖子 everywhere (header, footer, search, the feed pages, the watchlist); 动态 stays for trading activity and the live feed. Collecting fees reads 领取 on the dashboard as it already did on the token page and in the guide, with 提取 kept for a beneficiary's claim. The glossary in app/src/i18n/README.md now lists these terms. The error pages read their words from the messages, so the English helpers in lib/error-copy.ts that used to supply them are removed; the test pins the English messages instead. sanitizeDigest stays.
Translated, in the "launch" namespace:
- /launch: tab title and description (generateMetadata; canonical and
share card unchanged), heading, intro and the loading state's label.
- The launch form: chain cards, quote picker, the TWIG and GITLAWB notes
(the badge sits inside the sentence as a rich tag), the stock picker
and its issuer disclaimers, every label, hint, placeholder and
validation message, the fee split editor, the first buy, the step
summary, the console tiles, the submit button's states, the progress
line (the explorer link stays around the hash) and the toasts.
- The fee card, the live preview, the image and banner picker, the token
page's launch receipt (now an async server component), and the home
hero's locker drawing, its new-launch caption and the totals strip.
- lib/launchpad/recipients.ts words the split errors and the share
summary through clientTranslator("launch"); outside the browser it
reads English, so its tests are unchanged.
- Failure reasons from friendlyError, the metadata and image routes and
the form's own fallbacks are mapped to messages where they are shown;
anything else, such as a wallet's own error, reads as it came. Tests
keep each map in step with the reasons the code gives.
English reads as before: the rendered text of /launch, /, /rules and a
token page matches the previous code. Judgment calls: beneficiary is
受益人 and fee recipients 接收方; the name placeholder Clear Sky reads 晴空
while SKY and the preview's TICKER stay; the totals strip joins the chain
names per language (Base、Robinhood Chain 和 Arc); "The first ETH buys"
reads 最初的 1 ETH.
friendlyError already returns its reasons in the reader's language (the errors area). The launch form, the posts UI and the profile sheet also kept their own copies of those reasons, mapped from friendlyError's old English; in Chinese the map never matched, so the copies were dead and could drift. They are removed from the three maps and their messages, and the tests now check that friendlyError's words are not mapped again.
Conflicts: the command palette keeps its translated words and gains the Earn page entry (search.pages.earn); the dashboard keeps its translated states with the Earn card under the season card; friendlyError's new "insufficient liquidity" reason is a message (errors.insufficientLiquidity).
The /me Earn card, its sheet and the confirmation toast read from a new earn message area. lib/earn and useEarn keep their English reasons (the server's terms check sends its reason already worded); EarnPanel shows each one through explainEarn (components/earn/reasons.ts), which maps the English to its message with the values it carried, including the reason inside a paused-deposit sentence. Anything else, such as a wallet's own error, reads as it came. earn-i18n.test.ts checks that every reason message is one the code gives and reads back exactly in English and in Chinese. earn-ui.test.ts now normalises line endings, so its patterns hold on a Windows checkout, and checks the risk copy in both languages.
|
Important Review skippedToo many files! This PR contains 222 files, which is 72 over the limit of 150. To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to Team to raise the limit. This review is too large to run within your organization's remaining usage spending cap. Raise or remove your spending cap in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters: 1
📒 Files selected for processing: 222
You can disable this status message by setting the
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The site now reads in English or Simplified Chinese (
zh-CN), on every public page: the header and footer, the home page (hero, Trending, live activity, the market list), the token page (trade panel, chart, fees, posts), the launch flow,/me(including Earn), profiles, the leaderboard, the bridge,/rules,/about,/agents, the chain pages, the error pages and the 404, plus tab titles and descriptions.NEXT_LOCALEcookie, set by the switch in the header. Without it,Accept-Languagedecides: any Chinese tag reads Simplified Chinese, anything else English.<html lang>follows the language.How
use-intl4.14.9, the core of next-intl, pinned to an exact version. next-intl itself pulls in native@swc/coreand@parcel/watcher. This is a new dependency, so pr-guard will need @kevincodex1's approval.app/src/i18n/messages/{en,zh-CN}/.tsc.messages.test.tschecks that every Chinese key exists in English, that placeholders match, and that no message uses an em dash.friendlyErrorwords its reasons in the reader's language.app/src/i18n/README.md.Stays in English, on purpose
llms.txt, the admin page and share-card copy stay English too./agents.Worth a look
earn-ui.test.tsnow normalises line endings. It failed on Windows checkouts because it matched\nagainst CRLF files.lib/earnand the server's terms check stay in English in code and are matched to messages when shown.lib/error-copy.tswere dead and are gone.sanitizeDigeststays.Checks
npm test: 1304 tests, 1293 pass, 0 fail, 11 skipped. Main skips 10 of these; the 11th is Earn's live-vault test.tscis clean.eslint: 0 errors. The 5 warnings are the existing<img>warnings in the share-card routes.next buildpasses.<html lang>and tab title.Commits
79d254dfeat(i18n): English and Simplified Chinese, starting with the site shell7b86b02feat(og): draw Chinese, Japanese and Korean names in the cards' bold weightbd081b5feat(i18n): switch language without waiting for the server325101achore(i18n): register the trade message area974359ffeat(i18n): tab title and description in the reader's languagec368a56feat(i18n): home hero and posts in Simplified Chinesecec2abcfeat(i18n): live activity in Simplified Chinese02a47fbfeat(i18n): market list in Simplified Chinesef3920defeat(i18n): trading panel and chart in Simplified Chinesefde330afeat(i18n): bridge and shared error messages in Simplified Chinese8045d3efeat(i18n): dashboard, profiles and wallet picker in Simplified Chinese0d3836bfeat(i18n): token page in Simplified Chineseeb09f20feat(i18n): info, chain and error pages in Simplified Chinesed04a7dafix(i18n): one Chinese word per idea across the site6e2a142feat(i18n): launch flow in Simplified Chinesed377140fix(i18n): word wallet and contract errors once, in lib/errors.ts6ae7c26fix(i18n): /feed tab title and description in the reader's language0fbaea8feat(i18n): Earn in Simplified Chinese