mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 11:22:15 +03:00
docs(i18n): align every public locale-count claim with the 42-locale config; bumpCounts covers llm.txt file-count phrasings
This commit is contained in:
@@ -462,7 +462,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 355 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 355 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 42 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
|
||||
<sub>📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
|
||||
|
||||
@@ -1295,7 +1295,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
|
||||
<tr><td nowrap><b><a href="docs/ops/BRANCHING_MODEL.md">Branching & Release Model</a></b></td><td>Where PRs target (<code>release/*</code>), what <code>main</code> and tags mean</td></tr>
|
||||
<tr><td nowrap><b><a href="CHANGELOG.md">Changelog</a></b></td><td>Full per-version release history</td></tr>
|
||||
<tr><td nowrap><b><a href="SECURITY.md">Security Policy</a></b></td><td>Vulnerability reporting and security practices</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/guides/I18N.md">i18n Guide</a></b></td><td>43-language support, translation workflow, RTL</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/guides/I18N.md">i18n Guide</a></b></td><td>42-language support, translation workflow, RTL</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/ops/RELEASE_CHECKLIST.md">Release Checklist</a></b></td><td>Pre-release validation steps</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/ops/COVERAGE_PLAN.md">Coverage Plan</a></b></td><td>Test coverage strategy for 39,000+ static test declarations across 5,100+ tracked test files</td></tr>
|
||||
</table>
|
||||
|
||||
@@ -22,9 +22,9 @@ bin/cli/
|
||||
├── provider-test.mjs ← testProviderApiKey()
|
||||
├── settings-store.mjs ← DB CRUD for key_value settings
|
||||
├── locales/
|
||||
│ ├── en.json ← English strings (source of truth, 43 locales)
|
||||
│ ├── en.json ← English strings (source of truth, 42 locales)
|
||||
│ ├── pt-BR.json ← Portuguese (Brazil) — fully translated
|
||||
│ └── {locale}.json ← 42 additional locales (ar, az, de, es, fr, ja, zh-CN, …)
|
||||
│ └── {locale}.json ← 41 additional locales (ar, az, de, es, fr, ja, zh-CN, …)
|
||||
├── scripts/
|
||||
│ └── generate-locales.mjs ← scaffold new locale files from config/i18n.json
|
||||
└── commands/
|
||||
|
||||
@@ -142,5 +142,5 @@ Namespaces added by Group B:
|
||||
| `sidebar.costsOverview` | Costs overview item |
|
||||
| `activity.*` | All Activity page strings (title, verbs, filters, empty state) |
|
||||
|
||||
Source-of-truth locales: `pt-BR` and `en`. All other 41 locales fall back to
|
||||
Source-of-truth locales: `pt-BR` and `en`. All other 40 locales fall back to
|
||||
English via the `next-intl` fallback mechanism (configured in `src/i18n/config.ts`).
|
||||
|
||||
@@ -121,7 +121,7 @@ src/
|
||||
├── domain/ # Pure domain logic (policy engine, fallback, cost, lockout, comboResolver, assessment)
|
||||
├── server/ # Server-only modules (authz pipeline, cors, auth middleware) — cannot import from client
|
||||
├── shared/ # Shared between server and client where safe (constants, types, validation, contracts, utils)
|
||||
├── i18n/ # next-intl config + per-locale message JSON (43 locales)
|
||||
├── i18n/ # next-intl config + per-locale message JSON (42 locales)
|
||||
├── middleware/ # Next.js middleware (request enrichment, locale detection)
|
||||
├── mitm/ # MITM proxy core: cert gen/install, handlers, targets, inspector, masks, passthrough
|
||||
│ ├── handlers/ # 9 IDE-agent handler classes extending MitmHandlerBase (antigravity, kiro, copilot, codex, cursor, zed, claudeCode, openCode, trae)
|
||||
@@ -396,7 +396,7 @@ open-sse/
|
||||
| `COVERAGE_PLAN.md` | Coverage goals and current state |
|
||||
| `FREE_TIERS.md` | Curated free-tier providers (48+ free + 11 OAuth) |
|
||||
| `CLI-TOOLS.md` | External CLI integrations + Internal OmniRoute CLI |
|
||||
| `I18N.md` | i18n architecture, adding a language, 43 locales |
|
||||
| `I18N.md` | i18n architecture, adding a language, 42 locales |
|
||||
| `UNINSTALL.md` | Clean uninstall steps |
|
||||
| `PROVIDER_REFERENCE.md` | **Auto-generated** catalog of 355 providers (regen: `npm run gen:provider-reference`) |
|
||||
|
||||
@@ -448,7 +448,7 @@ open-sse/
|
||||
|
||||
| Subdir | Purpose |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `docs/i18n/` | Localized doc translations (42 locales) |
|
||||
| `docs/i18n/` | Localized doc translations (41 locales) |
|
||||
| `docs/screenshots/` | Image assets for guides |
|
||||
| `_tasks/superpowers/` | Plans/specs from superpowers (`writing-plans`/`brainstorming`) + research — isolated, separately-versioned repo, gitignored by the main tree. See CLAUDE.md → "Planning & Research Artifacts". |
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Objective feature comparison vs popular open-source AI routers.
|
||||
| **CLI with system tray (no Electron)** | ✅ | ❌ | n/a | n/a |
|
||||
| **CLI machine-ID auto-auth** | ✅ | ❌ | n/a | n/a |
|
||||
| **Dashboard** | Next.js 16 | basic | proprietary | proprietary |
|
||||
| **i18n** | **43 locales** | ❌ | ❌ | ⚠ |
|
||||
| **i18n** | **42 locales** | ❌ | ❌ | ⚠ |
|
||||
| **Public agent skills (SKILL.md)** | ✅ 45 | ❌ | ❌ | ❌ |
|
||||
| **Tunnel support (Cloudflared, Tailscale, Ngrok)** | ✅ | ❌ | n/a | n/a |
|
||||
| **License** | MIT | MIT | proprietary | proprietary |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 355 providers, 150+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 355 providers, 150+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 42 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
|
||||
<defs>
|
||||
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>
|
||||
@@ -117,7 +117,7 @@
|
||||
<rect x="36" y="632" width="1128" height="42" rx="6" fill="#ffffff" fill-opacity="0.02"/>
|
||||
<g opacity="0"><animate attributeName="opacity" values="0;1" dur="0.4s" begin="1.14s" fill="freeze"/>
|
||||
<text x="44" y="658" font-size="14.5" fill="#c9d1d9">i18n UI locales</text>
|
||||
<text x="440" y="658" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">43</text>
|
||||
<text x="440" y="658" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">42</text>
|
||||
<text x="604" y="658" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">6</text>
|
||||
<use href="#no" x="760" y="653"/>
|
||||
<use href="#no" x="916" y="653"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -352,7 +352,7 @@ migration 142.
|
||||
The version floor above compares `version`, not either date.
|
||||
|
||||
Two gaps remain, both deliberate: the dashboard still shows only `Last fetched`, so reading
|
||||
the build date there needs a new label (and its 42 locale entries); and the offers and intel
|
||||
the build date there needs a new label (and its 41 locale entries); and the offers and intel
|
||||
caches keep no build date at all, even though their feed schemas carry one — `GET
|
||||
/api/radar/status` therefore omits the field for those two rather than reporting a `null`
|
||||
that would read as "unknown".
|
||||
|
||||
@@ -46,8 +46,8 @@ That is the bypass npm sanctions now that tokens which skip 2FA are being retire
|
||||
it restores the fully automatic flow the project had up to v3.8.48 while keeping the
|
||||
WS1.3 guarantee (a leaked token cannot publish alone — there is no token).
|
||||
|
||||
**One-time setup (owner):** npmjs.com → package `omniroute` → Settings → *Trusted
|
||||
Publisher* → GitHub: owner `diegosouzapw`, repo `OmniRoute`, workflow `npm-publish.yml`
|
||||
**One-time setup (owner):** npmjs.com → package `omniroute` → Settings → _Trusted
|
||||
Publisher_ → GitHub: owner `diegosouzapw`, repo `OmniRoute`, workflow `npm-publish.yml`
|
||||
(environment: none). Until that exists, the automatic step fails with `ENEEDAUTH`:
|
||||
re-dispatch with `publish_mode=staged` (below) or `direct`.
|
||||
|
||||
@@ -193,7 +193,7 @@ Breaking changes: add `BREAKING CHANGE:` footer or `!` after the scope (e.g. `fe
|
||||
|
||||
- [ ] `npm run i18n:check` exits 0 — translation state (`.i18n-state.json`) in sync with source docs (no drifted sources in strict mode; warn-mode advisory is acceptable for last-minute doc touch-ups, but should be 0 before tagging)
|
||||
- [ ] `npm run i18n:check-ui-coverage` exits 0 — every UI locale at or above the 80% coverage floor
|
||||
- [ ] `npm run i18n:sync-ui:dry` reports 0 missing keys across all 43 locales
|
||||
- [ ] `npm run i18n:sync-ui:dry` reports 0 missing keys across all 42 locales
|
||||
- [ ] If source English docs changed, run `npm run i18n:run` (requires `OMNIROUTE_TRANSLATION_API_KEY` in `.env`) before tagging
|
||||
- [ ] Translation contributions can be deferred to next release if minor (track in CHANGELOG)
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
* insertReadmeFlagLink(readmeText, entry, total) README.md language block
|
||||
* insertDocsIndexRow(indexText, entry, total) docs/i18n/README.md
|
||||
* insertI18nGuideRow(guideText, entry, total, rtlCodes) docs/guides/I18N.md locale table
|
||||
* bumpCounts(text, total) "N languages" / "N translated documentation sets"
|
||||
* bumpCounts(text, total) llm.txt counts: "N languages",
|
||||
* "N translated documentation sets",
|
||||
* "N language JSON files",
|
||||
* "N-language translated docs"
|
||||
* buildMirrorStub({ heading, native, bar, body }) docs/i18n/<code>/llm.txt, CHANGELOG.md
|
||||
*
|
||||
* Every insertion leaves the existing lines exactly as they are and places the
|
||||
@@ -164,7 +167,9 @@ export function insertI18nGuideRow(guideText, entry, total, rtlCodes = DEFAULT_R
|
||||
export function bumpCounts(text, total) {
|
||||
return text
|
||||
.replace(/\d+ languages/g, `${total} languages`)
|
||||
.replace(/\d+ translated documentation sets/g, `${total - 1} translated documentation sets`);
|
||||
.replace(/\d+ translated documentation sets/g, `${total - 1} translated documentation sets`)
|
||||
.replace(/(\d+) language JSON files/g, `${total} language JSON files`)
|
||||
.replace(/(\d+)-language translated docs/g, `${total - 1}-language translated docs`);
|
||||
}
|
||||
|
||||
export function buildMirrorStub({ heading, native, bar, body }) {
|
||||
|
||||
@@ -533,16 +533,34 @@ test("bumpCounts rewrites language and doc-set counts", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("bumpCounts touches only those two phrases", () => {
|
||||
const text =
|
||||
"352 providers, 43 languages for UI, 42 translated documentation sets, 43 locales, 7 language packs, in 42 locales, 43 langs";
|
||||
// llm.txt's repo-tree comments count the same two things in a different shape:
|
||||
// `messages/ # N language JSON files` is the UI-locale total, `i18n/ # N-language
|
||||
// translated docs` is the docs total (one less — English is the source, not a
|
||||
// translation). Both were left behind by bumpCounts until this was added.
|
||||
test("bumpCounts rewrites the llm.txt tree-comment count phrasings", () => {
|
||||
assert.equal(
|
||||
bumpCounts(text, 44),
|
||||
"352 providers, 44 languages for UI, 43 translated documentation sets, 43 locales, 7 language packs, in 42 locales, 43 langs"
|
||||
bumpCounts("messages/ # 43 language JSON files\ni18n/ # 42-language translated docs", 44),
|
||||
"messages/ # 44 language JSON files\ni18n/ # 43-language translated docs"
|
||||
);
|
||||
});
|
||||
|
||||
test("bumpCounts on the real llm.txt rewrites exactly the three count lines, each by one", () => {
|
||||
test("bumpCounts touches only those four phrases", () => {
|
||||
const text =
|
||||
"352 providers, 43 languages for UI, 42 translated documentation sets, 43 language JSON files, 42-language translated docs, 43 locales, 7 language packs, in 42 locales, 43 langs";
|
||||
assert.equal(
|
||||
bumpCounts(text, 44),
|
||||
"352 providers, 44 languages for UI, 43 translated documentation sets, 44 language JSON files, 43-language translated docs, 43 locales, 7 language packs, in 42 locales, 43 langs"
|
||||
);
|
||||
});
|
||||
|
||||
// The four phrases, in the shapes llm.txt actually uses. `-language translated
|
||||
// docs` is hyphenated, so the separator class is `[ -]`, not a plain space.
|
||||
const LLM_COUNT_PHRASE =
|
||||
/(?:languages|translated documentation sets|language JSON files|language translated docs)/;
|
||||
const LLM_COUNT_LINE = new RegExp(`\\d+[ -]${LLM_COUNT_PHRASE.source}`);
|
||||
const LLM_COUNT_NUMBER = new RegExp(`\\d+(?=[ -]${LLM_COUNT_PHRASE.source})`);
|
||||
|
||||
test("bumpCounts on the real llm.txt rewrites exactly the five count lines, each by one", () => {
|
||||
const total = realConfig().locales.length;
|
||||
const before = readRepo("llm.txt").split("\n");
|
||||
const after = bumpCounts(before.join("\n"), total + 1).split("\n");
|
||||
@@ -550,18 +568,25 @@ test("bumpCounts on the real llm.txt rewrites exactly the three count lines, eac
|
||||
const changed = before
|
||||
.map((line, index) => [line, after[index]])
|
||||
.filter(([from, to]) => from !== to);
|
||||
assert.equal(changed.length, 3);
|
||||
assert.equal(changed.length, 5);
|
||||
for (const [from, to] of changed) {
|
||||
assert.match(from, /\d+ (languages|translated documentation sets)/);
|
||||
assert.match(from, LLM_COUNT_LINE);
|
||||
assert.equal(
|
||||
to,
|
||||
from.replace(/\d+(?= (?:languages|translated documentation sets))/, (n) =>
|
||||
String(Number(n) + 1)
|
||||
)
|
||||
from.replace(LLM_COUNT_NUMBER, (n) => String(Number(n) + 1))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// The counterpart of the test above: at the CURRENT locale total the file is
|
||||
// already correct, so bumpCounts must be a no-op. This is what would have caught
|
||||
// the two tree-comment phrasings drifting out of sync.
|
||||
test("bumpCounts on the real llm.txt is a no-op at the current locale total", () => {
|
||||
const total = realConfig().locales.length;
|
||||
const text = readRepo("llm.txt");
|
||||
assert.equal(bumpCounts(text, total), text);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// buildMirrorStub
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user