Files
Shadowbroker/CONTRIBUTING.md
T
Shadowbroker e3297e9bc0 i18n: add language toggle, neutrality policy, and codeowner gate (#238)
PR #226 landed the i18n infrastructure and Chinese (zh-CN) translations.
This follow-up adds the safeguards that make accepting community
translations sustainable without exposing the project to subtle
state-aligned framing in future translation PRs.

Changes:

  frontend/src/i18n/index.tsx (renamed from .ts)
    - Add LOCALES registry: a single source of truth for available
      languages and their NATIVE display names ("English", "中文 (简体)").
      Adding a new language is now a one-entry change here plus a
      JSON file.
    - Add isLocale() guard so an unknown value in localStorage falls
      through to navigator.language detection instead of corrupting
      state.
    - File renamed to .tsx because it contains JSX. Next.js tolerated
      JSX in .ts but Vite/Oxc (used by vitest) does not.

  frontend/src/components/SettingsPanel.tsx
    Add a UI language picker to the Settings header — a small <select>
    populated from LOCALES. Users no longer need the dev console to
    switch languages. Locale change remains 100% client-side
    (localStorage), no network call, no telemetry.

  CONTRIBUTING.md (new)
    Documents the translation-neutrality requirement that applies
    symmetrically to all source countries:
      - Translations must be technically faithful to the English source.
      - Substitutions aligned with state propaganda from ANY country
        (PRC, Russia, US, EU, etc.) will be rejected.
      - The test is: "would a translator working strictly from the
        English source produce this rendering?"
    Also explains how translation PRs are reviewed and how to add
    a new language.

  .github/CODEOWNERS (new)
    Auto-requests maintainer review on:
      - /frontend/src/i18n/  (translation safety)
      - /backend/auth.py, /backend/routers/wormhole.py,
        /backend/services/mesh/, /backend/services/fetchers/
        (the same paths recent security audits flagged as sensitive)
      - /.github/workflows/, /.gitlab-ci.yml, /docker-compose*.yml,
        /helm/  (build/deploy)
      - /CONTRIBUTING.md, /.github/CODEOWNERS  (policy itself)

  frontend/src/__tests__/i18n/i18nProvider.test.tsx (new, 8 tests)
    Locks in the i18n contract:
      - LOCALES has both en and zh-CN with non-empty native labels
      - Default English when navigator is English
      - Auto-detect zh-CN when navigator language starts with "zh"
      - localStorage preference overrides auto-detect
      - setLocale persists to localStorage
      - Unknown stored locale falls back to auto-detect
      - Renders a real zh-CN translation (catches large-scale
        translation removal in future PRs)
      - Missing key falls back to the key itself

  Note: i18n/index.tsx, the language toggle UI, the translation
  policy, and the test suite together form a defense-in-depth setup.
  The structural safety guarantee (no network calls, static JSON
  bundled at build) is intact; this PR makes the social contract
  around translations explicit and enforceable via branch
  protection on CODEOWNERS-marked paths.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 01:48:24 -06:00

76 lines
4.9 KiB
Markdown

# Contributing to Shadowbroker
Thank you for taking the time to contribute. This document covers things specific to this project — for general open-source contribution etiquette, see the GitHub docs.
---
## Code contributions
1. Fork the repo on GitHub (`bigbodycobain/Shadowbroker`) or GitLab (`bigbodycobain/Shadowbroker` mirror).
2. Make your changes on a feature branch.
3. Run the local test suite:
- Backend: `pytest backend/tests/`
- Frontend: `cd frontend && npx vitest run`
4. Open a Pull Request against `main`.
CI runs on every PR. If CI fails, that's blocking — please push fixes rather than asking for it to be merged anyway.
---
## Reporting security issues
Do **not** file security issues as public GitHub issues. Email the maintainer or use a private security advisory on GitHub. Public disclosure of an exploitable vulnerability without prior coordination will be rejected from the project.
---
## Translation contributions
Shadowbroker supports UI localization (`frontend/src/i18n/`). Translation contributions are welcome but held to a stricter standard than most code changes, because translations can subtly reshape user perception in ways that are hard to spot during review. Read this section before submitting one.
### The neutrality requirement
**Translations must be technically faithful to the English source.** That means:
- Each `t('key')` entry should mean approximately the same thing in the target language as in English, modulo idiom.
- Technical terms with established meanings (e.g. "GPS jamming," "military flight," "Tor," "onion routing," "encryption") should be translated using the corresponding established technical term in the target language — **not** softened, rebranded, or politically reframed.
- The set of UI strings should be **the same** between languages. Don't omit features from one locale that are visible in another.
### What will get a translation PR rejected
Translation choices that align the project with the framing or terminology of state propaganda — from **any** country — will be rejected. This applies symmetrically:
| Country / source | Examples of substitutions we will reject |
|---|---|
| **PRC / CCP** | Calling Taiwan a "province" or "renegade province"; reframing protest layers as "riots"; using softened or euphemistic terms for surveillance, internment, or jamming when the source text is direct |
| **Russia** | Calling the Ukraine war a "special military operation"; relabeling occupied territories as Russian; softening sanctions/jamming/disinfo terminology |
| **United States / EU** | Reframing adversaries with editorial labels not in the source (e.g. inserting "regime" where the English says "government"); applying labels like "terrorist" or "rogue state" to entities the English source describes neutrally |
| **Israel / Palestine / any active conflict** | Substituting one side's preferred terminology when the source uses the other side's or a neutral term |
| **Any government** | Adding political slogans, omitting features that government finds inconvenient, or inserting terminology associated with a specific political faction |
The test is **"would a translator working strictly from the English source produce this rendering?"** If the answer requires assuming a political stance the source does not take, the substitution does not belong in the translation.
### How translation PRs are reviewed
Changes to `frontend/src/i18n/**` are owned by the maintainer (see `CODEOWNERS`) and require explicit approval. We will:
1. Diff the translation against the English source key-by-key.
2. Spot-check a sample of entries with a native speaker of the target language when possible.
3. Look for the patterns above.
4. Look for suspicious additions to the i18n infrastructure itself (e.g. a remote translation fetcher, telemetry on language choice) — the i18n layer is supposed to be 100% client-side static JSON.
A PR that adds a new language is harder to review than one that fixes typos in an existing language. For new languages, please be patient and expect a real review window. For typo fixes, please describe each change in the PR body so the reviewer can verify intent.
### What about adding a new language?
We welcome new languages. The mechanical setup is documented in the header comment of `frontend/src/i18n/index.ts`. Beyond that:
- We are more likely to merge a new language quickly if at least one reviewer in the maintainer's network speaks it.
- If you are the *only* speaker of the target language reading this repo, your translation is welcome but the merge timeline will be longer while a reviewer is found.
- Partial translations are fine — the system falls back to English for any missing key.
---
## Anything else
If you have a question that isn't a security report, opening a GitHub Discussion or a draft PR with a question in the body is the fastest way to get a response. Direct emails are read but not always replied to promptly.