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>
4.9 KiB
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
- Fork the repo on GitHub (
bigbodycobain/Shadowbroker) or GitLab (bigbodycobain/Shadowbrokermirror). - Make your changes on a feature branch.
- Run the local test suite:
- Backend:
pytest backend/tests/ - Frontend:
cd frontend && npx vitest run
- Backend:
- 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:
- Diff the translation against the English source key-by-key.
- Spot-check a sample of entries with a native speaker of the target language when possible.
- Look for the patterns above.
- 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.