Files
SnapOtter/apps/docs/zh-TW/guide/translations.md
T
SnapOtterandGitHub 4963ab3bbd feat(docs-i18n): translate all documentation into 20 languages
All 181 docs markdown files translated into 20 languages (apps/docs/<locale>/**). Companion to the i18n code PR; admin-merged because the file count exceeds GitHub's per-PR CI trigger limit. Validated by pnpm i18n:check (all surfaces, 0 stale/missing) and a clean all-locale docs build.
2026-07-11 13:52:47 +08:00

9.3 KiB
Raw Blame History

description, i18n_source_hash, i18n_provenance, i18n_output_hash
description i18n_source_hash i18n_provenance i18n_output_hash
SnapOtter 支援的 21 種語言,以及如何使用受 TypeScript 強制檢查的 i18n 系統來建立或改善翻譯。 55837d9fdaef human c06a7448962a

翻譯指南

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

支援的語言

Code Language Native Name Direction
en English English LTR
zh-CN Chinese (Simplified) 简体中文 LTR
zh-TW Chinese (Traditional) 繁體中文 LTR
ja Japanese 日本語 LTR
ko Korean 한국어 LTR
es Spanish Español LTR
fr French Français LTR
it Italian Italiano LTR
pt-BR Portuguese (Brazil) Português (Brasil) LTR
de German Deutsch LTR
nl Dutch Nederlands LTR
sv Swedish Svenska LTR
ru Russian Русский LTR
pl Polish Polski LTR
uk Ukrainian Українська LTR
ar Arabic العربية RTL
tr Turkish Türkçe LTR
hi Hindi हिन्दी LTR
vi Vietnamese Tiếng Việt LTR
id Indonesian Bahasa Indonesia LTR
th Thai ไทย 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() 載入目前使用中的語系,讓主要套件保持精簡。

在元件中使用翻譯

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 並複製

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

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

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

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

3. 翻譯字串

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

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

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

5. 驗證

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. 將新鍵加入所有語系檔案(暫時以英文作為備援)

設定

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

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

檔案參考

File Purpose
packages/shared/src/i18n/en.ts 英文字串(參考語系,約 1500 個鍵)
packages/shared/src/i18n/index.ts SUPPORTED_LOCALESloadTranslations()、型別匯出
packages/shared/src/i18n/<locale>.ts 各語言的翻譯檔案
apps/web/src/contexts/i18n-context.tsx I18nProvideruseTranslation() hook
apps/web/src/lib/format.ts format()plural()formatFileSize() 輔助函式
apps/api/src/routes/config.ts GET /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。 :::