feat: auto-translate blog and memo content via DeepSeek API#87
Merged
Conversation
Adds automatic translation of blog posts and memos to the user's browser language using DeepSeek Chat API, with file-based caching. - lib/translate.ts — server-side engine using DeepSeek Chat API, with file-based caching in data/translations/ - lib/translate.shared.ts — client-safe locale helpers (shouldTranslate, localeToLabel) importable by browser components - app/api/translate/route.ts — POST /api/translate endpoint, validates inputs (max 50K chars), returns translated text - hooks/useTranslation.ts — React hook that auto-detects locale, calls the translate API, and caches in sessionStorage - components/TranslateContent.tsx — alternative wrapper component - components/BlogPostContent.tsx — title + content auto-translated, with translation indicator and show-original toggle - components/MemoCard.tsx — memo content auto-translated, with toggle - components/BlogCard.tsx — card listing titles auto-translated - .gitignore — ignore data/translations/ cache - .env.example — documents DEEPSEEK_APIKEY requirement Chinese readers (zh/zh-TW/zh-HK) see the original content (no-op). Translation gracefully falls back to identity when API key is unset.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Extract DeepSeek system prompt into buildTranslationPrompt() - Extract fetch call into callDeepSeek() — one function, one concern - Remove dead code: _translateFn variable, setTranslateFn export - Replace section-marker comments with structural code ordering - Extract magic 8000 into named MAX_SINGLE_REQUEST_CHARS constant - Rename _cacheDir → cacheDir, getCacheDir → ensureCacheDir - Inline redundant variable declarations for clarity
- Wrap sessionStorage.getItem() in try-catch in the second useEffect to prevent the effect from silently crashing during SSR - Add actuallyTranslated flag to useTranslation hook so the UI only shows the indicator when a real translation occurred - Accept DEEPSEEK_API_KEY as an alternative env var name for compatibility with Vercel conventions - Update .env.example to document both accepted names
TranslateContent duplicated the entire useTranslation hook (fetch, sessionStorage caching, hashText) and was imported nowhere. The hook is the single source of truth used by BlogPostContent, MemoCard, and BlogCard.
- Correct the normalizeLocale doc comment (engine is DeepSeek, not Google Translate) and describe what it actually does. - Merge the duplicate zh-HK/zh-TW branches in normalizeLocale. - Drop the redundant second Chinese-locale check in shouldTranslate; normalizeLocale already collapses every variant into CHINESE_LOCALES. Verified behaviourally identical across 15 locale inputs.
Collapse the separate reset effect, translate-trigger effect, and shouldFetch helper into a single resolution effect plus a stable translate callback. Behaviour is unchanged: Chinese/empty content shows the original, a cached translation is served immediately, and a cache miss fetches while showing the original meanwhile. Extract readSessionCache/writeSessionCache so every sessionStorage access stays wrapped in try-catch (preserving the SSR-safety fix) instead of being re-implemented inline in three places.
BlogPostContent and MemoCard had near-identical copies of the
translation status row (toggle button + auto-translated badge +
spinner). Extract a single TranslationIndicator that reproduces both
the verbose article ('full') and compact card ('compact') variants,
and owns the 'show only when in-flight or translated' guard.
- translate.shared: normalizeLocale, shouldTranslate, localeToLabel, getTranslatableLocales across Chinese variants and unknown codes. - translate engine: blank no-op, cache miss calls DeepSeek and caches, cache hit skips the API, and missing API key falls back to the original text. Uses an isolated temp cache dir and a mocked fetch.
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.
Summary
Adds automatic translation of blog posts and memos to the user's browser language using DeepSeek Chat API, with file-based caching.
Changes (10 files, +856/-131)
lib/translate.tsdata/translations/lib/translate.shared.tsapp/api/translate/route.ts/api/translate— accepts{text, targetLocale, isMarkdown}hooks/useTranslation.tscomponents/BlogPostContent.tsxcomponents/MemoCard.tsxcomponents/BlogCard.tsx.gitignoredata/translations/cache.env.exampleDEEPSEEK_APIKEY/DEEPSEEK_API_KEYHow it works
next-intldetects the user's browser locale viaAccept-LanguageuseTranslationhook calls/api/translate, which sends content to DeepSeek Chat with a markdown-preserving system promptdata/translations/(file) and client-side insessionStorageClean-code applied
buildTranslationPrompt,callDeepSeek,shouldFetch)MAX_SINGLE_REQUEST_CHARS = 8_000)setTranslateFn,_translateFn)Fixed
sessionStorage.getItem()in the translate-trigger effect was not wrapped in try-catch, causing the entire effect to silently crash during SSR — translation never startedactuallyTranslatedflag instead ofneedsTranslation— won't falsely claim "Auto-translated" when API key is missing or API returns unchanged textDEEPSEEK_APIKEYandDEEPSEEK_API_KEYenv var namesSetup
Set either
DEEPSEEK_APIKEYorDEEPSEEK_API_KEYin the Cofe Vercel project's Preview environment variables. Falls back gracefully (no-op) if unset.Verification