This page was machine-translated. Spotted a mistake?Help improve it.
Skip to content

翻譯指南

SnapOtter 內建支援 21 種語言。其 i18n 系統採用輕量的自訂執行環境,具備受 TypeScript 強制檢查的語系完整性,以及動態程式碼分割。

支援的語言

CodeLanguageNative NameDirection
enEnglishEnglishLTR
zh-CNChinese (Simplified)简体中文LTR
zh-TWChinese (Traditional)繁體中文LTR
jaJapanese日本語LTR
koKorean한국어LTR
esSpanishEspañolLTR
frFrenchFrançaisLTR
itItalianItalianoLTR
pt-BRPortuguese (Brazil)Português (Brasil)LTR
deGermanDeutschLTR
nlDutchNederlandsLTR
svSwedishSvenskaLTR
ruRussianРусскийLTR
plPolishPolskiLTR
ukUkrainianУкраїнськаLTR
arArabicالعربيةRTL
trTurkishTürkçeLTR
hiHindiहिन्दीLTR
viVietnameseTiếng ViệtLTR
idIndonesianBahasa IndonesiaLTR
thThaiไทยLTR

語言偵測的運作方式

SnapOtter 採用三層解析順序:

  1. 使用者偏好 - 儲存在 localStorage("snapotter-locale"),並在已驗證身分時同步至使用者設定
  2. 瀏覽器自動偵測 - 以 BCP 47 前綴比對走訪 navigator.languages 陣列
  3. 執行個體預設值 - 管理員的 DEFAULT_LOCALE 環境變數(從 GET /api/v1/config/locale 取得)
  4. 英文備援 - 永遠可用

使用者可從以下位置變更語言:

  • 頁尾的地球選擇器(桌面版,永遠可見)
  • 登入頁面的語言選擇器(登入前)
  • 設定 > 一般區段(每位使用者的偏好)
  • 行動版側邊欄的語言下拉選單
  • 設定 > 系統區段可設定整個執行個體的預設值(僅限管理員)

翻譯的運作方式

所有 UI 字串都放在 packages/shared/src/i18n/。參考檔案是 en.ts,它匯出一個具型別的物件,包含應用程式使用的每個字串(約 1500 個鍵)。其他語言則是分開的檔案(例如 de.tsfr.ts),匯出相同的結構。

TranslationKeys 型別使用 DeepStringRecord 接受任意字串值,同時強制檢查鍵結構。TypeScript 會在編譯時期抓出任何翻譯檔案中缺少的鍵。

執行階段只會透過動態 import() 載入目前使用中的語系,讓主要套件保持精簡。

在元件中使用翻譯

tsx
import { useTranslation } from "@/contexts/i18n-context";
import { format, plural } from "@/lib/format";

function MyComponent() {
  const { t, locale, setLocale } = useTranslation();
  
  return (
    <div>
      <h1>{t.common.settings}</h1>
      <p>{format(t.settings.people.deleteConfirm, { username: "admin" })}</p>
      <p>{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}</p>
    </div>
  );
}

貢獻翻譯

我們歡迎直接提交翻譯 PR。你可以改善既有的語系,或新增一個語系。

若要在不提交程式碼的情況下回報翻譯錯誤,請開一個 GitHub Issue,並附上語言、錯誤的字串以及建議的修正。

TIP

翻譯 PR 不需要事先核准。Fork 儲存庫、進行你的變更,然後開一個 PR。完整的 PR 流程與 CLA 要求請參閱 Contributing Guide

如何建立或更新翻譯

1. Fork 並複製

bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install

2. 複製參考檔案(僅限新語言)

若你是在改善既有的翻譯,請略過此步驟。

bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts

3. 翻譯字串

開啟你的新檔案並翻譯每個字串值。物件結構與鍵請保持完全相同。

ts
import type { TranslationKeys } from "./en.js";

export const xx: TranslationKeys = {
  common: {
    upload: "Your translation here",
    // ... translate all entries
  },
  // ... translate all sections
} as const;

規則:

  • 不要翻譯物件的鍵,只翻譯字串值
  • 在結尾保留 as const
  • ./en.js 匯入 TranslationKeys 並為你的匯出加上型別
  • {variable} 佔位符保持原樣
  • 陣列(rotatingPhrasesprogressMessages)必須有相同數量的項目
  • 不要翻譯:SnapOtter、JPEG、PNG、WebP、EXIF、API 以及其他技術術語

4. 註冊語系(僅限新語言)

packages/shared/src/i18n/index.ts 中將你的語系加入 SUPPORTED_LOCALES

ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },

5. 驗證

bash
pnpm typecheck    # catches missing or mistyped keys
pnpm lint         # formatting check
pnpm dev          # manually verify strings appear correctly

6. 提交

main 開一個 PR,標題類似 feat(i18n): add Swedish translationfix(i18n): correct German typos。CLA 機器人會在你第一次貢獻時要求你簽署。

新增翻譯鍵

當你新增需要新 UI 字串的功能時:

  1. 先將新鍵加入 en.ts(參考檔案)
  2. 執行 pnpm typecheck - 任何缺少新鍵的語系檔案都會失敗
  3. 將新鍵加入所有語系檔案(暫時以英文作為備援)

設定

透過環境變數設定執行個體的預設語言:

yaml
DEFAULT_LOCALE: "de"  # German as the default for all new users

檔案參考

FilePurpose
packages/shared/src/i18n/en.ts英文字串(參考語系,約 1500 個鍵)
packages/shared/src/i18n/index.tsSUPPORTED_LOCALESloadTranslations()、型別匯出
packages/shared/src/i18n/<locale>.ts各語言的翻譯檔案
apps/web/src/contexts/i18n-context.tsxI18nProvideruseTranslation() hook
apps/web/src/lib/format.tsformat()plural()formatFileSize() 輔助函式
apps/api/src/routes/config.tsGET /api/v1/config/locale 公開端點

翻譯網站、文件與 API 參考

上述的 21 種語言支援涵蓋的是應用程式。公開網站(snapotter.com)、這個文件網站以及 REST API 參考同樣會翻譯成全部 21 種語言,由另一條經過雜湊把關的流程處理,該流程重複使用 packages/shared/src/i18n 中相同的工具名稱與描述,因此各處的術語都保持一致。

預設為機器翻譯

網站與文件上的每個非英文頁面在第一輪都是機器翻譯的(由 Claude Code 工作階段完成,而非第三方服務),並帶有一個小巧、可關閉的橫幅來說明這一點,還附上回到本頁的連結。這是刻意的:它能快速且誠實地推出全部 21 種語言,接著邀請社群去精修最重要的頁面。機器翻譯能傳達意思;人工審閱則讓文字讀起來自然。

流程如何決定要翻譯什麼

每個可翻譯的英文原文單元都會被雜湊,且該雜湊值會與其翻譯一併儲存。每次執行時,流程會:

  • 翻譯任何尚未有翻譯的單元,
  • 略過任何儲存的雜湊值仍與英文原文相符的單元,
  • 機器單元的英文原文變更時,重新翻譯它,
  • 當經人工精修的單元的英文原文變更時,將其標記為 stale(需要審閱),而非覆蓋你的成果。

透過 PR 精修網頁翻譯

你精修網站、文件或 API 參考翻譯的方式,與改善應用程式語系相同:編輯產生的檔案並開一個 PR。

  1. 找到你語言的產生翻譯:
    • 網站 UI 字串:apps/landing/src/i18n/<locale>.json
    • 某個文件頁面:apps/docs/<locale>/**.md
    • API 參考:apps/api/src/openapi.<locale>.yaml
  2. 編輯文字。程式碼、連結、{placeholders} 以及任何 ⸤I18N…⸥ 標記都請保持原樣;流程的驗證器會拒絕丟失或重新排序它們的翻譯。
  3. 開一個 PR。編輯一個單元會將其來源標記從 machine 翻轉為 human,因此流程在之後的執行中絕不會覆蓋它。若英文原文之後變更,你的單元會被標記為 stale 以供審閱,而不是被靜默取代。

若要在不提交程式碼的情況下回報翻譯錯誤,請開一個 GitHub Issue,並附上頁面 URL、語言、錯誤的文字以及你建議的修正。

TIP

翻譯流程由維護者執行;你不需要 API 金鑰即可貢獻。只要編輯產生的檔案並開一個 PR。流程如何運作請參閱 scripts/i18n/README.md