Compare commits

..

1 Commits

Author SHA1 Message Date
2348cc2234 feat(B5): single-use top-up codes (redeem + mint CLI)
All checks were successful
CI / Format (push) Successful in 33s
CI / CUDA type-check (push) Successful in 1m33s
CI / Clippy (push) Successful in 3m5s
CI / Test (push) Successful in 6m29s
CI / Build cortex SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
Completes the hybrid allocation model — the flat free grant (B4) plus
top-up codes that extend an account's allocation.

- topup.rs: redeem(account, raw_code) — timing-safe, single-use. A
  conditional `UPDATE … WHERE redeemed_by IS NULL RETURNING value` claims
  the code atomically (concurrent double-redeem → exactly one winner), then
  raises accounts.allocation_total in the same tx. Only sha256(code) is
  stored; a not-found code and an already-redeemed code return the SAME
  generic error via the same path (no "valid but spent" oracle). Raising
  the total automatically lifts every percent-limited key's effective cap;
  hardcap keys stay pinned (by design).
- mint(value, count, denomination) — inserts codes (hash-only), returns the
  raw codes once. Exposed as `helexa-upstream mint --value --count
  [--denomination]` (raw codes to stdout, one per line) — the seam the
  future faucet bot calls. Bot itself out of scope.
- POST /web/v1/redeem (session-protected) → {allocation_total} | generic 400.

Validated against Postgres: redeem raises the 1_000_000 free grant to
1_500_000, second redemption + unknown code both generic-400, and a
concurrent race for one code yields exactly one HTTP 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6o3ddqmYNh9kzdwq6eowh
2026-06-23 10:50:43 +03:00
154 changed files with 295 additions and 10623 deletions

View File

@@ -90,18 +90,3 @@ account_id = "operator"
key_id = "infra"
# No hard_cap → uncapped operator infra key (own fleet, own use). Still
# metered for visibility.
# -- Upstream (helexa mesh) entitlements client (#57) --------------------
# When enabled, a bearer key NOT found in [[entitlements.keys]] above is
# validated against the helexa-upstream authority (mesh accounts), and its
# budget is reserved/settled there. Operator-local keys (incl. the infra
# key) never leave this process. Fail-closed: if upstream is unreachable a
# request is refused (503 + Retry-After), never served un-authorized.
# Disabled by default — a standalone operator runs purely local.
[upstream]
enabled = false
# url = "https://upstream.helexa.ai"
# Shared client bearer this cortex presents (maps to an operator_id
# upstream). Override via CORTEX_UPSTREAM__BEARER in prod.
# bearer = "replace-with-operator-client-secret"
# timeout_secs = 5

View File

@@ -22,36 +22,6 @@ pub struct GatewayConfig {
/// setups keep working until keys are configured.
#[serde(default)]
pub entitlements: EntitlementsConfig,
/// helexa-upstream client (#57). When enabled, keys not found in the
/// local `[entitlements]` config are validated against the mesh
/// authority, and budget is reserved/settled there. Disabled by default
/// — a single operator runs purely local.
#[serde(default)]
pub upstream: UpstreamClientConfig,
}
/// `[upstream]` — the helexa-upstream authority client (#57). Locally
/// unrecognised bearer keys are resolved against `url`'s `/authz/v1` surface
/// (mesh accounts); local keys (operator + infra) never leave the process.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UpstreamClientConfig {
/// Enable the upstream fallthrough. Off → purely local entitlements.
#[serde(default)]
pub enabled: bool,
/// Base URL of helexa-upstream (e.g. "https://upstream.helexa.ai").
#[serde(default)]
pub url: String,
/// Shared client bearer this cortex presents to `/authz/v1` (maps to an
/// operator_id upstream). Sent as `Authorization: Bearer <bearer>`.
#[serde(default)]
pub bearer: String,
/// Per-call timeout (seconds) to upstream.
#[serde(default = "default_upstream_timeout")]
pub timeout_secs: u64,
}
fn default_upstream_timeout() -> u64 {
5
}
/// `[entitlements]` — the local/static [`crate::entitlements::EntitlementProvider`]
@@ -159,7 +129,6 @@ impl Default for GatewayConfig {
neurons: vec![],
models_config: default_models_path(),
entitlements: EntitlementsConfig::default(),
upstream: UpstreamClientConfig::default(),
}
}
}

View File

@@ -81,19 +81,12 @@ pub struct BudgetSnapshot {
pub reserved: u64,
}
/// Authentication failure — the bearer key could not be resolved.
/// Authentication failure — the bearer key could not be resolved. Maps to
/// `401 invalid_api_key` (#49/#63).
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
/// The key is genuinely unknown → `401 invalid_api_key` (#49/#63).
#[error("invalid or unknown API key")]
InvalidKey,
/// The authority that could resolve the key is unreachable (e.g. the
/// helexa-upstream client failed, #57). Fail **closed** but distinctly:
/// a transient outage must surface as `503 service_unavailable` +
/// `Retry-After`, never `401` — a real key must not be rejected as
/// invalid during an upstream blip.
#[error("entitlement authority unavailable; retry in {retry_after_secs}s")]
Unavailable { retry_after_secs: u64 },
}
/// Why a reservation was refused. Carries enough for the caller to build the

View File

@@ -22,7 +22,7 @@ use axum::http::header::AUTHORIZATION;
use axum::http::{HeaderMap, HeaderValue};
use axum::middleware::Next;
use axum::response::Response;
use cortex_core::entitlements::{AuthError, HEADER_ACCOUNT_ID, HEADER_KEY_ID};
use cortex_core::entitlements::{HEADER_ACCOUNT_ID, HEADER_KEY_ID};
use cortex_core::error_envelope::OpenAiError;
use std::sync::Arc;
@@ -83,25 +83,14 @@ pub async fn require_principal(
req.extensions_mut().insert(principal);
next.run(req).await
}
// The entitlement authority is unreachable (upstream client
// blip, #57). Fail **closed but distinct**: a transient outage
// must not reject a real key as `401 invalid_api_key` — it's a
// retryable `503`. This holds regardless of require_auth: we
// can't safely serve a key we couldn't authorize.
Err(AuthError::Unavailable { retry_after_secs }) => {
envelope_response(OpenAiError::service_unavailable(
"entitlement authority temporarily unavailable",
Some(retry_after_secs),
))
}
// A genuinely unrecognized key only hard-fails when auth is
// *required*. In allow-anonymous mode (the default) we IGNORE it
// and serve unauthenticated — otherwise the placeholder keys that
// OpenAI-compatible clients send by default (opencode, Open WebUI,
// Agent Zero, litellm) would all break though the operator never
// opted into auth. Pre-#49 the bearer was never inspected; this
// preserves that for require_auth=false.
Err(AuthError::InvalidKey) => {
// An unrecognized key only hard-fails when auth is *required*.
// In allow-anonymous mode (the default) we must IGNORE it and
// serve the request unauthenticated — otherwise the placeholder
// keys that OpenAI-compatible clients send by default (opencode,
// Open WebUI, Agent Zero, litellm) would all break, even though
// the operator never opted into auth. Pre-#49 the bearer was
// never inspected at all; this preserves that for require_auth=false.
Err(_) => {
if fleet.require_auth {
unauthorized("invalid API key")
} else {

View File

@@ -1,112 +0,0 @@
//! Chained entitlement provider (#57): operator-local keys first, mesh
//! upstream for everything else.
//!
//! `resolve` tries the [`LocalEntitlementProvider`] (operator + infra keys —
//! never a network hop); only a locally-unknown key falls through to
//! [`UpstreamEntitlementProvider`]. Because the local provider treats an
//! unconfigured principal as uncapped, reserve/settle/release/snapshot must
//! **not** blindly hit local — they dispatch to whichever backend resolved
//! that account, remembered in a map keyed by `account_id` (populated at
//! resolve time).
use crate::entitlements_local::LocalEntitlementProvider;
use crate::entitlements_upstream::UpstreamEntitlementProvider;
use async_trait::async_trait;
use cortex_core::entitlements::{
AuthError, BudgetError, BudgetSnapshot, EntitlementProvider, Principal, Reservation,
};
use std::collections::HashMap;
use tokio::sync::RwLock;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Backend {
Local,
Upstream,
}
pub struct ChainedEntitlementProvider {
local: LocalEntitlementProvider,
upstream: UpstreamEntitlementProvider,
/// account_id → which backend owns it, learned at resolve time.
backends: RwLock<HashMap<String, Backend>>,
}
impl ChainedEntitlementProvider {
pub fn new(local: LocalEntitlementProvider, upstream: UpstreamEntitlementProvider) -> Self {
Self {
local,
upstream,
backends: RwLock::new(HashMap::new()),
}
}
async fn record(&self, account_id: &str, backend: Backend) {
self.backends
.write()
.await
.insert(account_id.to_string(), backend);
}
/// The backend that owns `account_id`. Defaults to `Upstream` for an
/// account never resolved this process-lifetime (a resolve always
/// precedes reserve in a request, so this is just a safe fallback —
/// upstream fails closed if the account is bogus).
async fn backend_for(&self, account_id: &str) -> Backend {
self.backends
.read()
.await
.get(account_id)
.copied()
.unwrap_or(Backend::Upstream)
}
}
#[async_trait]
impl EntitlementProvider for ChainedEntitlementProvider {
async fn resolve(&self, api_key: &str) -> Result<Principal, AuthError> {
match self.local.resolve(api_key).await {
Ok(p) => {
self.record(&p.account_id, Backend::Local).await;
Ok(p)
}
Err(AuthError::InvalidKey) => {
let p = self.upstream.resolve(api_key).await?;
self.record(&p.account_id, Backend::Upstream).await;
Ok(p)
}
Err(e) => Err(e),
}
}
async fn reserve(
&self,
principal: &Principal,
max_tokens: u64,
) -> Result<Reservation, BudgetError> {
match self.backend_for(&principal.account_id).await {
Backend::Local => self.local.reserve(principal, max_tokens).await,
Backend::Upstream => self.upstream.reserve(principal, max_tokens).await,
}
}
async fn settle(&self, reservation: Reservation, actual_tokens: u64) {
match self.backend_for(&reservation.principal.account_id).await {
Backend::Local => self.local.settle(reservation, actual_tokens).await,
Backend::Upstream => self.upstream.settle(reservation, actual_tokens).await,
}
}
async fn release(&self, reservation: Reservation) {
match self.backend_for(&reservation.principal.account_id).await {
Backend::Local => self.local.release(reservation).await,
Backend::Upstream => self.upstream.release(reservation).await,
}
}
async fn snapshot(&self, principal: &Principal) -> Option<BudgetSnapshot> {
match self.backend_for(&principal.account_id).await {
Backend::Local => self.local.snapshot(principal).await,
Backend::Upstream => self.upstream.snapshot(principal).await,
}
}
}

View File

@@ -1,246 +0,0 @@
//! helexa-upstream client (#57): an [`EntitlementProvider`] that resolves
//! keys and reserves/settles budget against the mesh authority's
//! `/authz/v1` surface (B2). It is "just another impl of the trait" — cortex
//! enforcement (`auth.rs`, `metering.rs`) is unchanged.
//!
//! **Fail closed.** When upstream is unreachable, `resolve` returns
//! [`AuthError::Unavailable`] (→ `503`, never `401`) and `reserve` refuses
//! with a retryable [`BudgetError::RateLimited`] — a request is never served
//! on an un-authorized key, and a real key is never rejected as invalid
//! during a blip.
use async_trait::async_trait;
use cortex_core::config::UpstreamClientConfig;
use cortex_core::entitlements::{
AuthError, BudgetError, BudgetSnapshot, EntitlementProvider, Principal, Reservation,
};
use serde::Deserialize;
use std::time::Duration;
/// Retry-After (seconds) advertised when we fail closed on an upstream
/// outage.
const FAIL_CLOSED_RETRY_SECS: u64 = 5;
pub struct UpstreamEntitlementProvider {
client: reqwest::Client,
base_url: String,
bearer: String,
}
#[derive(Deserialize)]
struct PrincipalDto {
account_id: String,
key_id: String,
}
#[derive(Deserialize)]
struct SnapshotDto {
hard_cap: Option<u64>,
spent: u64,
reserved: u64,
}
#[derive(Deserialize)]
struct ResolveResp {
principal: PrincipalDto,
#[allow(dead_code)]
snapshot: Option<SnapshotDto>,
}
#[derive(Deserialize)]
struct ReserveResp {
reservation_id: Option<i64>,
rejected: Option<Rejection>,
}
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
enum Rejection {
InsufficientQuota {
requested: u64,
available: u64,
},
RateLimited {
requested: u64,
available: u64,
retry_after_secs: u64,
},
}
impl UpstreamEntitlementProvider {
pub fn new(cfg: &UpstreamClientConfig) -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(cfg.timeout_secs))
.build()
.expect("failed to build upstream HTTP client");
Self {
client,
base_url: cfg.url.trim_end_matches('/').to_string(),
bearer: cfg.bearer.clone(),
}
}
fn url(&self, path: &str) -> String {
format!("{}{}", self.base_url, path)
}
}
#[async_trait]
impl EntitlementProvider for UpstreamEntitlementProvider {
async fn resolve(&self, api_key: &str) -> Result<Principal, AuthError> {
let resp = self
.client
.post(self.url("/authz/v1/resolve"))
.bearer_auth(&self.bearer)
.json(&serde_json::json!({ "api_key": api_key }))
.send()
.await;
let resp = match resp {
Ok(r) => r,
Err(e) => {
tracing::warn!(error = %e, "upstream resolve unreachable; failing closed");
return Err(AuthError::Unavailable {
retry_after_secs: FAIL_CLOSED_RETRY_SECS,
});
}
};
if resp.status().as_u16() == 401 {
return Err(AuthError::InvalidKey);
}
if !resp.status().is_success() {
return Err(AuthError::Unavailable {
retry_after_secs: FAIL_CLOSED_RETRY_SECS,
});
}
match resp.json::<ResolveResp>().await {
Ok(r) => Ok(Principal {
account_id: r.principal.account_id,
key_id: r.principal.key_id,
}),
Err(e) => {
tracing::warn!(error = %e, "upstream resolve: bad body; failing closed");
Err(AuthError::Unavailable {
retry_after_secs: FAIL_CLOSED_RETRY_SECS,
})
}
}
}
async fn reserve(
&self,
principal: &Principal,
max_tokens: u64,
) -> Result<Reservation, BudgetError> {
let fail_closed = || BudgetError::RateLimited {
requested: max_tokens,
available: 0,
retry_after_secs: FAIL_CLOSED_RETRY_SECS,
};
let resp = self
.client
.post(self.url("/authz/v1/reserve"))
.bearer_auth(&self.bearer)
.json(&serde_json::json!({
"account_id": principal.account_id,
"key_id": principal.key_id,
"max_tokens": max_tokens,
}))
.send()
.await;
let resp = match resp {
Ok(r) if r.status().is_success() => r,
Ok(r) => {
tracing::warn!(status = %r.status(), "upstream reserve non-2xx; failing closed");
return Err(fail_closed());
}
Err(e) => {
tracing::warn!(error = %e, "upstream reserve unreachable; failing closed");
return Err(fail_closed());
}
};
match resp.json::<ReserveResp>().await {
Ok(ReserveResp {
reservation_id: Some(id),
..
}) => Ok(Reservation {
id: id as u64,
principal: principal.clone(),
reserved: max_tokens,
}),
Ok(ReserveResp {
rejected:
Some(Rejection::InsufficientQuota {
requested,
available,
}),
..
}) => Err(BudgetError::InsufficientQuota {
requested,
available,
}),
Ok(ReserveResp {
rejected:
Some(Rejection::RateLimited {
requested,
available,
retry_after_secs,
}),
..
}) => Err(BudgetError::RateLimited {
requested,
available,
retry_after_secs,
}),
_ => Err(fail_closed()),
}
}
async fn settle(&self, reservation: Reservation, actual_tokens: u64) {
// Best-effort; a lost settle is reaped by the upstream sweeper (B2).
let _ = self
.client
.post(self.url("/authz/v1/settle"))
.bearer_auth(&self.bearer)
.json(&serde_json::json!({
"reservation_id": reservation.id as i64,
"actual_tokens": actual_tokens,
}))
.send()
.await
.inspect_err(
|e| tracing::warn!(error = %e, "upstream settle failed (sweeper will reap)"),
);
}
async fn release(&self, reservation: Reservation) {
let _ = self
.client
.post(self.url("/authz/v1/release"))
.bearer_auth(&self.bearer)
.json(&serde_json::json!({ "reservation_id": reservation.id as i64 }))
.send()
.await
.inspect_err(
|e| tracing::warn!(error = %e, "upstream release failed (sweeper will reap)"),
);
}
async fn snapshot(&self, principal: &Principal) -> Option<BudgetSnapshot> {
let resp = self
.client
.post(self.url("/authz/v1/snapshot"))
.bearer_auth(&self.bearer)
.json(&serde_json::json!({
"account_id": principal.account_id,
"key_id": principal.key_id,
}))
.send()
.await
.ok()?;
if !resp.status().is_success() {
return None;
}
let dto = resp.json::<SnapshotDto>().await.ok()?;
Some(BudgetSnapshot {
hard_cap: dto.hard_cap,
spent: dto.spent,
reserved: dto.reserved,
})
}
}

View File

@@ -1,8 +1,6 @@
pub mod anthropic_sse;
pub mod auth;
pub mod entitlements_chain;
pub mod entitlements_local;
pub mod entitlements_upstream;
pub mod error;
pub mod evictor;
pub mod handlers;

View File

@@ -1,6 +1,4 @@
use crate::entitlements_chain::ChainedEntitlementProvider;
use crate::entitlements_local::LocalEntitlementProvider;
use crate::entitlements_upstream::UpstreamEntitlementProvider;
use cortex_core::catalogue::ModelCatalogue;
use cortex_core::config::{EvictionSettings, GatewayConfig, NeuronEndpoint};
use cortex_core::entitlements::EntitlementProvider;
@@ -47,20 +45,8 @@ impl CortexState {
let catalogue = ModelCatalogue::load(&config.models_config);
// Local provider always handles operator + infra keys. When the
// upstream client is enabled (#57), wrap it in the chain so locally
// unknown keys fall through to the mesh authority; otherwise stay
// purely local.
let local = LocalEntitlementProvider::from_config(&config.entitlements);
let entitlements: Arc<dyn EntitlementProvider> = if config.upstream.enabled {
tracing::info!(url = %config.upstream.url, "upstream entitlement client enabled");
Arc::new(ChainedEntitlementProvider::new(
local,
UpstreamEntitlementProvider::new(&config.upstream),
))
} else {
Arc::new(local)
};
let entitlements: Arc<dyn EntitlementProvider> =
Arc::new(LocalEntitlementProvider::from_config(&config.entitlements));
Self {
nodes: RwLock::new(nodes),

View File

@@ -57,7 +57,6 @@ async fn test_alias_resolves_in_chat_completions() {
}],
models_config: models_path.to_string_lossy().to_string(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -144,7 +143,6 @@ async fn test_aliases_surface_in_v1_models() {
}],
models_config: models_path.to_string_lossy().to_string(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -234,7 +232,6 @@ async fn test_alias_falls_through_for_unmapped_model() {
}],
models_config: models_path.to_string_lossy().to_string(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -105,7 +105,6 @@ async fn spawn_gateway(neuron_url: &str, entitlements: EntitlementsConfig) -> St
}],
models_config: "/dev/null".into(),
entitlements,
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -81,7 +81,6 @@ async fn spawn_gateway(neuron_url: &str, key: ApiKeyConfig) -> (Arc<CortexState>
require_auth: true,
keys: vec![key],
},
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
{

View File

@@ -430,7 +430,6 @@ pub async fn spawn_gateway_with_state(mock_url: &str) -> (Arc<CortexState>, Stri
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -89,7 +89,6 @@ async fn error_response_no_healthy_nodes() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(cortex_gateway::state::CortexState::from_config(&config));

View File

@@ -72,7 +72,6 @@ fn make_fleet(endpoint: &str, defrag_after: u32) -> Arc<CortexState> {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
Arc::new(CortexState::from_config(&config))
}

View File

@@ -74,7 +74,6 @@ async fn fleet_with(big_healthy: bool, big_devices: usize) -> Arc<CortexState> {
],
models_config: cat.to_string_lossy().into_owned(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
{

View File

@@ -72,7 +72,6 @@ async fn two_neuron_fleet(endpoint_a: &str, endpoint_b: &str) -> Arc<CortexState
],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
Arc::new(CortexState::from_config(&config))
}

View File

@@ -53,7 +53,6 @@ async fn spawn_metered_gateway(neuron_url: &str) -> (Arc<CortexState>, String) {
window: CapWindow::Balance,
}],
},
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -159,7 +158,6 @@ async fn anonymous_request_records_no_spend() {
}],
models_config: "/dev/null".into(),
entitlements: EntitlementsConfig::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
{

View File

@@ -66,7 +66,6 @@ harness = "candle"
}],
models_config: cat_path.to_string_lossy().into_owned(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -55,7 +55,6 @@ capabilities = ["text"]
}],
models_config: cat_path.to_string_lossy().into_owned(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -32,7 +32,6 @@ async fn test_poller_discovers_models() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -85,7 +84,6 @@ async fn test_poller_updates_gateway_models_endpoint() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -158,7 +156,6 @@ async fn test_models_endpoint_unions_capabilities_across_nodes() {
],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -222,7 +219,6 @@ async fn test_poller_marks_unreachable_node_unhealthy() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -277,7 +273,6 @@ async fn test_poller_removes_stale_models() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -309,7 +304,6 @@ async fn test_poller_removes_stale_models() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet2 = Arc::new(CortexState::from_config(&config2));
@@ -392,7 +386,6 @@ async fn test_poller_captures_activation_from_health() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
@@ -438,7 +431,6 @@ async fn test_poller_parses_recovering_status() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));

View File

@@ -75,7 +75,6 @@ async fn spawn_gateway(neuron: &str, context: usize) -> String {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
{

View File

@@ -118,7 +118,6 @@ async fn test_no_healthy_nodes() {
}],
models_config: "/dev/null".into(),
entitlements: Default::default(),
upstream: Default::default(),
};
let fleet = std::sync::Arc::new(cortex_gateway::state::CortexState::from_config(&config));

View File

@@ -1,105 +0,0 @@
//! B3: the chained entitlement provider (local → upstream) and fail-closed
//! semantics, exercised against a mock helexa-upstream `/authz/v1`.
use axum::{Json, Router, routing::post};
use cortex_core::config::{ApiKeyConfig, EntitlementsConfig, UpstreamClientConfig};
use cortex_core::entitlements::{AuthError, EntitlementProvider};
use cortex_gateway::entitlements_chain::ChainedEntitlementProvider;
use cortex_gateway::entitlements_local::LocalEntitlementProvider;
use cortex_gateway::entitlements_upstream::UpstreamEntitlementProvider;
use serde_json::{Value, json};
use tokio::net::TcpListener;
/// Mock upstream: `mesh-key` resolves to a mesh account; anything else 401.
/// reserve always grants reservation 1.
async fn spawn_mock_upstream() -> String {
async fn resolve(Json(body): Json<Value>) -> axum::response::Response {
use axum::response::IntoResponse;
if body["api_key"] == "mesh-key" {
Json(json!({"principal": {"account_id": "mesh-acct", "key_id": "mesh-key-1"}}))
.into_response()
} else {
(
axum::http::StatusCode::UNAUTHORIZED,
Json(json!({"error": {"code": "invalid_api_key"}})),
)
.into_response()
}
}
async fn reserve() -> Json<Value> {
Json(json!({ "reservation_id": 1 }))
}
let app = Router::new()
.route("/authz/v1/resolve", post(resolve))
.route("/authz/v1/reserve", post(reserve));
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
format!("http://{addr}")
}
fn local_with_key() -> LocalEntitlementProvider {
let cfg = EntitlementsConfig {
require_auth: false,
keys: vec![ApiKeyConfig {
key: "local-key".into(),
account_id: "op".into(),
key_id: None,
hard_cap: None,
window: Default::default(),
}],
};
LocalEntitlementProvider::from_config(&cfg)
}
fn chain(local: LocalEntitlementProvider, url: &str) -> ChainedEntitlementProvider {
let upstream = UpstreamEntitlementProvider::new(&UpstreamClientConfig {
enabled: true,
url: url.to_string(),
bearer: "client-secret".into(),
timeout_secs: 5,
});
ChainedEntitlementProvider::new(local, upstream)
}
#[tokio::test]
async fn local_key_resolves_locally() {
let url = spawn_mock_upstream().await;
let c = chain(local_with_key(), &url);
let p = c.resolve("local-key").await.expect("local resolves");
assert_eq!(p.account_id, "op");
}
#[tokio::test]
async fn unknown_key_falls_through_to_upstream() {
let url = spawn_mock_upstream().await;
let c = chain(local_with_key(), &url);
let p = c.resolve("mesh-key").await.expect("upstream resolves");
assert_eq!(p.account_id, "mesh-acct");
assert_eq!(p.key_id, "mesh-key-1");
}
#[tokio::test]
async fn unknown_everywhere_is_invalid_key() {
let url = spawn_mock_upstream().await;
let c = chain(local_with_key(), &url);
match c.resolve("nope").await {
Err(AuthError::InvalidKey) => {}
other => panic!("expected InvalidKey, got {other:?}"),
}
}
#[tokio::test]
async fn upstream_unreachable_fails_closed_as_unavailable() {
// No mock — point at a dead port. A locally-unknown key must surface
// Unavailable (→ 503), never InvalidKey (→ 401).
let c = chain(local_with_key(), "http://127.0.0.1:1");
match c.resolve("some-mesh-key").await {
Err(AuthError::Unavailable { retry_after_secs }) => assert!(retry_after_secs > 0),
other => panic!("expected Unavailable, got {other:?}"),
}
// A local key still resolves without touching upstream.
assert_eq!(c.resolve("local-key").await.unwrap().account_id, "op");
}

View File

@@ -21,6 +21,7 @@ pub mod error;
pub mod handlers;
pub mod ledger;
pub mod state;
pub mod topup;
pub mod web;
use anyhow::Result;

View File

@@ -20,6 +20,22 @@ enum Commands {
#[arg(short, long, default_value = "helexa-upstream.toml")]
config: String,
},
/// Mint single-use top-up codes and print them (one per line). The raw
/// codes are shown only here — only their hash is stored. (The future
/// faucet bot calls the same path.)
Mint {
#[arg(short, long, default_value = "helexa-upstream.toml")]
config: String,
/// Tokens each code grants.
#[arg(long)]
value: i64,
/// How many codes to mint.
#[arg(long, default_value_t = 1)]
count: u32,
/// Optional human label (e.g. "small", "beta-launch").
#[arg(long)]
denomination: Option<String>,
},
}
#[tokio::main]
@@ -40,6 +56,25 @@ async fn main() -> Result<()> {
tracing::info!(listen = %cfg.server.listen, "starting helexa-upstream");
helexa_upstream::run(cfg).await?;
}
Commands::Mint {
config,
value,
count,
denomination,
} => {
let cfg = UpstreamConfig::load(&config)
.map_err(|e| anyhow::anyhow!("failed to load config from '{config}': {e}"))?;
let pool =
helexa_upstream::db::connect_and_migrate(&cfg.db.url, cfg.db.max_connections)
.await?;
let codes =
helexa_upstream::topup::mint(&pool, value, count, denomination.as_deref()).await?;
// Raw codes to stdout (one per line) for the operator to distribute;
// logs/diagnostics go to stderr via tracing.
for code in codes {
println!("{code}");
}
}
}
Ok(())

View File

@@ -0,0 +1,82 @@
//! Single-use top-up codes (#B5) — the second half of the hybrid allocation
//! model. Each code grants `value` tokens to the account that redeems it,
//! raising `accounts.allocation_total`. Minting codes is operator/CLI side
//! (the future faucet bot calls the same `mint` path); redemption is a
//! `/web/v1` action.
//!
//! Security: only `sha256(code)` is stored. Redemption is **timing-safe and
//! single-use** — a conditional `UPDATE … WHERE redeemed_by IS NULL` does
//! the claim atomically (concurrent double-redeem → exactly one winner), and
//! a not-found code and an already-redeemed code return the **same** generic
//! failure with the same code path (no oracle for "valid but spent").
use crate::crypto::{random_token, sha256};
use sqlx::Row;
use sqlx::postgres::PgPool;
use uuid::Uuid;
#[derive(Debug, thiserror::Error)]
pub enum TopUpError {
/// Code unknown OR already redeemed — deliberately indistinguishable.
#[error("invalid or already-redeemed code")]
Invalid,
#[error(transparent)]
Db(#[from] sqlx::Error),
}
/// Redeem `raw_code` for `account_id`, raising the account's
/// `allocation_total` by the code's value. Returns the new total.
pub async fn redeem(pool: &PgPool, account_id: Uuid, raw_code: &str) -> Result<i64, TopUpError> {
let mut tx = pool.begin().await?;
// Atomic single-use claim. `redeemed_by IS NULL` is the guarantee: under
// concurrent redemption exactly one UPDATE touches the row.
let claimed = sqlx::query(
"UPDATE top_up_codes SET redeemed_by = $1, redeemed_at = now() \
WHERE code_hash = $2 AND redeemed_by IS NULL RETURNING value",
)
.bind(account_id)
.bind(sha256(raw_code))
.fetch_optional(&mut *tx)
.await?;
let Some(row) = claimed else {
// Not found or already redeemed — same path, same error.
return Err(TopUpError::Invalid);
};
let value: i64 = row.get("value");
let new_total: i64 = sqlx::query(
"UPDATE accounts SET allocation_total = allocation_total + $1 WHERE id = $2 \
RETURNING allocation_total",
)
.bind(value)
.bind(account_id)
.fetch_one(&mut *tx)
.await?
.get("allocation_total");
tx.commit().await?;
Ok(new_total)
}
/// Mint `count` codes each worth `value` tokens, optionally tagged with a
/// `denomination` label. Returns the raw codes (shown once — only their
/// hash is stored). The CLI prints these; the future faucet bot calls this.
pub async fn mint(
pool: &PgPool,
value: i64,
count: u32,
denomination: Option<&str>,
) -> Result<Vec<String>, sqlx::Error> {
let mut codes = Vec::with_capacity(count as usize);
for _ in 0..count {
let raw = format!("helexa-topup-{}", random_token());
sqlx::query(
"INSERT INTO top_up_codes (code_hash, value, denomination) VALUES ($1, $2, $3)",
)
.bind(sha256(&raw))
.bind(value)
.bind(denomination)
.execute(pool)
.await?;
codes.push(raw);
}
Ok(codes)
}

View File

@@ -35,6 +35,7 @@ pub fn router(state: &AppState) -> Router<AppState> {
"/web/v1/keys/{id}/limit",
axum::routing::patch(update_key_limit),
)
.route("/web/v1/redeem", post(redeem))
.layer(axum::middleware::from_fn_with_state(
state.clone(),
require_session,
@@ -565,3 +566,29 @@ async fn update_key_limit(
}
Ok(StatusCode::NO_CONTENT.into_response())
}
#[derive(Deserialize)]
struct RedeemReq {
code: String,
}
/// `POST /web/v1/redeem` — redeem a single-use top-up code, raising the
/// account's allocation. Returns the new total. Generic 400 for an invalid
/// or already-redeemed code (no oracle).
async fn redeem(
State(state): State<AppState>,
Extension(user): Extension<AuthUser>,
Json(req): Json<RedeemReq>,
) -> WebResult<Response> {
let acct = account_id_for(&state, user.0).await?;
match crate::topup::redeem(&state.pool, acct, &req.code).await {
Ok(new_total) => Ok(Json(json!({ "allocation_total": new_total })).into_response()),
Err(crate::topup::TopUpError::Invalid) => {
Err(WebError::BadRequest("invalid or already-redeemed code"))
}
Err(crate::topup::TopUpError::Db(e)) => {
tracing::error!(error = %e, "redeem db error");
Err(WebError::Internal)
}
}
}

View File

@@ -294,3 +294,132 @@ async fn fingerprint_abuse_silently_deactivates_all_no_clue() {
"deactivated account's key looks like any invalid key"
);
}
#[tokio::test]
async fn topup_redeem_raises_allocation_single_use() {
let Some((base, pool)) = spawn_or_skip("topup_redeem_raises_allocation_single_use").await
else {
return;
};
let email = unique_email();
post(
format!("{base}/web/v1/register"),
json!({"email": email, "password": "password123"}),
None,
)
.await;
pool.execute(
sqlx::query("UPDATE users SET email_verified = true WHERE email = $1").bind(&email),
)
.await
.unwrap();
let token = post(
format!("{base}/web/v1/login"),
json!({"email": email, "password": "password123"}),
None,
)
.await
.json::<Value>()
.await
.unwrap()["token"]
.as_str()
.unwrap()
.to_string();
// Mint a code worth 500_000 (mint path used by the CLI/faucet).
let codes = helexa_upstream::topup::mint(&pool, 500_000, 1, Some("test"))
.await
.unwrap();
let code = &codes[0];
// Redeem → allocation_total rises from the 1_000_000 free grant.
let r = post(
format!("{base}/web/v1/redeem"),
json!({"code": code}),
Some(&token),
)
.await;
assert_eq!(r.status(), 200);
assert_eq!(
r.json::<Value>().await.unwrap()["allocation_total"],
1_500_000
);
// Single-use: a second redemption fails generically (no oracle).
let r = post(
format!("{base}/web/v1/redeem"),
json!({"code": code}),
Some(&token),
)
.await;
assert_eq!(r.status(), 400);
// Unknown code: same generic 400.
let r = post(
format!("{base}/web/v1/redeem"),
json!({"code": "helexa-topup-does-not-exist"}),
Some(&token),
)
.await;
assert_eq!(r.status(), 400);
}
#[tokio::test]
async fn topup_concurrent_double_redeem_one_winner() {
let Some((base, pool)) = spawn_or_skip("topup_concurrent_double_redeem_one_winner").await
else {
return;
};
// Two verified accounts.
let mut tokens = Vec::new();
for _ in 0..2 {
let email = unique_email();
post(
format!("{base}/web/v1/register"),
json!({"email": email, "password": "password123"}),
None,
)
.await;
pool.execute(
sqlx::query("UPDATE users SET email_verified = true WHERE email = $1").bind(&email),
)
.await
.unwrap();
let t = post(
format!("{base}/web/v1/login"),
json!({"email": email, "password": "password123"}),
None,
)
.await
.json::<Value>()
.await
.unwrap()["token"]
.as_str()
.unwrap()
.to_string();
tokens.push(t);
}
let code = helexa_upstream::topup::mint(&pool, 100, 1, None)
.await
.unwrap()
.remove(0);
// Both accounts race to redeem the same code; exactly one wins.
let (a, b) = tokio::join!(
post(
format!("{base}/web/v1/redeem"),
json!({"code": code}),
Some(&tokens[0])
),
post(
format!("{base}/web/v1/redeem"),
json!({"code": code}),
Some(&tokens[1])
),
);
let wins = [a.status(), b.status()]
.iter()
.filter(|s| s.as_u16() == 200)
.count();
assert_eq!(wins, 1, "exactly one redemption wins the single-use code");
}

View File

@@ -8,10 +8,7 @@
"build": "tsc -b && vite build",
"preview": "vite preview",
"lint": "eslint .",
"typecheck": "tsc -b",
"i18n:check": "node ./scripts/check-i18n-keys.mjs",
"i18n:meta": "node ./scripts/check-i18n-metadata.mjs",
"i18n:lang-labels": "node ./scripts/check-i18n-lang-labels.mjs"
"typecheck": "tsc -b"
},
"dependencies": {
"@fingerprintjs/fingerprintjs": "^4.6.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -1,332 +0,0 @@
#!/usr/bin/env node
/**
* Simple i18n key consistency checker.
*
* Compares translation JSONs for all configured languages against the English
* baseline, for each namespace (common, home, chat).
*
* Exit codes:
* - 0: all good
* - 1: inconsistencies found or unexpected error
*/
import fs from "fs";
import path from "path";
import url from "url";
// Adjust these if your i18n structure changes.
const ROOT = path.resolve(
path.dirname(url.fileURLToPath(import.meta.url)),
"..",
);
const RESOURCES_DIR = path.join(ROOT, "src", "i18n", "resources");
// Namespaces to validate.
const NAMESPACES = ["common", "mission", "chat"];
// Languages to validate should track SUPPORTED_LANGUAGES in src/i18n/languages.ts.
// NOTE: This list is intentionally narrower than SUPPORTED_LANGUAGES and does not
// enforce that every supported language has wired resources. That enforcement is
// implemented further below by checking the i18n index.
const LANGUAGES = [
"bg",
"de",
"el",
"en",
"es",
"et",
"fr",
"it",
"pt",
"ro",
"ru",
];
function readJson(filePath) {
try {
const raw = fs.readFileSync(filePath, "utf8");
return JSON.parse(raw);
} catch (err) {
throw new Error(`Failed to read/parse JSON at ${filePath}: ${err.message}`);
}
}
/**
* Recursively walk an object and collect all key paths using dot-notation.
* Arrays are traversed structurally but their indexes are not part of the key path
* (we only care that the shape exists, not array lengths).
*/
function collectKeyPaths(obj, prefix = "") {
const keys = new Set();
if (obj === null || obj === undefined) {
return keys;
}
if (typeof obj !== "object") {
if (prefix) keys.add(prefix);
return keys;
}
// If this is an array, walk its elements but don't add indices to the path.
if (Array.isArray(obj)) {
obj.forEach((item) => {
for (const childKey of collectKeyPaths(item, prefix)) {
keys.add(childKey);
}
});
return keys;
}
// Plain object
for (const [k, v] of Object.entries(obj)) {
const nextPrefix = prefix ? `${prefix}.${k}` : k;
if (v !== null && typeof v === "object") {
for (const childKey of collectKeyPaths(v, nextPrefix)) {
keys.add(childKey);
}
} else {
keys.add(nextPrefix);
}
}
return keys;
}
function diffKeys(baseSet, targetSet) {
const missing = [];
const extra = [];
for (const k of baseSet) {
if (!targetSet.has(k)) missing.push(k);
}
for (const k of targetSet) {
if (!baseSet.has(k)) extra.push(k);
}
missing.sort();
extra.sort();
return { missing, extra };
}
function logHeader(title) {
// Simple console formatting without external deps.
console.log("\n" + "=".repeat(title.length));
console.log(title);
console.log("=".repeat(title.length));
}
function main() {
let hadIssues = false;
console.log("helexa.ai i18n key consistency check");
console.log(`Root: ${ROOT}`);
console.log(`Resources dir: ${RESOURCES_DIR}`);
console.log(`Languages: ${LANGUAGES.join(", ")}`);
console.log(`Namespaces: ${NAMESPACES.join(", ")}`);
// --- Wiring check: ensure each SUPPORTED_LANGUAGES entry has resources registered ---
//
// This prevents cases where a language is:
// - present in LanguageCode and SUPPORTED_LANGUAGES
// - has translation JSONs under src/i18n/resources/<code>/
// but is *not* wired into the i18n `resources` object (and thus silently
// falls back to English at runtime).
try {
const languagesTs = fs.readFileSync(
path.join(ROOT, "src", "i18n", "languages.ts"),
"utf8",
);
const i18nIndexTs = fs.readFileSync(
path.join(ROOT, "src", "i18n", "index.ts"),
"utf8",
);
// Parse SUPPORTED_LANGUAGES from languages.ts
const marker = "export const SUPPORTED_LANGUAGES";
const start = languagesTs.indexOf(marker);
if (start === -1) {
throw new Error("Could not find `SUPPORTED_LANGUAGES` in languages.ts");
}
const after = languagesTs.slice(start);
const bracketIndex = after.indexOf("[");
const closingIndex = after.indexOf("];");
if (bracketIndex === -1 || closingIndex === -1) {
throw new Error("Malformed SUPPORTED_LANGUAGES array");
}
const arraySlice = after.slice(bracketIndex + 1, closingIndex);
const supportedFromTs = new Set();
const codeRegex = /"([^"]+)"/g;
let m;
while ((m = codeRegex.exec(arraySlice)) !== null) {
supportedFromTs.add(m[1]);
}
console.log(
`SUPPORTED_LANGUAGES from TS for wiring check: ${Array.from(
supportedFromTs,
)
.sort()
.join(", ")}`,
);
// Languages that are intentionally "future" and may not yet have
// resources wired in. Keep them out of the hard failure path so
// CI does not break while their translations are still pending.
const FUTURE_LANGUAGES = new Set(["ig", "om", "so", "ti", "wo"]);
// Parse i18n resources object keys from index.ts.
// We look for a block like:
// const resources: Resource = {
// en: { ... },
// fr: { ... },
// };
const resourcesMarker = "const resources: Resource = {";
const resStart = i18nIndexTs.indexOf(resourcesMarker);
if (resStart === -1) {
throw new Error("Could not find `const resources: Resource` in index.ts");
}
const resAfter = i18nIndexTs.slice(resStart + resourcesMarker.length);
const resEndIndex = resAfter.indexOf("};");
if (resEndIndex === -1) {
throw new Error("Malformed resources object in index.ts");
}
const resourcesBlock = resAfter.slice(0, resEndIndex);
// Extract top-level language keys: lines starting with two spaces then <code>:
// Example: " en: {" or " fr: {"
const wiredLangs = new Set();
const lineRegex = /^\s*([a-z]{2}):\s*{\s*$/gm;
let lm;
while ((lm = lineRegex.exec(resourcesBlock)) !== null) {
wiredLangs.add(lm[1]);
}
console.log(
`Languages wired in i18n resources: ${Array.from(wiredLangs)
.sort()
.join(", ")}`,
);
// Now compare SUPPORTED_LANGUAGES vs wired resources.
const missingWiring = [];
for (const code of supportedFromTs) {
if (FUTURE_LANGUAGES.has(code)) {
// These are explicitly allowed to be missing until their
// translations and wiring land.
continue;
}
if (!wiredLangs.has(code)) {
missingWiring.push(code);
}
}
if (missingWiring.length > 0) {
hadIssues = true;
console.error(
"\nERROR: Some SUPPORTED_LANGUAGES codes are not wired into the i18n `resources` object in src/i18n/index.ts:",
);
for (const code of missingWiring.sort()) {
console.error(` - ${code}`);
}
console.error(
"These languages will silently fall back to English at runtime. Ensure that:",
);
console.error(
" 1) ./resources/<code>/{common,home,chat}.json exist, and",
);
console.error(
" 2) They are imported and registered in the `resources` object.",
);
console.error("");
} else {
console.log(
"OK: Every SUPPORTED_LANGUAGES entry (excluding future languages) is wired into the i18n resources object.",
);
}
} catch (err) {
hadIssues = true;
console.error(
"ERROR: Failed while checking SUPPORTED_LANGUAGES wiring in i18n index:",
err.message,
);
}
for (const ns of NAMESPACES) {
logHeader(`Namespace: ${ns}`);
const basePath = path.join(RESOURCES_DIR, "en", `${ns}.json`);
let baseJson;
try {
baseJson = readJson(basePath);
} catch (err) {
console.error(err.message);
hadIssues = true;
continue;
}
const baseKeys = collectKeyPaths(baseJson);
console.log(`Baseline (en) keys: ${baseKeys.size}`);
for (const lang of LANGUAGES) {
if (lang === "en") continue;
const langPath = path.join(RESOURCES_DIR, lang, `${ns}.json`);
if (!fs.existsSync(langPath)) {
console.error(` [${lang}] MISSING file: ${langPath}`);
hadIssues = true;
continue;
}
let langJson;
try {
langJson = readJson(langPath);
} catch (err) {
console.error(` [${lang}] ${err.message}`);
hadIssues = true;
continue;
}
const langKeys = collectKeyPaths(langJson);
const { missing, extra } = diffKeys(baseKeys, langKeys);
if (missing.length === 0 && extra.length === 0) {
console.log(` [${lang}] OK (keys: ${langKeys.size})`);
} else {
hadIssues = true;
console.log(` [${lang}] Issues found:`);
if (missing.length > 0) {
console.log(" Missing keys (present in en, absent here):");
for (const k of missing) {
console.log(` - ${k}`);
}
}
if (extra.length > 0) {
console.log(" Extra keys (present here, absent in en):");
for (const k of extra) {
console.log(` + ${k}`);
}
}
}
}
}
console.log("");
if (hadIssues) {
console.error("i18n check completed with inconsistencies.");
process.exitCode = 1;
} else {
console.log("i18n check completed successfully. All keys are consistent.");
process.exitCode = 0;
}
}
try {
main();
} catch (err) {
console.error("Unexpected error while running i18n check:", err);
process.exitCode = 1;
}

View File

@@ -1,249 +0,0 @@
#!/usr/bin/env node
/**
* helexa.ai i18n language label consistency check
*
* This script validates that for every supported language code in
* `SUPPORTED_LANGUAGES`:
*
* 1. The English `common.lang` map (`src/i18n/resources/en/common.json`)
* contains a human-readable label at `lang.<code>`.
* 2. Optionally (and more leniently), other languages that define a
* `lang` block do not omit supported language codes.
*
* At minimum, it enforces that the English UI always has labels for all
* supported languages, since the header language selector renders
* `t("lang.<code>")` using the active language.
*
* Exit codes:
* - 0: all checks pass
* - 1: one or more inconsistencies found
*/
import fs from "fs";
import path from "path";
import url from "url";
const ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "..");
const I18N_DIR = path.join(ROOT, "src", "i18n");
const LANGUAGES_TS = path.join(I18N_DIR, "languages.ts");
const EN_COMMON_JSON = path.join(I18N_DIR, "resources", "en", "common.json");
/**
* Utility: read a file as UTF-8 or throw with a helpful message.
*/
function readFileOrDie(filePath) {
try {
return fs.readFileSync(filePath, "utf8");
} catch (err) {
throw new Error(`Failed to read ${filePath}: ${err.message}`);
}
}
/**
* Parse SUPPORTED_LANGUAGES from languages.ts.
*
* Expects a definition like:
* export const SUPPORTED_LANGUAGES: LanguageCode[] = [
* "en",
* "bg",
* ];
*/
function parseSupportedLanguages(source) {
const marker = "export const SUPPORTED_LANGUAGES";
const start = source.indexOf(marker);
if (start === -1) {
throw new Error("Could not find `SUPPORTED_LANGUAGES` in languages.ts");
}
const after = source.slice(start);
const bracketIndex = after.indexOf("[");
const closingIndex = after.indexOf("];");
if (bracketIndex === -1 || closingIndex === -1) {
throw new Error("Malformed SUPPORTED_LANGUAGES array");
}
const arraySlice = after.slice(bracketIndex + 1, closingIndex);
const codes = new Set();
const regex = /"([^"]+)"/g;
let m;
while ((m = regex.exec(arraySlice)) !== null) {
codes.add(m[1]);
}
if (codes.size === 0) {
throw new Error("No entries found in SUPPORTED_LANGUAGES");
}
return codes;
}
/**
* Safely read and parse a JSON file.
*/
function readJsonOrDie(filePath) {
const raw = readFileOrDie(filePath);
try {
return JSON.parse(raw);
} catch (err) {
throw new Error(`Failed to parse JSON at ${filePath}: ${err.message}`);
}
}
/**
* Compute set difference: a \ b
*/
function difference(a, b) {
const result = new Set();
for (const x of a) {
if (!b.has(x)) result.add(x);
}
return result;
}
/**
* Convenience: sorted array from a Set.
*/
function toSortedArray(set) {
return [...set].sort();
}
function main() {
console.log("helexa.ai i18n language label check");
console.log(`Root: ${ROOT}`);
console.log(`Languages file: ${LANGUAGES_TS}`);
console.log(`English common.json: ${EN_COMMON_JSON}`);
console.log("");
let hadIssues = false;
// 1) Load SUPPORTED_LANGUAGES
let languagesSource;
try {
languagesSource = readFileOrDie(LANGUAGES_TS);
} catch (err) {
console.error(err.message);
process.exitCode = 1;
return;
}
let supportedLanguages;
try {
supportedLanguages = parseSupportedLanguages(languagesSource);
console.log(
`SUPPORTED_LANGUAGES (${supportedLanguages.size}): ${toSortedArray(
supportedLanguages,
).join(", ")}`,
);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
console.log("");
// 2) Load English common.json and extract lang map
let enCommon;
try {
enCommon = readJsonOrDie(EN_COMMON_JSON);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
const enLangMap = enCommon?.lang ?? {};
if (!enLangMap || typeof enLangMap !== "object") {
console.error("ERROR: `en/common.json` does not contain a `lang` object.");
process.exitCode = 1;
return;
}
const enLangKeys = new Set(Object.keys(enLangMap));
console.log(
`English lang map keys (${enLangKeys.size}): ${toSortedArray(
enLangKeys,
).join(", ")}`,
);
console.log("");
// 3) Ensure every supported language has a corresponding key in en.lang
const missingInEnglish = difference(supportedLanguages, enLangKeys);
if (missingInEnglish.size > 0) {
hadIssues = true;
console.error(
"ERROR: The following supported languages are missing labels in `en/common.json` under `lang`:",
);
for (const code of toSortedArray(missingInEnglish)) {
console.error(` - lang.${code}`);
}
} else {
console.log(
"OK: Every supported language has a `lang.<code>` entry in `en/common.json`.",
);
}
console.log("");
// 4) Optional: scan other locales for informational consistency
// (non-fatal, just warnings).
const resourcesDir = path.join(I18N_DIR, "resources");
let otherLocales = [];
try {
otherLocales = fs
.readdirSync(resourcesDir, { withFileTypes: true })
.filter((d) => d.isDirectory() && d.name !== "en")
.map((d) => d.name);
} catch (err) {
console.warn(
`WARN: Could not list locales in ${resourcesDir}: ${err.message}`,
);
}
for (const locale of otherLocales) {
const filePath = path.join(resourcesDir, locale, "common.json");
if (!fs.existsSync(filePath)) continue;
let localeJson;
try {
localeJson = readJsonOrDie(filePath);
} catch (err) {
console.warn(`WARN: Failed to read ${filePath}: ${err.message}`);
continue;
}
const langObj = localeJson?.lang;
if (!langObj || typeof langObj !== "object") {
// Not all locales need to maintain a full lang map; skip silently.
continue;
}
const localeKeys = new Set(Object.keys(langObj));
const missingHere = difference(supportedLanguages, localeKeys);
if (missingHere.size > 0) {
console.warn(
`WARN: Locale '${locale}' is missing some supported language labels under "lang":`,
);
for (const code of toSortedArray(missingHere)) {
console.warn(` - lang.${code}`);
}
console.warn("");
}
}
if (hadIssues) {
console.error("Language label check completed with inconsistencies.");
process.exitCode = 1;
} else {
console.log("Language label check completed successfully.");
process.exitCode = 0;
}
}
try {
main();
} catch (err) {
console.error("Unexpected error while running language label check:", err);
process.exitCode = 1;
}

View File

@@ -1,327 +0,0 @@
#!/usr/bin/env node
/**
* helexa.ai i18n metadata consistency check
*
* This script validates that:
* - Every `LanguageCode` used in the project appears in `TRANSLATION_PRIORITY`
* - Every `TRANSLATION_PRIORITY` entry refers to a valid `LanguageCode`
* - `REMAINING_LANGUAGES` is a subset of `LanguageCode`
* - `REMAINING_LANGUAGES` is disjoint from `SUPPORTED_LANGUAGES`
*
* It is intentionally implemented as a standalone Node script (no TypeScript
* build step required) and does a very lightweight parse of the TypeScript
* source files to avoid pulling in a full TS compiler.
*
* Exit codes:
* - 0: all checks pass
* - 1: one or more metadata inconsistencies found
*/
import fs from "fs";
import path from "path";
import url from "url";
/**
* Resolve project root as the directory containing this script.
*/
const ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "..");
const I18N_DIR = path.join(ROOT, "src", "i18n");
const LANGUAGES_TS = path.join(I18N_DIR, "languages.ts");
const PRIORITY_TS = path.join(I18N_DIR, "translation-priority.ts");
function readFileOrDie(filePath) {
try {
return fs.readFileSync(filePath, "utf8");
} catch (err) {
console.error(`Failed to read ${filePath}: ${err.message}`);
process.exitCode = 1;
return "";
}
}
/**
* Extract union members from a definition like:
*
* export type LanguageCode =
* | "en"
* | "bg"
* | "cs";
*/
function parseLanguageCodeUnion(source) {
const start = source.indexOf("export type LanguageCode");
if (start === -1) {
throw new Error("Could not find `export type LanguageCode` in languages.ts");
}
const after = source.slice(start);
const eqIndex = after.indexOf("=");
if (eqIndex === -1) {
throw new Error("Malformed LanguageCode definition (no '=')");
}
const unionBlock = after.slice(eqIndex + 1);
const semicolonIndex = unionBlock.indexOf(";");
const unionSlice = semicolonIndex === -1 ? unionBlock : unionBlock.slice(0, semicolonIndex);
const codes = new Set();
const regex = /\|\s*"([^"]+)"/g;
let m;
while ((m = regex.exec(unionSlice)) !== null) {
codes.add(m[1]);
}
if (codes.size === 0) {
throw new Error("No language codes found in LanguageCode union");
}
return codes;
}
/**
* Parse SUPPORTED_LANGUAGES from languages.ts
*
* export const SUPPORTED_LANGUAGES: LanguageCode[] = [
* "en",
* "bg",
* ];
*/
function parseSupportedLanguages(source) {
const marker = "export const SUPPORTED_LANGUAGES";
const start = source.indexOf(marker);
if (start === -1) {
throw new Error("Could not find `SUPPORTED_LANGUAGES` in languages.ts");
}
const after = source.slice(start);
const bracketIndex = after.indexOf("[");
const closingIndex = after.indexOf("];");
if (bracketIndex === -1 || closingIndex === -1) {
throw new Error("Malformed SUPPORTED_LANGUAGES array");
}
const arraySlice = after.slice(bracketIndex + 1, closingIndex);
const codes = new Set();
const regex = /"([^"]+)"/g;
let m;
while ((m = regex.exec(arraySlice)) !== null) {
codes.add(m[1]);
}
if (codes.size === 0) {
throw new Error("No entries found in SUPPORTED_LANGUAGES");
}
return codes;
}
/**
* Parse REMAINING_LANGUAGES from translation-priority.ts
*
* export const REMAINING_LANGUAGES: LanguageCode[] = [
* "tr",
* "pl",
* ];
*/
function parseRemainingLanguages(source) {
const marker = "export const REMAINING_LANGUAGES";
const start = source.indexOf(marker);
if (start === -1) {
// It's valid for this list to not exist; treat as empty if missing
return new Set();
}
const after = source.slice(start);
const bracketIndex = after.indexOf("[");
const closingIndex = after.indexOf("];");
if (bracketIndex === -1 || closingIndex === -1) {
throw new Error("Malformed REMAINING_LANGUAGES array");
}
const arraySlice = after.slice(bracketIndex + 1, closingIndex);
const codes = new Set();
const regex = /"([^"]+)"/g;
let m;
while ((m = regex.exec(arraySlice)) !== null) {
codes.add(m[1]);
}
return codes;
}
/**
* Parse TRANSLATION_PRIORITY entries from translation-priority.ts
*
* export const TRANSLATION_PRIORITY: TranslationPriorityEntry[] = [
* {
* code: "tr",
* bucket: "high",
* nativeSpeakers: "7090M",
* },
* ...
* ];
*/
function parseTranslationPriority(source) {
const marker = "export const TRANSLATION_PRIORITY";
const start = source.indexOf(marker);
if (start === -1) {
throw new Error("Could not find `TRANSLATION_PRIORITY` in translation-priority.ts");
}
const after = source.slice(start);
const bracketIndex = after.indexOf("[");
const closingIndex = after.indexOf("];");
if (bracketIndex === -1 || closingIndex === -1) {
throw new Error("Malformed TRANSLATION_PRIORITY array");
}
const arraySlice = after.slice(bracketIndex + 1, closingIndex);
// Simple heuristic: find code: "<value>" inside objects
const codes = new Set();
const regex = /code:\s*"([^"]+)"/g;
let m;
while ((m = regex.exec(arraySlice)) !== null) {
codes.add(m[1]);
}
if (codes.size === 0) {
throw new Error("No `code` entries found in TRANSLATION_PRIORITY");
}
return codes;
}
function difference(a, b) {
const result = new Set();
for (const x of a) {
if (!b.has(x)) result.add(x);
}
return result;
}
function toSortedArray(set) {
return [...set].sort();
}
function main() {
console.log("helexa.ai i18n metadata consistency check");
console.log(`Root: ${ROOT}`);
console.log(`Languages file: ${LANGUAGES_TS}`);
console.log(`Priority file: ${PRIORITY_TS}`);
console.log("");
const languagesSource = readFileOrDie(LANGUAGES_TS);
const prioritySource = readFileOrDie(PRIORITY_TS);
let hadIssues = false;
let languageCodes;
let supportedLanguages;
let remainingLanguages;
let priorityCodes;
try {
languageCodes = parseLanguageCodeUnion(languagesSource);
console.log(`LanguageCode entries: ${languageCodes.size}`);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
try {
supportedLanguages = parseSupportedLanguages(languagesSource);
console.log(`SUPPORTED_LANGUAGES entries: ${supportedLanguages.size}`);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
try {
remainingLanguages = parseRemainingLanguages(prioritySource);
console.log(`REMAINING_LANGUAGES entries: ${remainingLanguages.size}`);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
try {
priorityCodes = parseTranslationPriority(prioritySource);
console.log(`TRANSLATION_PRIORITY entries: ${priorityCodes.size}`);
} catch (err) {
console.error(`ERROR: ${err.message}`);
process.exitCode = 1;
return;
}
console.log("");
// 1) Every LanguageCode should have a TRANSLATION_PRIORITY entry
const missingInPriority = difference(languageCodes, priorityCodes);
if (missingInPriority.size > 0) {
hadIssues = true;
console.error("ERROR: The following LanguageCode values are missing from TRANSLATION_PRIORITY:");
for (const code of toSortedArray(missingInPriority)) {
console.error(` - ${code}`);
}
} else {
console.log("OK: All LanguageCode values are present in TRANSLATION_PRIORITY.");
}
// 2) Every TRANSLATION_PRIORITY code must be a valid LanguageCode
const unknownPriorityCodes = difference(priorityCodes, languageCodes);
if (unknownPriorityCodes.size > 0) {
hadIssues = true;
console.error("ERROR: The following TRANSLATION_PRIORITY codes are not present in LanguageCode:");
for (const code of toSortedArray(unknownPriorityCodes)) {
console.error(` - ${code}`);
}
} else {
console.log("OK: All TRANSLATION_PRIORITY codes are valid LanguageCode values.");
}
// 3) REMAINING_LANGUAGES must be subset of LanguageCode
const remainingNotInLanguageCode = difference(remainingLanguages, languageCodes);
if (remainingNotInLanguageCode.size > 0) {
hadIssues = true;
console.error("ERROR: The following REMAINING_LANGUAGES entries are not in LanguageCode:");
for (const code of toSortedArray(remainingNotInLanguageCode)) {
console.error(` - ${code}`);
}
} else {
console.log("OK: All REMAINING_LANGUAGES are valid LanguageCode values.");
}
// 4) REMAINING_LANGUAGES must be disjoint from SUPPORTED_LANGUAGES
const remainingThatAreSupported = difference(remainingLanguages, difference(remainingLanguages, supportedLanguages));
// Equivalent to intersection(remainingLanguages, supportedLanguages)
if (remainingThatAreSupported.size > 0) {
hadIssues = true;
console.error("ERROR: The following REMAINING_LANGUAGES are already in SUPPORTED_LANGUAGES:");
for (const code of toSortedArray(remainingThatAreSupported)) {
console.error(` - ${code}`);
}
} else {
console.log("OK: REMAINING_LANGUAGES does not include any SUPPORTED_LANGUAGES.");
}
console.log("");
if (hadIssues) {
console.error("i18n metadata check completed with inconsistencies.");
process.exitCode = 1;
} else {
console.log("i18n metadata check completed successfully. All metadata is consistent.");
process.exitCode = 0;
}
}
try {
main();
} catch (err) {
console.error("Unexpected error while running i18n metadata check:", err);
process.exitCode = 1;
}

View File

@@ -1,411 +0,0 @@
:root {
--color-bg: #f5f7fb;
--color-bg-elevated: #ffffff;
--color-bg-subtle: #eef1f7;
--color-border-subtle: rgba(15, 23, 42, 0.12);
--color-text: #020617;
--color-text-muted: #64748b;
--color-accent: #22d3ee;
--color-accent-hot: #ec4899;
--color-accent-soft: rgba(34, 211, 238, 0.12);
--color-navbar-bg: rgba(255, 255, 255, 0.85);
--color-footer-bg: rgba(255, 255, 255, 0.9);
--shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.08);
--transition-fast: 150ms ease-out;
}
/* Dark theme overrides, driven by ThemeProvider via [data-theme] */
:root[data-theme="dark"] {
--color-bg: #000618;
--color-bg-elevated: #020617;
--color-bg-subtle: #020617;
--color-border-subtle: rgba(148, 163, 184, 0.24);
--color-text: #e2e8f0;
--color-text-muted: #cbd5f5;
--color-accent: #22d3ee;
--color-accent-soft: rgba(34, 211, 238, 0.18);
--color-navbar-bg: rgba(2, 6, 23, 0.92);
--color-footer-bg: rgba(2, 6, 23, 0.96);
--shadow-soft: 0 22px 55px rgba(15, 23, 42, 0.8);
}
/* Global base styles */
html,
body,
#root {
height: 100%;
}
body {
margin: 0;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"SF Pro Text",
"Segoe UI",
sans-serif;
background-color: var(--color-bg);
color: var(--color-text);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
/* Basic RTL reset: when the root document is RTL, ensure logical direction */
html[dir="rtl"] body {
direction: rtl;
}
.app-root {
min-height: 100vh;
background-color: var(--color-bg);
color: var(--color-text);
transition:
background-color var(--transition-fast),
color var(--transition-fast);
}
/* Typography helpers */
.app-main h1,
.app-main h2,
.app-main h3,
.app-main h4,
.app-main h5 {
letter-spacing: 0.02em;
}
.app-main p.lead {
color: var(--color-text-muted);
}
/* Hot pink accent utility for occasional emphasis */
.text-hot-pink {
color: var(--color-accent-hot) !important;
}
/* Header / Navbar */
.app-header {
background-color: var(--color-navbar-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 1px 0 var(--color-border-subtle);
transition:
background-color var(--transition-fast),
box-shadow var(--transition-fast),
border-color var(--transition-fast);
}
.app-header .navbar-brand {
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
font-size: 0.85rem;
color: var(--color-text);
}
.app-header .navbar-brand:hover {
color: var(--color-accent);
}
.app-header .nav-link {
color: var(--color-text-muted);
font-weight: 500;
font-size: 0.9rem;
transition:
color var(--transition-fast),
background-color var(--transition-fast);
}
.app-header .nav-link:hover,
.app-header .nav-link:focus {
color: var(--color-accent);
background-color: transparent;
}
.app-header .nav-link.active {
color: var(--color-accent);
}
.app-header .btn.btn-outline-secondary {
--bs-btn-color: var(--color-text-muted);
--bs-btn-border-color: var(--color-border-subtle);
--bs-btn-hover-bg: var(--color-accent-soft);
--bs-btn-hover-border-color: var(--color-accent);
--bs-btn-hover-color: var(--color-accent);
--bs-btn-focus-shadow-rgb: 34, 211, 238;
padding-inline: 0.55rem;
padding-block: 0.3rem;
border-radius: 999px;
}
/* Language dropdown: theme-aware styling */
.app-header .dropdown-menu-dark {
--bs-dropdown-bg: var(--color-bg-elevated);
--bs-dropdown-link-color: var(--color-text);
--bs-dropdown-link-hover-bg: var(--color-accent-soft);
--bs-dropdown-link-hover-color: var(--color-accent);
--bs-dropdown-link-active-bg: var(--color-accent-soft);
--bs-dropdown-link-active-color: var(--color-accent);
--bs-dropdown-border-color: var(--color-border-subtle);
background-color: var(--color-bg-elevated);
border-color: var(--color-border-subtle);
color: var(--color-text);
}
/* Prevent language dropdown from overflowing off-screen in RTL */
html[dir="rtl"] .app-header .dropdown-menu-end {
left: auto;
right: 0;
transform: none;
}
.app-header .dropdown-menu-dark .dropdown-item.active,
.app-header .dropdown-menu-dark .dropdown-item:active {
background-color: var(--color-accent-soft);
color: var(--color-accent);
}
.app-header .dropdown-menu-dark-context .dropdown-toggle.btn-secondary {
--bs-btn-bg: transparent;
--bs-btn-border-color: var(--color-border-subtle);
--bs-btn-color: var(--color-text-muted);
--bs-btn-hover-bg: var(--color-accent-soft);
--bs-btn-hover-border-color: var(--color-accent);
--bs-btn-hover-color: var(--color-accent);
--bs-btn-focus-shadow-rgb: 34, 211, 238;
}
.app-header .btn.btn-outline-secondary svg {
display: block;
}
/* Generic helper class for mirroring directional icons in RTL.
Used by DirectionalIcon when `mirrorInRtl` is enabled. */
html[dir="rtl"] .diricon-mirror-rtl {
transform: scaleX(-1);
}
/* Provide a visible toggler icon when using default Bootstrap styles */
.navbar-toggler {
border-color: var(--color-border-subtle);
}
.navbar-toggler-icon {
background-image: none;
position: relative;
width: 1.25rem;
height: 1.25rem;
}
.navbar-toggler-icon::before,
.navbar-toggler-icon::after,
.navbar-toggler-icon span {
content: "";
position: absolute;
left: 0;
right: 0;
height: 2px;
border-radius: 999px;
background-color: var(--color-text);
transition:
transform 150ms ease-out,
opacity 150ms ease-out;
}
.navbar-toggler-icon::before {
top: 0.25rem;
}
.navbar-toggler-icon::after {
bottom: 0.25rem;
}
/* Footer */
.app-footer {
background-color: var(--color-footer-bg);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
box-shadow: 0 -1px 0 var(--color-border-subtle);
transition:
background-color var(--transition-fast),
box-shadow var(--transition-fast),
border-color var(--transition-fast);
}
/* Main content layout */
.app-main {
max-width: 1120px;
}
/* When in RTL mode, flip common flex directions and alignment where needed */
html[dir="rtl"] .app-root {
direction: rtl;
text-align: right;
}
html[dir="rtl"] .app-header .navbar-brand {
flex-direction: row-reverse;
}
html[dir="rtl"] .app-header .nav-link {
text-align: right;
}
html[dir="rtl"] .app-header .d-flex.align-items-center.gap-2 {
flex-direction: row-reverse;
}
html[dir="rtl"] .app-main .card-body {
text-align: right;
}
/* Ensure footer content also respects RTL direction */
html[dir="rtl"] .app-footer {
direction: rtl;
text-align: right;
}
.app-main .card,
.app-main .card-body {
background-color: var(--color-bg-elevated);
border-color: var(--color-border-subtle);
}
/* Generic utility styles */
.surface-elevated {
background-color: var(--color-bg-elevated);
border-radius: 1rem;
border: 1px solid var(--color-border-subtle);
box-shadow: var(--shadow-soft);
}
/* Mesh / principle icon containers */
.mesh-icon,
.principle-icon {
width: 2.25rem;
height: 2.25rem;
border-radius: 999px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-border-subtle);
background: radial-gradient(
circle,
var(--color-accent-soft),
transparent 60%
);
color: var(--color-accent);
margin-bottom: 0.35rem;
flex-shrink: 0;
}
.principle-icon {
width: 2rem;
height: 2rem;
}
/* CTA background texture */
.join-mesh-cta {
background:
radial-gradient(
circle at top,
rgba(34, 211, 238, 0.18),
transparent 60%
),
url("/bg-logo-right.png") center/cover no-repeat,
var(--color-bg-elevated);
background-blend-mode: screen, normal;
}
.badge-accent {
background-color: var(--color-accent-soft);
color: var(--color-accent);
border-radius: 999px;
padding: 0.1rem 0.55rem;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* Ensure muted text uses our theme variable instead of Bootstrap defaults */
.text-muted,
small {
color: var(--color-text-muted) !important;
}
/* Small cyan dot used inside hero and CTA badges */
.bg-cyan-500-dot {
display: inline-block;
width: 0.45rem;
height: 0.45rem;
border-radius: 999px;
background: radial-gradient(
circle,
var(--color-accent) 0%,
transparent 65%
);
}
/* Links */
a {
color: var(--color-accent);
text-decoration: none;
}
a:hover {
color: var(--color-accent);
text-decoration: underline;
}
/* Scrollbar theming (WebKit) */
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-thumb {
background-color: rgba(148, 163, 184, 0.6);
border-radius: 999px;
}
:root[data-theme="dark"] *::-webkit-scrollbar-thumb {
background-color: rgba(148, 163, 184, 0.8);
}
/* Code blocks / preformatted text placeholders */
pre,
code {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
pre {
background-color: var(--color-bg-subtle);
border-radius: 0.75rem;
padding: 0.75rem 1rem;
border: 1px solid var(--color-border-subtle);
overflow-x: auto;
}
/* Small-screen adjustments */
@media (max-width: 576px) {
.app-main {
padding-left: 1rem !important;
padding-right: 1rem !important;
}
.app-header .navbar-brand {
font-size: 0.8rem;
}
}

View File

@@ -1,27 +1,12 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import ThemeProvider from "./layout/ThemeProvider";
import Header from "./components/Header";
import Footer from "./components/Footer";
import Mission from "./pages/Mission";
import Chat from "./pages/Chat";
import "./App.css";
import { Container } from "react-bootstrap";
// Composition root: theme + router + layout shell. `/` is the chat
// workspace (F3, anonymous for now); `/mission` (F2) is the EU-sovereignty
// narrative; the auth/account routes (F4) land next.
// F0 scaffold shell. Theming, i18n, routing, the chat workspace, mission
// page and account dashboard land in the F1+ phases.
export default function App() {
return (
<ThemeProvider>
<BrowserRouter>
<div className="d-flex flex-column min-vh-100">
<Header />
<Routes>
<Route path="/" element={<Chat />} />
<Route path="/mission" element={<Mission />} />
</Routes>
<Footer />
</div>
</BrowserRouter>
</ThemeProvider>
<Container className="py-5">
<h1 className="mb-2">helexa.ai</h1>
<p className="text-muted">Public beta coming online.</p>
</Container>
);
}

View File

@@ -1,127 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { isRtlLanguage, type LanguageCode } from "../i18n/languages";
export type Direction = "forward" | "back";
/**
* DirectionalIcon
*
* Small helper component to render direction-aware icons that respect
* the current UI writing direction (LTR vs RTL).
*
* Usage example:
*
* <DirectionalIcon
* direction="forward"
* ltrIcon={FaArrowRight}
* rtlIcon={FaArrowLeft}
* />
*
* - `direction="forward"` means “toward the natural reading direction”
* (right in LTR, left in RTL).
* - `direction="back"` means the opposite (left in LTR, right in RTL).
*
* You can either:
* - pass explicit `ltrIcon` and `rtlIcon` React components, or
* - pass a single `icon` component and set `mirrorInRtl` to flip it
* horizontally when in RTL (via CSS transform).
*
* In most cases, using explicit LTR / RTL icons is clearer and avoids
* surprises with asymmetric icon shapes.
*/
export interface DirectionalIconProps {
/**
* Logical direction relative to reading order.
* - "forward": in the direction of the text flow
* - "back": opposite the direction of the text flow
*/
direction: Direction;
/**
* Icon component to use for LTR contexts (e.g. FaArrowRight).
*/
ltrIcon?: React.ComponentType<{ size?: number | string; className?: string }>;
/**
* Icon component to use for RTL contexts (e.g. FaArrowLeft).
*/
rtlIcon?: React.ComponentType<{ size?: number | string; className?: string }>;
/**
* Single base icon component. When provided together with
* `mirrorInRtl={true}`, it will be mirrored horizontally in RTL.
*/
icon?: React.ComponentType<{ size?: number | string; className?: string }>;
/**
* Whether to flip the `icon` horizontally in RTL.
* Ignored if both `ltrIcon` and `rtlIcon` are supplied.
*/
mirrorInRtl?: boolean;
/**
* Optional size forwarded to the rendered icon.
*/
size?: number | string;
/**
* Additional className to apply to the rendered icon.
*/
className?: string;
}
/**
* Determine if current language is RTL based on i18next language code.
*
* Delegates to the shared `isRtlLanguage` helper from i18n/languages.ts
* so that all RTL logic lives in one place.
*/
const isRtlLanguageCode = (code: string | undefined | null): boolean => {
if (!code) return false;
const lang = code.split("-")[0].toLowerCase() as LanguageCode;
return isRtlLanguage(lang);
};
const DirectionalIcon: React.FC<DirectionalIconProps> = ({
direction,
ltrIcon: LtrIcon,
rtlIcon: RtlIcon,
icon: BaseIcon,
mirrorInRtl = false,
size,
className,
}) => {
const { i18n } = useTranslation();
const isRtl = isRtlLanguageCode(i18n.language);
// If explicit LTR/RTL icons are provided, prefer those.
if (LtrIcon && RtlIcon) {
const IconComponent =
(direction === "forward" && !isRtl) || (direction === "back" && isRtl)
? LtrIcon
: RtlIcon;
return <IconComponent size={size} className={className} />;
}
// Fallback: single base icon, optionally mirrored in RTL.
if (!BaseIcon) {
return null;
}
const shouldMirror =
mirrorInRtl &&
((direction === "forward" && isRtl) || (direction === "back" && !isRtl));
const combinedClassName = [
className,
shouldMirror ? "diricon-mirror-rtl" : null,
]
.filter(Boolean)
.join(" ");
return <BaseIcon size={size} className={combinedClassName} />;
};
export default DirectionalIcon;

View File

@@ -1,23 +0,0 @@
import React from "react";
import { useTranslation } from "react-i18next";
/**
* Footer
*
* Simple application footer used in the main layout.
* Renders a subtle, theme-aware bar with copyright text.
*/
const Footer: React.FC = () => {
const year = new Date().getFullYear();
const { t } = useTranslation("common");
return (
<footer className="app-footer border-top py-3 mt-auto">
<div className="container-fluid text-center text-muted small">
<span>{t("footer.copyright", { year })}</span>
</div>
</footer>
);
};
export default Footer;

View File

@@ -1,140 +0,0 @@
import React from "react";
import { Link, NavLink } from "react-router-dom";
import { Navbar, Container, Nav, Button, Dropdown } from "react-bootstrap";
import { FaRegMoon, FaRegSun } from "react-icons/fa6";
import { useTheme } from "../layout/theme";
import { useTranslation } from "react-i18next";
import { AUTONYM_MAP, type LanguageCode, isRtlLanguage } from "../i18n/languages";
import { getLanguageOptionsByUsage } from "../i18n/translation-priority";
/**
* Top navigation: brand, primary routes (chat at `/`, `/mission`), an
* auth-aware cluster (stubbed until F4 wires sessions), the theme toggle,
* and the language selector.
*
* The language picker is ordered by **estimated usage**
* (getLanguageOptionsByUsage), not alphabetically — a deliberate choice that
* foregrounds helexa's international grounding. Each item shows the autonym
* (language in its own script) plus a secondary label in the current
* language; RTL-aware alignment.
*/
const Header: React.FC = () => {
const { theme, toggleTheme } = useTheme();
const { t, i18n } = useTranslation("common");
const currentLanguage: LanguageCode = (i18n.language.split("-")[0] ||
"en") as LanguageCode;
const isRtl = isRtlLanguage(currentLanguage);
const languageOptions = getLanguageOptionsByUsage();
return (
<Navbar
expand="lg"
className="app-header border-bottom"
variant={theme === "dark" ? "dark" : "light"}
>
<Container fluid>
<Navbar.Brand
as={Link}
to="/"
className="d-flex align-items-center gap-2"
>
<img
src="/logo.png"
alt="helexa logo"
width={28}
height={28}
style={{ borderRadius: "999px" }}
/>
<span className="fw-semibold text-uppercase small tracking-wide">
{t("app.name")}
</span>
</Navbar.Brand>
<Navbar.Toggle aria-controls="main-navbar" />
<Navbar.Collapse id="main-navbar">
<Nav className="me-auto">
<NavLink
to="/"
end
className={({ isActive }): string =>
isActive ? "nav-link active" : "nav-link"
}
>
{t("nav.chat")}
</NavLink>
<NavLink
to="/mission"
className={({ isActive }): string =>
isActive ? "nav-link active" : "nav-link"
}
>
{t("nav.mission")}
</NavLink>
</Nav>
<div className="d-flex align-items-center gap-2">
{/* Auth cluster — plain links until F4 wires session state. */}
<NavLink to="/login" className="nav-link">
{t("nav.login")}
</NavLink>
<NavLink to="/register" className="nav-link">
{t("nav.register")}
</NavLink>
<Button
size="sm"
variant="outline-secondary"
type="button"
onClick={toggleTheme}
aria-label={
theme === "dark"
? t("theme.toggle.toLight")
: t("theme.toggle.toDark")
}
className="d-inline-flex align-items-center justify-content-center"
>
{theme === "dark" ? <FaRegSun size={16} /> : <FaRegMoon size={16} />}
</Button>
<Dropdown
align={isRtl ? "start" : "end"}
className={theme === "dark" ? "dropdown-menu-dark-context" : ""}
>
<Dropdown.Toggle
size="sm"
variant={theme === "dark" ? "secondary" : "outline-secondary"}
id="language-switcher"
>
<span className="me-1" aria-hidden="true">
A
</span>
<span>{AUTONYM_MAP[currentLanguage]}</span>
</Dropdown.Toggle>
<Dropdown.Menu
className={theme === "dark" ? "dropdown-menu-dark" : ""}
>
{languageOptions.map(({ code, autonym }) => (
<Dropdown.Item
key={code}
active={code === currentLanguage}
onClick={() => void i18n.changeLanguage(code)}
className="d-flex align-items-center gap-2"
>
<span>{autonym}</span>
<span className="text-muted small fw-light">
· {t(`lang.${code}`)}
</span>
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
</div>
</Navbar.Collapse>
</Container>
</Navbar>
);
};
export default Header;

View File

@@ -1,72 +0,0 @@
// IndexedDB (Dexie) — the ONLY home for chat history and project
// organisation. Nothing here is ever sent to a server (#69/#F3): the mesh
// serves inference, but conversations live exclusively in the browser.
//
// `owner` namespaces data: `"anon"` for the fingerprinted anonymous visitor,
// or an account id once signed in. On login, anonymous data can be claimed
// into the account (F4) — still purely client-side.
import Dexie, { type Table } from "dexie";
export interface Project {
id: string;
owner: string;
name: string;
createdAt: number;
updatedAt: number;
archived: boolean;
sortOrder: number;
}
export interface Conversation {
id: string;
owner: string;
projectId: string | null; // null → "Unsorted"
title: string;
model: string;
createdAt: number;
updatedAt: number;
pinned: boolean;
}
export type MessageRole = "system" | "user" | "assistant";
export type MessageStatus = "complete" | "streaming" | "error";
export interface Message {
id: string;
conversationId: string;
role: MessageRole;
content: string;
createdAt: number;
status: MessageStatus;
errorCode?: string;
promptTokens?: number;
completionTokens?: number;
}
/** Small key/value store: fingerprint, active conversation, anon usage. */
export interface Meta {
key: string;
value: unknown;
}
class HelexaDB extends Dexie {
projects!: Table<Project, string>;
conversations!: Table<Conversation, string>;
messages!: Table<Message, string>;
meta!: Table<Meta, string>;
constructor() {
super("helexa");
this.version(1).stores({
// Indexes only — Dexie stores the whole object. Compound indexes
// drive the common queries (by owner, by conversation in time order).
projects: "id, owner, [owner+archived], updatedAt",
conversations: "id, owner, projectId, [owner+projectId], updatedAt",
messages: "id, conversationId, [conversationId+createdAt]",
meta: "key",
});
}
}
export const db = new HelexaDB();

View File

@@ -1,154 +0,0 @@
// Typed CRUD + queries over the Dexie store. UI components use the
// `useLiveQuery` hook (dexie-react-hooks) with the list helpers here so the
// sidebar/thread react to writes automatically.
import Dexie from "dexie";
import {
db,
type Conversation,
type Message,
type MessageRole,
type Project,
} from "./db";
function uuid(): string {
return crypto.randomUUID();
}
function now(): number {
return Date.now();
}
// ── projects ────────────────────────────────────────────────────────
export async function listProjects(owner: string): Promise<Project[]> {
const rows = await db.projects.where({ owner }).toArray();
return rows
.filter((p) => !p.archived)
.sort((a, b) => a.sortOrder - b.sortOrder || a.createdAt - b.createdAt);
}
export async function createProject(owner: string, name: string): Promise<string> {
const id = uuid();
const ts = now();
await db.projects.add({
id,
owner,
name,
createdAt: ts,
updatedAt: ts,
archived: false,
sortOrder: ts,
});
return id;
}
export async function renameProject(id: string, name: string): Promise<void> {
await db.projects.update(id, { name, updatedAt: now() });
}
export async function archiveProject(id: string): Promise<void> {
// Detach its conversations to "Unsorted" so nothing is orphaned.
await db.transaction("rw", db.projects, db.conversations, async () => {
await db.projects.update(id, { archived: true, updatedAt: now() });
const convs = await db.conversations.where({ projectId: id }).toArray();
await Promise.all(
convs.map((c) => db.conversations.update(c.id, { projectId: null })),
);
});
}
// ── conversations ───────────────────────────────────────────────────
export async function listConversations(owner: string): Promise<Conversation[]> {
const rows = await db.conversations.where({ owner }).toArray();
return rows.sort(
(a, b) => Number(b.pinned) - Number(a.pinned) || b.updatedAt - a.updatedAt,
);
}
export async function createConversation(
owner: string,
model: string,
projectId: string | null = null,
title = "New chat",
): Promise<string> {
const id = uuid();
const ts = now();
await db.conversations.add({
id,
owner,
projectId,
title,
model,
createdAt: ts,
updatedAt: ts,
pinned: false,
});
return id;
}
export async function renameConversation(id: string, title: string): Promise<void> {
await db.conversations.update(id, { title, updatedAt: now() });
}
export async function moveConversation(
id: string,
projectId: string | null,
): Promise<void> {
await db.conversations.update(id, { projectId, updatedAt: now() });
}
export async function deleteConversation(id: string): Promise<void> {
await db.transaction("rw", db.conversations, db.messages, async () => {
await db.messages.where({ conversationId: id }).delete();
await db.conversations.delete(id);
});
}
// ── messages ────────────────────────────────────────────────────────
export async function listMessages(conversationId: string): Promise<Message[]> {
return db.messages
.where("[conversationId+createdAt]")
.between([conversationId, Dexie.minKey], [conversationId, Dexie.maxKey])
.toArray();
}
export async function addMessage(
conversationId: string,
role: MessageRole,
content: string,
status: Message["status"] = "complete",
): Promise<string> {
const id = uuid();
await db.messages.add({ id, conversationId, role, content, createdAt: now(), status });
await db.conversations.update(conversationId, { updatedAt: now() });
return id;
}
export async function appendToMessage(id: string, delta: string): Promise<void> {
const msg = await db.messages.get(id);
if (!msg) return;
await db.messages.update(id, { content: msg.content + delta });
}
export async function finalizeMessage(
id: string,
patch: Partial<Pick<Message, "status" | "errorCode" | "promptTokens" | "completionTokens">>,
): Promise<void> {
await db.messages.update(id, patch);
}
/** Rewrite all `anon` data to `accountId` on first login (stays local). */
export async function claimAnonymousData(accountId: string): Promise<void> {
await db.transaction("rw", db.projects, db.conversations, async () => {
const projects = await db.projects.where({ owner: "anon" }).toArray();
await Promise.all(
projects.map((p) => db.projects.update(p.id, { owner: accountId })),
);
const convs = await db.conversations.where({ owner: "anon" }).toArray();
await Promise.all(
convs.map((c) => db.conversations.update(c.id, { owner: accountId })),
);
});
}

View File

@@ -1,360 +0,0 @@
import i18n, { type Resource } from "i18next";
import { initReactI18next } from "react-i18next";
import {
SUPPORTED_LANGUAGES,
normalizeLocaleToLanguage,
isRtlLanguage,
} from "./languages";
import type { LanguageCode } from "./languages";
// Core languages
import enCommon from "./resources/en/common.json";
import ruCommon from "./resources/ru/common.json";
import enMission from "./resources/en/mission.json";
import ruMission from "./resources/ru/mission.json";
import enChat from "./resources/en/chat.json";
import ruChat from "./resources/ru/chat.json";
// Scandinavian & Nordic languages
import daCommon from "./resources/da/common.json";
import daMission from "./resources/da/mission.json";
import daChat from "./resources/da/chat.json";
import fiCommon from "./resources/fi/common.json";
import fiMission from "./resources/fi/mission.json";
import fiChat from "./resources/fi/chat.json";
import noCommon from "./resources/no/common.json";
import noMission from "./resources/no/mission.json";
import noChat from "./resources/no/chat.json";
import svCommon from "./resources/sv/common.json";
import svMission from "./resources/sv/mission.json";
import svChat from "./resources/sv/chat.json";
import bgCommon from "./resources/bg/common.json";
import bgMission from "./resources/bg/mission.json";
import bgChat from "./resources/bg/chat.json";
import etCommon from "./resources/et/common.json";
import etMission from "./resources/et/mission.json";
import etChat from "./resources/et/chat.json";
// African & MENA languages
import swCommon from "./resources/sw/common.json";
import swMission from "./resources/sw/mission.json";
import swChat from "./resources/sw/chat.json";
import arCommon from "./resources/ar/common.json";
import arMission from "./resources/ar/mission.json";
import arChat from "./resources/ar/chat.json";
import faCommon from "./resources/fa/common.json";
import faMission from "./resources/fa/mission.json";
import faChat from "./resources/fa/chat.json";
import haCommon from "./resources/ha/common.json";
import haMission from "./resources/ha/mission.json";
import haChat from "./resources/ha/chat.json";
import amCommon from "./resources/am/common.json";
import amMission from "./resources/am/mission.json";
import amChat from "./resources/am/chat.json";
import yoCommon from "./resources/yo/common.json";
import yoMission from "./resources/yo/mission.json";
import yoChat from "./resources/yo/chat.json";
import zuCommon from "./resources/zu/common.json";
import zuMission from "./resources/zu/mission.json";
import zuChat from "./resources/zu/chat.json";
// Darija (Moroccan Arabic)
import maCommon from "./resources/ma/common.json";
import maMission from "./resources/ma/mission.json";
import maChat from "./resources/ma/chat.json";
// European / other languages
import esCommon from "./resources/es/common.json";
import esMission from "./resources/es/mission.json";
import esChat from "./resources/es/chat.json";
import frCommon from "./resources/fr/common.json";
import frMission from "./resources/fr/mission.json";
import frChat from "./resources/fr/chat.json";
import deCommon from "./resources/de/common.json";
import deMission from "./resources/de/mission.json";
import deChat from "./resources/de/chat.json";
import elCommon from "./resources/el/common.json";
import elMission from "./resources/el/mission.json";
import elChat from "./resources/el/chat.json";
import itCommon from "./resources/it/common.json";
import itMission from "./resources/it/mission.json";
import itChat from "./resources/it/chat.json";
import heCommon from "./resources/he/common.json";
import heMission from "./resources/he/mission.json";
import heChat from "./resources/he/chat.json";
import ptCommon from "./resources/pt/common.json";
import ptMission from "./resources/pt/mission.json";
import ptChat from "./resources/pt/chat.json";
import roCommon from "./resources/ro/common.json";
import roMission from "./resources/ro/mission.json";
import roChat from "./resources/ro/chat.json";
import kaCommon from "./resources/ka/common.json";
import kaMission from "./resources/ka/mission.json";
import kaChat from "./resources/ka/chat.json";
import trCommon from "./resources/tr/common.json";
import trMission from "./resources/tr/mission.json";
import trChat from "./resources/tr/chat.json";
import plCommon from "./resources/pl/common.json";
import plMission from "./resources/pl/mission.json";
import plChat from "./resources/pl/chat.json";
import ukCommon from "./resources/uk/common.json";
import ukMission from "./resources/uk/mission.json";
import ukChat from "./resources/uk/chat.json";
import nlCommon from "./resources/nl/common.json";
import nlMission from "./resources/nl/mission.json";
import nlChat from "./resources/nl/chat.json";
import srCommon from "./resources/sr/common.json";
import srMission from "./resources/sr/mission.json";
import srChat from "./resources/sr/chat.json";
import kkCommon from "./resources/kk/common.json";
import kkMission from "./resources/kk/mission.json";
import kkChat from "./resources/kk/chat.json";
import uzCommon from "./resources/uz/common.json";
import uzMission from "./resources/uz/mission.json";
import uzChat from "./resources/uz/chat.json";
/**
* Application translation resources, split by language and namespace.
*
* - `common`: shared UI elements (navigation, theme toggle, etc.)
* - `home`: marketing / narrative copy on the landing page
* - `chat`: copy for the chat workspace
*/
const resources: Resource = {
en: {
common: enCommon,
mission: enMission,
chat: enChat,
},
ru: {
common: ruCommon,
mission: ruMission,
chat: ruChat,
},
bg: {
common: bgCommon,
mission: bgMission,
chat: bgChat,
},
da: {
common: daCommon,
mission: daMission,
chat: daChat,
},
et: {
common: etCommon,
mission: etMission,
chat: etChat,
},
fi: {
common: fiCommon,
mission: fiMission,
chat: fiChat,
},
kk: {
common: kkCommon,
mission: kkMission,
chat: kkChat,
},
uz: {
common: uzCommon,
mission: uzMission,
chat: uzChat,
},
// African & MENA languages (LTR unless marked RTL via isRtlLanguage)
sw: {
common: swCommon,
mission: swMission,
chat: swChat,
},
ar: {
common: arCommon,
mission: arMission,
chat: arChat,
},
fa: {
common: faCommon,
mission: faMission,
chat: faChat,
},
ha: {
common: haCommon,
mission: haMission,
chat: haChat,
},
am: {
common: amCommon,
mission: amMission,
chat: amChat,
},
yo: {
common: yoCommon,
mission: yoMission,
chat: yoChat,
},
zu: {
common: zuCommon,
mission: zuMission,
chat: zuChat,
},
ma: {
common: maCommon,
mission: maMission,
chat: maChat,
},
// European & other languages
es: {
common: esCommon,
mission: esMission,
chat: esChat,
},
fr: {
common: frCommon,
mission: frMission,
chat: frChat,
},
de: {
common: deCommon,
mission: deMission,
chat: deChat,
},
el: {
common: elCommon,
mission: elMission,
chat: elChat,
},
it: {
common: itCommon,
mission: itMission,
chat: itChat,
},
he: {
common: heCommon,
mission: heMission,
chat: heChat,
},
pt: {
common: ptCommon,
mission: ptMission,
chat: ptChat,
},
ro: {
common: roCommon,
mission: roMission,
chat: roChat,
},
ka: {
common: kaCommon,
mission: kaMission,
chat: kaChat,
},
tr: {
common: trCommon,
mission: trMission,
chat: trChat,
},
pl: {
common: plCommon,
mission: plMission,
chat: plChat,
},
uk: {
common: ukCommon,
mission: ukMission,
chat: ukChat,
},
nl: {
common: nlCommon,
mission: nlMission,
chat: nlChat,
},
sr: {
common: srCommon,
mission: srMission,
chat: srChat,
},
no: {
common: noCommon,
mission: noMission,
chat: noChat,
},
sv: {
common: svCommon,
mission: svMission,
chat: svChat,
},
};
// Determine initial language from browser, normalised to language-only.
const browserLang: LanguageCode =
typeof navigator !== "undefined"
? normalizeLocaleToLanguage(navigator.language)
: "en";
// Keep document direction (ltr/rtl) in sync with the active language.
if (typeof document !== "undefined") {
document.documentElement.dir = isRtlLanguage(browserLang) ? "rtl" : "ltr";
}
/**
* Initialize i18next with React bindings.
*
* This module is imported once in src/main.tsx before any React
* rendering so that `useTranslation` is ready everywhere.
*/
i18n.use(initReactI18next).init({
resources,
lng: browserLang,
fallbackLng: "en",
supportedLngs: SUPPORTED_LANGUAGES,
ns: ["common", "mission", "chat"],
defaultNS: "common",
// Because we control the keys and interpolate only simple values.
interpolation: {
escapeValue: false,
},
// For now we stay language-only; we already normalise the browser locale.
load: "languageOnly",
// Be explicit about react options for clarity.
react: {
useSuspense: false,
},
});
// Ensure that when the language changes at runtime, document direction
// tracks the new language's natural writing direction.
i18n.on("languageChanged", (lng) => {
if (typeof document === "undefined") return;
const lang = normalizeLocaleToLanguage(lng);
document.documentElement.dir = isRtlLanguage(lang) ? "rtl" : "ltr";
});
export default i18n;

View File

@@ -1,232 +0,0 @@
import type { Resource } from "i18next";
/**
* Supported language codes for the application.
*
* For the foreseeable future we deliberately stay at the language level
* (e.g. "en", "ru") rather than full locales (e.g. "en-GB") to keep
* translation overhead manageable.
*
* When you add a new language:
* - Add its code to `SUPPORTED_LANGUAGES`
* - Add its autonym to `AUTONYM_MAP`
* - Add its resources to the i18n configuration
*/
export type LanguageCode =
| "en"
| "bg"
| "cs"
| "da"
| "de"
| "el"
| "es"
| "he"
| "et"
| "ar"
| "fa"
| "fi"
| "sw"
| "ha"
| "am"
| "yo"
| "zu"
| "fr"
| "ma"
| "ga"
| "hr"
| "hu"
| "is"
| "it"
| "ka"
| "lt"
| "lv"
| "mt"
| "nl"
| "no"
| "pl"
| "pt"
| "ro"
| "ru"
| "sk"
| "sl"
| "sr"
| "sv"
| "tr"
| "uk"
| "bs"
| "mk"
| "kk"
| "uz"
| "ig"
| "om"
| "so"
| "ti"
| "wo";
/**
* Ordered list of languages enabled in the UI.
*
* For now you can keep `SUPPORTED_LANGUAGES` in sync with the
* actually configured i18n resources (e.g. ["en", "ru"]) and grow
* it as translations land.
*/
export const SUPPORTED_LANGUAGES: LanguageCode[] = [
"bg",
"da",
"de",
"el",
"en",
"es",
"et",
"fi",
"fr",
"he",
"it",
"ka",
"kk",
"nl",
"no",
"sv",
"uz",
"ar",
"fa",
"sw",
"ha",
"am",
"yo",
"zu",
"ma",
"pl",
"pt",
"ro",
"ru",
"sr",
"tr",
"uk",
// Future AfroEuropean / Eurasian candidates; keep out of SUPPORTED_LANGUAGES until translated:
// "ig", // Igbo
// "om", // Oromo
// "so", // Somali
// "ti", // Tigrinya
// "wo", // Wolof
];
/**
* Autonym map.
*
* Each language is named in its own language so that a user only
* needs to know their own language to find it in the selector.
*/
export const AUTONYM_MAP: Record<LanguageCode, string> = {
en: "English",
bg: "български",
cs: "čeština",
da: "dansk",
de: "Deutsch",
el: "Ελληνικά",
es: "español",
et: "eesti",
he: "עברית",
ar: "العربية",
fa: "فارسی",
sw: "Kiswahili",
ha: "Hausa",
am: "አማርኛ",
yo: "Yorùbá",
zu: "isiZulu",
ma: "Darija",
fi: "suomi",
fr: "français",
ga: "Gaeilge",
hr: "hrvatski",
hu: "magyar",
is: "íslenska",
it: "italiano",
lt: "lietuvių",
lv: "latviešu",
mt: "Malti",
nl: "Nederlands",
no: "norsk",
pl: "polski",
pt: "português",
ro: "română",
ru: "русский",
sk: "slovenčina",
sl: "slovenščina",
sr: "српски",
sv: "svenska",
tr: "Türkçe",
uk: "українська",
bs: "bosanski",
mk: "македонски",
ka: "ქართული", // Georgian
kk: "қазақ тілі", // Kazakh
uz: "oʻzbekcha", // Uzbek
ig: "Igbo", // Igbo
om: "Afaan Oromoo", // Oromo
so: "Af-Soomaali", // Somali
ti: "ትግርኛ", // Tigrinya
wo: "Wolof", // Wolof
};
/**
* Normalize a full locale (e.g. "en-GB") down to a `LanguageCode`.
*
* - Uses the first segment of the locale (before "-")
* - Falls back to "en" if the language is unsupported or invalid
*/
export const normalizeLocaleToLanguage = (
locale: string | null | undefined,
): LanguageCode => {
if (!locale) return "en";
const lang = locale.split("-")[0]?.toLowerCase() ?? "en";
if (SUPPORTED_LANGUAGES.includes(lang as LanguageCode)) {
return lang as LanguageCode;
}
return "en";
};
/**
* Build a stable list of language options for UI components
* such as dropdowns.
*/
export type LanguageOption = {
code: LanguageCode;
autonym: string;
};
export const getLanguageOptions = (): LanguageOption[] =>
[...SUPPORTED_LANGUAGES]
.map((code) => ({
code,
autonym: AUTONYM_MAP[code],
}))
.sort((a, b) => a.autonym.localeCompare(b.autonym));
/**
* Utility to derive i18next `supportedLngs` from our language codes,
* so configuration can import from this module instead of hardcoding
* the list in multiple places.
*/
export const getSupportedLngsForI18Next = (): Resource["en"] extends never
? string[]
: string[] => {
// i18next accepts string[], while we keep a stricter LanguageCode[]
return [...SUPPORTED_LANGUAGES];
};
/**
* Languages whose natural writing direction is right-to-left.
*
* This is used by layout code (outside this module) to switch
* document direction and RTL-aware styling when needed.
*/
export const RTL_LANGUAGES: LanguageCode[] = ["he", "ar", "fa", "ma"];
/**
* Utility to check whether a given language code is RTL.
*/
export const isRtlLanguage = (code: LanguageCode): boolean =>
RTL_LANGUAGES.includes(code);

View File

@@ -1,17 +0,0 @@
{
"title": "የውይይት ቦታ",
"badge": "ውይይት",
"lead": "ይህ የውይይት እይታ ነው። የውይይት ሎጂክዎን እና የተጠቃሚ በስተጀርባ ክፍሎችን ወደዚህ ገፅ ያገናኙ።",
"transcriptPlaceholder": "የውይይቱ ሪኮርድ እዚህ ይታያል። የሞዴሉን እና የተጠቃሚውን መልዕክቶች በሚንቀሳቀስ ኮንቴይነር ውስጥ ያቀርቡ፣ приወይም በዙር ዙር በመከፈል ማቅረብ ይችላሉ።",
"inputPlaceholder": "ውይይትን ለመጀምር መልዕክት ይፃፉ…",
"send": "መላክ",
"clear": "ማጽዳት",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "መነሻ ገፅ",
"docs": "ሰነዶች",
"chat": "ውይይት",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "ወደ ብርሃን ሁኔታ መቀየር",
"toDark": "ወደ ጨለማ ሁኔታ መቀየር"
}
},
"lang": {
"bg": "ቡልጋሪኛ",
"de": "ጀርመንኛ",
"el": "ግሪከኛ",
"en": "እንግሊዝኛ",
"es": "ስፓኒሽኛ",
"et": "ኤስቶኒያን",
"fr": "ፈረንሳይኛ",
"he": "እብራስጥ",
"it": "ጣሊያንኛ",
"nl": "ደችኛ",
"da": "ዴኒሽኛ",
"fi": "ፊኒሽኛ",
"no": "ኖርዌጂያንኛ",
"sv": "ስዊድንኛ",
"ar": "ዐርቢኛ",
"fa": "ፐርሺያኛ",
"sw": "ስዋሂሊኛ",
"ha": "ሃውሳኛ",
"am": "አማርኛ",
"yo": "ዮሩባ",
"zu": "ዙሉ",
"ma": "ዳሪጃ",
"ig": "ኢግቦኛ",
"ka": "ጊዮርጂያንኛ",
"kk": "ካዛክኛ",
"om": "ኦሮሞኛ",
"so": "ሶማሊኛ",
"ti": "ትግርኛ",
"uz": "ኡዝቤክኛ",
"wo": "ዎሎፍኛ",
"pl": "ፖሊሽኛ",
"pt": "ፖርቱጋልኛ",
"ro": "ሮማኒያን",
"ru": "ራሽኛ",
"sr": "ሰርቢኛ",
"tr": "ቱርክኛ",
"uk": "ዩክሬንኛ"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "አዲስ የአእምሮ ቅርጽ",
"title": "አዲስ የአእምሮ ቅርጽ",
"lead": "Helexa በተገናኙ ነጻ ኦፕሬተሮች የሚነሳ ራሷን የሚያደርግ የኤ.አይ. መረብ ነው። ክፍት ነው። ተበታተነ። በመደጋገም ይቀየራል።",
"ctaJoinMesh": "ወደ መረቡ ይቀላቀሉ",
"ctaFollowProject": "ፕሮጀክቱን ይከተሉ",
"subcopy": "AI ክፍት፣ ጠንካራ እና የሚጋራ መሆን አለበት በሚሉ ለኦፕሬተሮች፣ ለአበልጣጫዎች እና ለማህበረሰቦች ተሠርቷል።",
"imageAlt": "የHelexa ሄሊክስ ምስል"
},
"intent": {
"title": "Helexa ለምን አለች",
"p1": "AI በምድር ላይ በጣም ኃይለኛው መሠረታዊ መዋቅር እየሆነ ነው። ነገር ግን ዛሬ፣ ያ ኃይል በግል ቅድሚያዎች፣ በጂኦግራፊያዊ ገደቦች እና በደካማ ኢኮኖሚዎች የተሳሰበ በጥቂት ኩባንያዎች ውስጥ ተሰብስቧል።",
"p2Intro": "Helexa የተለየ ነገር ታስባለች፦",
"bullet1": "ከአንድ ቦታ ሳይሆን ከሁሉም ቦታ የሚያድግ አእምሮ።",
"bullet2": "ማንኛውም ሰው ሊያስተዋውቅበትና ሊጠቀምበት የሚችለው መረብ።",
"bullet3": "በትእዛዝ ሳይሆን በፍላጎት ላይ የሚማር ስርዓት።",
"bullet4": "ማህበረሰቦችን ለመተካት ሳይሆን የሚጠናከል ቴክኖሎጂ።",
"closing": "Helexa መድረክ አይደለችም። ደመና አይደለችም።\nመረብ ነች — የተወለደ እና የሚሻሻል የነጻ ኦፕሬተሮች መረብ ሆና አዲስ ዓይነት አእምሮ የምታቀና."
},
"whyNow": {
"title": "ለAI የሚለው የመቀየር ጊዜ",
"problemTitle": "ችግኙ",
"problemBullet1": "AI ከቀድሞው ማንኛውም ቴክኖሎጂ የማይተካ ፍጥነት በመሰብሰብ ላይ ነው።",
"problemBullet2": "የኮምፒውተር ኃይል መዳረሻ ችሎታን ያወራል፣ ይህም መዳረሻ ግን በቀስታ እየተጠበቀ ነው።",
"problemBullet3": "የወጪ መከልከያዎች ምርምር እና የኢንተርፕራይዝ ጀማሪዎችን እና ማህበረሰቦችን እያወጡ ነው።",
"problemBullet4": "የጄኦፖለቲካ እና የህግ ግፊት የዓለም አቀፍ መዳረሻን እያቀረበ ነው።",
"problemBullet5": "የሞዴሎች ፈጣሪዎችና የሀርድዌር ኦፕሬተሮች ብዙ ጊዜ በራሳቸው የሚፈጥሩትን ዋጋ አያጋሩም።",
"opportunityTitle": "እድሉ",
"opportunityIntro": "ግን የተሰራጨ ዓለም የሚቻል ነው።",
"opportunityBullet1": "ሺዎች የሚቆጠሩ የGPU ካርዶች በዓለም ዙሪያ አሁንም በትንሽ እየተጠቀሙባቸው ነው።",
"opportunityBullet2": "ኦፕሬተሮች ለየሚሰጡት ኮምፒውተር ኃይል እውነተኛ እና ፍትሃዊ ክፍያ ይፈልጋሉ።",
"opportunityBullet3": "ዲቨሎፐሮች ክፍት እና ከመቆጣጠር የተጠበቀ መሠረታዊ መዋቅር ይፈልጋሉ።",
"opportunityBullet4": "ማህበረሰቦች በዲጂታል ስርዓቶቻቸው ውስጥ ስብስብ መንግስታዊነትን እና ጽኑ መሆን ይፈልጋሉ።",
"opportunityBullet5": "የAI እድገት ባለመለኪያ ሁኔታ ከባለሙያ ደመናዎች በላይ ቀድሞ ሄዷል — አዳዲስ ቅርጾች ያስፈልጋሉ።",
"opportunityClosing": "Helexa እነዚህ ኃይሎች በአንድ ጊዜ የሚገናኙበት ጊዜ ነው።"
},
"howItWorks": {
"title": "መረቡ እንዴት እንደሚሰራ",
"operators": {
"eyebrow": "ኦፕሬተሮች ኖዶችን ይያዙ",
"title": "ማንኛውም ሰው የኮምፒውተር ኃይል ሊያቀርብ ይችላል።",
"body": "ኦፕሬተሮች የHelexa ኖዶችን ያስኬዳሉ። የማንኛውንም ሞዴል እንዲሰሩ ያስችላሉ። በሀርድዌሩና በኢኮኖሚያዊ ውሳኔዎቻቸው ላይ ቁጥጥር ይጠብቃሉ። ምንም የፈቃድ ሂደት የለም፣ ጠባቂ መከላከያ የለም።"
},
"routing": {
"eyebrow": "መረቡ አእምሮን ይመራል",
"title": "ፍላጎት በመረቡ ውስጥ ይፈሳሳል።",
"body": "Helexa እየማረከች የቆየበት አቅም ወዴት እንደሚገኝ፣ ፍላጎት የሚጨምርበት ቦታ የት እንደሆነ እና ምን ኖድ ለማቅረብ ተስማሚ እንደሆነ ትማራለች። መረቡ በተፈጥሯዊ መንገድ ይለመዳል — እያደገ ያለ ሄሊክስ እንደሆነ።"
},
"value": {
"eyebrow": "ዋጋው ወደ ኋላ ይመለሳል",
"title": "ሥራ ታረጋገጠ። ክፍያ ፍትሃዊ ነው።",
"body": "እያንዳንዱ ስራ ክሪፕቶግራፊ የተፈረመ ደረሰኝ ይይዛል። ኦፕሬተሮች በሚሰጡት አእምሮ ላይ ገንዘብ ያገኛሉ። የመድረክ ታክስ የለም። ግልጽ ካልሆነ የክፍያ ሂደት የለም።"
}
},
"principles": {
"title": "በመድረኮች ላይ ሳይሆን በመርሆዎች ላይ የተገነባ",
"distributed": {
"title": "በንድፍ የተበታተነ",
"body": "አንድ የብቻ የእርምጃ መደበቂያ የለም። ማዕከላዊ ሥልጣን የለም። በእያንዳንዱ አዲስ ኦፕሬተር ጋር የሚጠናከር መረብ ነው።"
},
"participation": {
"title": "ተሳትፎ ለሁሉም ክፍት",
"body": "ኮምፒውተር ኃይል ካለህ ልዩ ልዩ ድርሻ ሊያስተዋውቅ ትችላለህ። መረቡ ሁሉንም ይቀበላል — ከኤጀጅ መሣሪያዎች፣ እስከ የቤት አገልጋዮች እና የዳታ ማእከላት ድረስ።"
},
"fairness": {
"title": "ፍትህና ተመልካችነት",
"body": "ገቢዎች በእውነተኛ ስራ ላይ ተመስርተዋል፣ በክሪፕቶግራፊ መሠረት ተረጋግጠዋል። ጥቁር ሳጥን የለም። የተደበቀ ክፍያ የለም።"
},
"evolving": {
"title": "የሚያድግ አእምሮ",
"body": "መረቡ ከፍላጎት ይማራል። ሞዴሎች የሚፈለጉበት ቦታ ላይ ይጫናሉ። አእምሮ በተባባሪነት ይበተናል።"
}
},
"roadAhead": {
"title": "Helexa ምን ለመሆን እየተሻሻለች ነው",
"p1": "ለሁሉም የሚያገለግል የዓለም አቀፍ የአእምሮ ንብርብር፣ በሄሊክስ ያለ የኖዶችና የማህበረሰቦች መረብ የሚደገፍ።",
"p2": "ማቋረጥ፣ ፖለቲካ፣ ነጠላ ባለቤቶች እና ወደፊት ሊከሰት በሚችል እንኳን የሚቋቋም መረብ።",
"p3": "ኦፕሬተሮች፣ አበልጣጫዎች እና ተጠቃሚዎች ሁሉም ተዋጊ የሚሆኑበት አዲስ የኢኮኖሚ ሞዴል።",
"p4": "ፈጠራ ከጠርዝ የሚያድግ፣ ከማዕከል ሳይሆን፣ የሥራ አካባቢ ኢኮሲስተም።",
"card": {
"eyebrow": "የራዕይ አጭር እትም",
"title": "ወደ የተጋራ የአእምሮ መረብ",
"body": "Helexa ገና በቀዳሚ ደረጃ ላይ ይ beታል። ሀሳቦቹ ከአሁን እና ከተፈጠረው ተቀባይነት የበለጠ ትልቅ ናቸው — ይህም በተወሰነ ዓላማ ነው። መረቡ በእያንዳንዱ ደረጃ ይበቃል፣ ኦፕሬተሮችና አበልጣጫዎችም እድገቷን በጋራ ይፀናሉ።"
}
},
"joinMesh": {
"badge": "የመጀመሪያ ደረጃ",
"title": "መረቡ እየተፈጠረ ነው።",
"titleHighlight": "እርስዎም ክፍል ሆነው ሊሳተፉበት ይችላሉ።",
"lead": "ሀርድዌር ብትከናወኑ፣ ሞዴሎችን ብታበርኩ፣ ወይም በአጠቃላይ AI እንዴት እንደሚመራ ብታስቡ ሆነው፣ በዚህ መረብ ውስጥ ለእርስዎ የተዘጋበ ቦታ አለ።",
"ctaRunNode": "ኖድ ያስኬዱ (በቅርቡ)",
"ctaJoinAnnouncements": "የቀድሞ ማስታወቂያዎችን ይቀበሉ",
"ctaExploreCode": "ኮድን ያስሱ",
"footer": "የተጣሉ ፍራንቻዎች የሉም። አንድ ብቻ ባለቤት የለም። የሚመጣውን የአእምሮ ወደፊት በሚያቀና መልኩ በሰዎች፣ በሀርድዌር እና በሀሳቦች የተሠራ መረብ ብቻ ነው።"
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "مساحة محادثة",
"badge": "محادثة",
"lead": "هذه هي واجهة المحادثة. قم بتوصيل منطق الحوار الخاص بك ومكوّنات واجهة المستخدم بهذه الصفحة.",
"transcriptPlaceholder": "سجل المحادثة سيظهر هنا. اعرض رسائل النموذج والمستخدم في حاوية قابلة للتمرير، ويمكنك تجميعها حسب أدوار الحوار إذا رغبت.",
"inputPlaceholder": "اكتب رسالة لبدء المحادثة…",
"send": "إرسال",
"clear": "مسح",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "الصفحة الرئيسية",
"docs": "التوثيق",
"chat": "المحادثة",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "التبديل إلى الوضع الفاتح",
"toDark": "التبديل إلى الوضع الداكن"
}
},
"lang": {
"bg": "البلغارية",
"de": "الألمانية",
"el": "اليونانية",
"en": "الإنجليزية",
"es": "الإسبانية",
"et": "الإستونية",
"fr": "الفرنسية",
"he": "العبرية",
"it": "الإيطالية",
"nl": "الهولندية",
"da": "الدنماركية",
"fi": "الفنلندية",
"ar": "العربية",
"fa": "الفارسية",
"sw": "السواحيلية",
"ha": "الهوسا",
"am": "الأمهرية",
"yo": "اليوربا",
"zu": "الزولو",
"ma": "الدارجة المغربية",
"ig": "الإيجبو",
"ka": "الجورجية",
"kk": "الكازاخية",
"no": "النرويجية",
"om": "الأورومو",
"so": "الصومالية",
"sv": "السويدية",
"ti": "التيغرينية",
"uz": "الأوزبكية",
"wo": "الولوف",
"pl": "البولندية",
"pt": "البرتغالية",
"ro": "الرومانية",
"ru": "الروسية",
"sr": "الصربية",
"tr": "التركية",
"uk": "الأوكرانية"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "شكل جديد من الذكاء",
"title": "شكل جديد من الذكاء",
"lead": "هيليكسـا هي شبكة ذكاء اصطناعي ذاتية التنظيم، يشغّلها مشغّلون مستقلون. مفتوحة. موزعة. تتطور باستمرار.",
"ctaJoinMesh": "انضم إلى الشبكة",
"ctaFollowProject": "تابع المشروع",
"subcopy": "صُممت للمشغّلين والبنّائين والمجتمعات التي تؤمن بأن الذكاء الاصطناعي يجب أن يكون مفتوحًا، مرنًا، ومشتركًا.",
"imageAlt": "تصور حلزوني لشكل Helexa"
},
"intent": {
"title": "لماذا وُجدت Helexa",
"p1": "الذكاء الاصطناعي يتحول إلى أقوى بنية تحتية على كوكب الأرض. لكن اليوم، هذه القوة مركّزة في أيدي عدد قليل من الشركات، تشكلها أولويات خاصة، وحدود جغرافية، واقتصادات هشة.",
"p2Intro": "هيليكسـا تتخيل شيئًا مختلفًا:",
"bullet1": "ذكاء ينمو من كل مكان، لا من نقطة واحدة.",
"bullet2": "شبكة يمكن للجميع فيها أن يساهموا ويستفيدوا.",
"bullet3": "نظام يتكيّف مع الطلب، لا مع الأوامر الفوقية.",
"bullet4": "تكنولوجيا تقوّي المجتمعات بدلًا من أن تستبدلها.",
"closing": "هيليكسـا ليست منصة. وليست سحابة.\nإنها شبكة — نسيج حيّ ومتطوّر من مشغّلين مستقلين يشكّلون نوعًا جديدًا من الذكاء."
},
"whyNow": {
"title": "نقطة تحوّل للذكاء الاصطناعي",
"problemTitle": "المشكلة",
"problemBullet1": "الذكاء الاصطناعي يتمركز أسرع من أي تكنولوجيا سبقته.",
"problemBullet2": "الوصول إلى القدرة الحاسوبية يحدد الإمكانات، وهذا الوصول يضيق باستمرار.",
"problemBullet3": "حواجز التكلفة تستبعد الباحثين والشركات الناشئة والمجتمعات.",
"problemBullet4": "الضغوط الجيوسياسية والتنظيمية تهدّد التوافر العالمي.",
"problemBullet5": "مُنشئو النماذج ومشغّلو العتاد نادرًا ما يشاركون في القيمة التي ينتجونها.",
"opportunityTitle": "الفرصة",
"opportunityIntro": "لكن عالمًا موزعًا ممكن.",
"opportunityBullet1": "آلاف وحدات الـGPU حول العالم تعمل بأقل من طاقتها.",
"opportunityBullet2": "المشغّلون يريدون تعويضًا عادلًا عن الحوسبة التي يقدّمونها.",
"opportunityBullet3": "المطورون يريدون بنية تحتية مفتوحة ومقاوِمة للرقابة.",
"opportunityBullet4": "المجتمعات تريد سيادة ومرونة في أنظمتها الرقمية.",
"opportunityBullet5": "نمو الذكاء الاصطناعي تجاوز البنى السحابية التقليدية — نحن بحاجة إلى أشكال جديدة.",
"opportunityClosing": "هيليكسـا هي لحظة تلاقي هذه القوى."
},
"howItWorks": {
"title": "كيف تتشكّل الشبكة",
"operators": {
"eyebrow": "المشغّلون يديرون العقد",
"title": "أي شخص يمكنه المساهمة بقوة حاسوبية.",
"body": "المشغّلون يشغّلون عُقد Helexa. يقررون أي النماذج يستضيفون. يبقون متحكمين في عتادهم واقتصادهم. لا موافقات، ولا حراس بوابة."
},
"routing": {
"eyebrow": "الشبكة توجّه الذكاء",
"title": "الطلب يتدفق عبر الشبكة.",
"body": "هيليكسـا تتعلم أين توجد السعة، وأين يرتفع الطلب، وأي العقد أنسب لخدمة الطلبات. الشبكة تتكيّف بشكل عضوي — مثل حلزون ينمو."
},
"value": {
"eyebrow": "القيمة تعود إلى المصدر",
"title": "العمل مُثبت. والدفع عادل.",
"body": "كل مهمة تحمل إيصالًا تشفيريًا. يكسب المشغّلون مقابل الذكاء الذي يساعدون في توفيره. لا ضرائب منصة. لا فواتير غامضة."
}
},
"principles": {
"title": "مبنية على مبادئ، لا على منصات",
"distributed": {
"title": "موزعة بالتصميم",
"body": "لا توجد نقطة فشل واحدة. لا توجد سلطة مركزية. شبكة تقوى مع كل مشغّل جديد."
},
"participation": {
"title": "مشاركة مفتوحة",
"body": "إذا كان لديك قدرة حاسوبية، يمكنك المساهمة. الشبكة ترحّب بالجميع — من الحافة، إلى الخوادم المنزلية، إلى مراكز البيانات."
},
"fairness": {
"title": "العدالة والشفافية",
"body": "الأرباح مبنية على عمل حقيقي، مُثبت تشفيريًا. لا صناديق سوداء. لا رسوم خفية."
},
"evolving": {
"title": "ذكاء يتطور باستمرار",
"body": "الشبكة تتعلم من الطلب. تُحمَّل النماذج حيث تكون مطلوبة. الذكاء ينتشر من خلال التعاون."
}
},
"roadAhead": {
"title": "إلى ماذا تطمح Helexa أن تصبح",
"p1": "طبقة ذكاء عالمية تنتمي للجميع، مدفوعة بحلزونة من العقد والمجتمعات.",
"p2": "شبكة مقاومة للانقطاعات، والسياسة، والاحتكارات، والإخفاق.",
"p3": "نموذج اقتصادي جديد يستفيد منه المشغّلون والبنّاؤون والمستخدمون جميعًا.",
"p4": "نظام بيئي تنمو فيه الابتكارات من الأطراف — لا من المركز.",
"card": {
"eyebrow": "لمحة عن الرؤية",
"title": "نحو شبكة ذكاء مشتركة",
"body": "هيليكسـا في مرحلة مبكرة. الأفكار أكبر من التنفيذ الحالي — وهذا مقصود. الشبكة ستنمو تدريجيًا، بينما يشكّل المشغّلون والبنّاؤون مسار تطورها."
}
},
"joinMesh": {
"badge": "مرحلة مبكرة",
"title": "الشبكة قيد التشكل.",
"titleHighlight": "ويمكنك أن تكون جزءًا منها.",
"lead": "سواءً كنت تدير عتادًا، تبني نماذج، أو يهمّك ببساطة كيف يُدار الذكاء الاصطناعي — هناك مكان لك داخل الشبكة.",
"ctaRunNode": "تشغيل عقدة (قريبًا)",
"ctaJoinAnnouncements": "انضم إلى الإعلانات المبكرة",
"ctaExploreCode": "استكشف الشفرة",
"footer": "لا حدائق مسوّرة. لا مالك واحد. مجرد شبكة من أشخاص وعتاد وأفكار — تصوغ مستقبلًا مختلفًا للذكاء."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Работно пространство за разговори",
"badge": "Чат",
"lead": "Това е изгледът за чат. Тук можеш да свържеш своята логика за разговори и потребителски интерфейс.",
"transcriptPlaceholder": "Тук ще се показва историята на чата. Визуализирай съобщенията от модела и потребителя в превъртащ се контейнер, по желание групирани по ход.",
"inputPlaceholder": "Напиши съобщение, за да започнеш разговора…",
"send": "Изпрати",
"clear": "Изчисти",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Начало",
"docs": "Документация",
"chat": "Чат",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Превключване към светла тема",
"toDark": "Превключване към тъмна тема"
}
},
"lang": {
"bg": "български",
"de": "немски",
"el": "гръцки",
"en": "английски",
"es": "испански",
"et": "естонски",
"fr": "френски",
"he": "иврит",
"it": "италиански",
"nl": "Нидерландски",
"da": "Датски",
"fi": "Фински",
"no": "Норвежки",
"sv": "Шведски",
"ar": "Арабски",
"fa": "персийски",
"sw": "суахили",
"ha": "хауза",
"am": "амхарски",
"yo": "йоруба",
"zu": "зулу",
"ma": "дариджа",
"ig": "игбо",
"ka": "грузински",
"kk": "казахски",
"om": "оромо",
"so": "сомалийски",
"ti": "тигриња",
"uz": "узбекски",
"wo": "волоф",
"pl": "полски",
"pt": "португалски",
"ro": "румънски",
"ru": "руски",
"sr": "сръбски",
"tr": "турски",
"uk": "украински"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Нова форма на интелигентност",
"title": "Нова форма на интелигентност",
"lead": "Helexa е самоорганизираща се AI мрежа, задвижвана от независими оператори. Отворена. Разпределена. Еволюираща.",
"ctaJoinMesh": "Присъедини се към мрежата",
"ctaFollowProject": "Следвай проекта",
"subcopy": "Създадена за оператори, разработчици и общности, които вярват, че AI трябва да бъде отворен, устойчив и споделен.",
"imageAlt": "Визуализация на хеликса на Helexa"
},
"intent": {
"title": "Защо съществува Helexa",
"p1": "AI се превръща в най-мощната инфраструктура на Земята. Но днес тази сила е концентрирана в шепа корпорации, оформяна от частни приоритети, географски ограничения и крехка икономика.",
"p2Intro": "Helexa си представя нещо различно:",
"bullet1": "Интелигентност, която расте от всякъде, не от едно място.",
"bullet2": "Мрежа, в която всеки може да допринася и да се възползва.",
"bullet3": "Система, която се адаптира към търсенето, не към заповеди.",
"bullet4": "Технология, която подсилва общностите вместо да ги заменя.",
"closing": "Helexa не е платформа. Не е и облак.\nТя е мрежа — жива, еволюираща решетка от независими оператори, която оформя нов вид интелигентност."
},
"whyNow": {
"title": "Преломен момент за AI",
"problemTitle": "Проблемът",
"problemBullet1": "AI се централизира по-бързо от всяка предишна технология.",
"problemBullet2": "Достъпът до изчислителни ресурси определя възможностите, а достъпът се стеснява.",
"problemBullet3": "Ценовите бариери изключват изследователи, стартъпи и общности.",
"problemBullet4": "Геополитически и регулаторни натиск застрашават глобалната достъпност.",
"problemBullet5": "Създателите на модели и операторите на хардуер рядко споделят стойността, която създават.",
"opportunityTitle": "Възможността",
"opportunityIntro": "Но един разпределен свят е възможен.",
"opportunityBullet1": "Хиляди GPU машини вече са недоизползвани по целия свят.",
"opportunityBullet2": "Операторите искат честно заплащане за изчислителната си мощ.",
"opportunityBullet3": "Разработчиците искат отворена, устойчива на цензура инфраструктура.",
"opportunityBullet4": "Общностите искат суверенитет и устойчивост в дигиталните системи.",
"opportunityBullet5": "Ръстът на AI изпревари традиционните облаци — нужни са нови форми.",
"opportunityClosing": "Helexa е моментът, в който тези сили се подравняват."
},
"howItWorks": {
"title": "Как се формира мрежата",
"operators": {
"eyebrow": "Операторите стартират възли",
"title": "Всеки може да допринесе с изчислителна мощ.",
"body": "Операторите стартират възли на Helexa. Те решават кои модели да хостват. Остават в контрол над своя хардуер и своята икономика. Без одобрения, без посредници."
},
"routing": {
"eyebrow": "Мрежата маршрутизира интелигентността",
"title": "Търсенето тече през мрежата.",
"body": "Helexa научава къде има капацитет, къде търсенето нараства и кои възли са най-подходящи да обслужват заявките. Мрежата се адаптира органично — като растяща спирала."
},
"value": {
"eyebrow": "Стойността се връща обратно",
"title": "Работата е доказуема. Заплащането е честно.",
"body": "Всяка задача носи криптографско потвърждение. Операторите печелят за интелигентността, която помагат да се предостави. Без платформи данъкоплатци. Без непрозрачни сметки."
}
},
"principles": {
"title": "Изградена върху принципи, не върху платформи",
"distributed": {
"title": "Разпределена по замисъл",
"body": "Без една-единствена точка на отказ. Без централен орган. Мрежа, която става по-силна с всеки нов оператор."
},
"participation": {
"title": "Отворено участие",
"body": "Ако имаш изчислителни ресурси, можеш да допринесеш. Мрежата посреща всички — edge устройства, домашни сървъри, центрове за данни."
},
"fairness": {
"title": "Справедливост и прозрачност",
"body": "Приходите се базират на реална свършена работа, криптографски потвърдена. Без черни кутии. Без скрити такси."
},
"evolving": {
"title": "Еволюираща интелигентност",
"body": "Мрежата учи от търсенето. Моделите се зареждат там, където са нужни. Интелигентността се разпространява чрез сътрудничество."
}
},
"roadAhead": {
"title": "Какво цели да стане Helexa",
"p1": "Глобален слой от интелигентност, който принадлежи на всички, задвижван от спирала от възли и общности.",
"p2": "Мрежа, устойчива на сривове, политика, монополи и провали.",
"p3": "Нов икономически модел, в който оператори, създатели и потребители печелят заедно.",
"p4": "Екосистема, в която иновациите растат от периферията — не от центъра.",
"card": {
"eyebrow": "Миг от визията",
"title": "Към споделена мрежа от интелигентност",
"body": "Helexa е в ранен етап. Идеите са по-големи от реализацията — и това е умишлено. Мрежата ще расте итеративно, оформяна от операторите и разработчиците."
}
},
"joinMesh": {
"badge": "Ранен етап",
"title": "Мрежата се формира.",
"titleHighlight": "Ти можеш да бъдеш част от нея.",
"lead": "Независимо дали управляваш хардуер, изграждаш модели или просто ти пука как се управлява AI, има място за теб в мрежата.",
"ctaRunNode": "Пусни възел (скоро)",
"ctaJoinAnnouncements": "Включи се в ранните известия",
"ctaExploreCode": "Разгледай кода",
"footer": "Без оградени градини. Без един собственик. Само мрежа от хора, хардуер и идеи — които заедно създават различно бъдеще за интелигентността."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Samtalearbejdsområde",
"badge": "Chat",
"lead": "Dette er chatvisningen. Tilføj din samtalelogik og dine UIkomponenter til denne side.",
"transcriptPlaceholder": "Chattransskriptionen vises her. Gengiv beskeder fra modellen og brugeren i en rullende container, eventuelt grupperet efter tur.",
"inputPlaceholder": "Skriv en besked for at starte en chat…",
"send": "Send",
"clear": "Ryd",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Hjem",
"docs": "Dokumentation",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Skift til lyst tema",
"toDark": "Skift til mørkt tema"
}
},
"lang": {
"am": "Amharisk",
"ar": "Arabisk",
"bg": "Bulgarsk",
"da": "Dansk",
"de": "Tysk",
"el": "Græsk",
"en": "Engelsk",
"es": "Spansk",
"et": "Estisk",
"fa": "Persisk",
"fi": "Finsk",
"fr": "Fransk",
"ha": "Hausa",
"he": "Hebraisk",
"ig": "Igbo",
"it": "Italiensk",
"ka": "Georgisk",
"kk": "Kasakhisk",
"ma": "Darija",
"nl": "Hollandsk",
"no": "Norsk",
"om": "Oromo",
"pl": "Polsk",
"pt": "Portugisisk",
"ro": "Rumænsk",
"ru": "Russisk",
"so": "Somalisk",
"sr": "Serbisk",
"sv": "Svensk",
"sw": "Swahili",
"ti": "Tigrinya",
"tr": "Tyrkisk",
"uk": "Ukrainsk",
"uz": "Usbekisk",
"wo": "Wolof",
"yo": "Yoruba",
"zu": "Zulu"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "En ny form for intelligens",
"title": "En ny form for intelligens",
"lead": "Helexa er et selvorganiserende AImesh drevet af uafhængige operatører. Åbent. Distribueret. Under udvikling.",
"ctaJoinMesh": "Deltag i mesh'en",
"ctaFollowProject": "Følg projektet",
"subcopy": "Bygget til operatører, udviklere og fællesskaber, der mener, at AI skal være åbent, robust og delt.",
"imageAlt": "Helexa helixvisualisering"
},
"intent": {
"title": "Hvorfor Helexa findes",
"p1": "AI er ved at blive den mest magtfulde infrastruktur på Jorden. Men i dag er den magt koncentreret hos en håndfuld virksomheder, formet af private prioriteter, geografiske begrænsninger og skrøbelig økonomi.",
"p2Intro": "Helexa forestiller sig noget andet:",
"bullet1": "Intelligens, der vokser frem overalt ikke ét sted.",
"bullet2": "Et netværk, hvor alle kan bidrage og få gavn.",
"bullet3": "Et system, der tilpasser sig efter efterspørgsel, ikke direktiver.",
"bullet4": "Teknologi, der styrker fællesskaber i stedet for at erstatte dem.",
"closing": "Helexa er ikke en platform. Det er ikke en cloud.\nDet er et mesh et levende, udviklende gitter af uafhængige operatører, der danner en ny form for intelligens."
},
"whyNow": {
"title": "Et vendepunkt for AI",
"problemTitle": "Problemet",
"problemBullet1": "AI centraliseres hurtigere end nogen tidligere teknologi.",
"problemBullet2": "Adgang til compute definerer kapacitet, og adgangen bliver snævrere.",
"problemBullet3": "Omkostninger udelukker forskere, startups og fællesskaber.",
"problemBullet4": "Geopolitiske og regulatoriske pres truer global tilgængelighed.",
"problemBullet5": "Skaberne af modeller og operatørerne af hardware deler sjældent i den værdi, de producerer.",
"opportunityTitle": "Muligheden",
"opportunityIntro": "Men en distribueret verden er mulig.",
"opportunityBullet1": "Tusindvis af GPU'er står allerede uudnyttede rundt om i verden.",
"opportunityBullet2": "Operatører ønsker retfærdig kompensation for compute.",
"opportunityBullet3": "Udviklere ønsker åben, censurresistent infrastruktur.",
"opportunityBullet4": "Fællesskaber ønsker suverænitet og robusthed i digitale systemer.",
"opportunityBullet5": "AI's vækst har overhalet traditionelle clouds der er brug for nye former.",
"opportunityClosing": "Helexa er øjeblikket, hvor disse kræfter mødes."
},
"howItWorks": {
"title": "Sådan dannes mesh'en",
"operators": {
"eyebrow": "Operatører kører noder",
"title": "Alle kan bidrage med compute.",
"body": "Operatører kører Helexanoder. De beslutter, hvilke modeller der skal hostes. De bevarer kontrollen over deres hardware og økonomi. Ingen godkendelser, ingen gatekeepere."
},
"routing": {
"eyebrow": "Mesh'en dirigerer intelligens",
"title": "Efterspørgslen flyder gennem netværket.",
"body": "Helexa lærer, hvor kapacitet findes, hvor efterspørgslen stiger, og hvilke noder der er bedst egnet til at håndtere forespørgsler. Mesh'en tilpasser sig organisk som en voksende helix."
},
"value": {
"eyebrow": "Værdien flyder tilbage",
"title": "Arbejdet bevises. Betalingen er retfærdig.",
"body": "Hver opgave bærer en kryptografisk kvittering. Operatører tjener på den intelligens, de hjælper med at levere. Ingen platformsskat. Ingen uigennemsigtig fakturering."
}
},
"principles": {
"title": "Bygget på principper, ikke platforme",
"distributed": {
"title": "Distribueret fra starten",
"body": "Ingen enkelt fejlkilde. Ingen central autoritet. Et netværk, der bliver stærkere for hver ny operatør."
},
"participation": {
"title": "Åben deltagelse",
"body": "Hvis du har compute, kan du bidrage. Mesh'en byder alle velkommen edge, hjemmeserver, datacenter."
},
"fairness": {
"title": "Retfærdighed og gennemsigtighed",
"body": "Indtjening er baseret på reelt arbejde, kryptografisk verificeret. Ingen sorte bokse. Ingen skjulte gebyrer."
},
"evolving": {
"title": "Udviklende intelligens",
"body": "Mesh'en lærer af efterspørgslen. Modeller loades dér, hvor de behøves. Intelligens spredes gennem samarbejde."
}
},
"roadAhead": {
"title": "Hvad Helexa sigter mod at blive",
"p1": "Et globalt intelligenslag, der tilhører alle, drevet af en helix af noder og fællesskaber.",
"p2": "Et netværk, der er robust over for nedbrud, politik, monopoler og fejl.",
"p3": "En ny økonomisk model, hvor operatører, udviklere og brugere alle får udbytte.",
"p4": "Et økosystem, hvor innovation vokser ud fra kanterne ikke centrum.",
"card": {
"eyebrow": "Visionsoversigt",
"title": "Mod et delt intelligensmesh",
"body": "Helexa er i en tidlig fase. Idéerne er større end implementationen og det er med vilje. Netværket vil vokse gradvist, hvor operatører og udviklere sammen former dets udvikling."
}
},
"joinMesh": {
"badge": "Tidlig fase",
"title": "Mesh'en tager form.",
"titleHighlight": "Du kan være en del af den.",
"lead": "Uanset om du kører hardware, bygger modeller eller blot går op i, hvordan AI styres, er der en plads til dig i mesh'en.",
"ctaRunNode": "Kør en node (snart)",
"ctaJoinAnnouncements": "Deltag i tidlige annonceringer",
"ctaExploreCode": "Udforsk koden",
"footer": "Ingen lukkede haver. Ingen enkelt ejer. Bare et mesh af mennesker, hardware og idéer der sammen former en anderledes fremtid for intelligens."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Konversationsbereich",
"badge": "Chat",
"lead": "Dies ist die Chat-Ansicht. Binde hier deine Konversationslogik und UI-Komponenten ein.",
"transcriptPlaceholder": "Das Chat-Protokoll erscheint hier. Rendern die Nachrichten des Modells und der Nutzerin bzw. des Nutzers in einem scrollbaren Container, optional nach Gesprächsrunden gruppiert.",
"inputPlaceholder": "Schreibe eine Nachricht, um mit dem Chat zu beginnen…",
"send": "Senden",
"clear": "Leeren",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Startseite",
"docs": "Dokumentation",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "In den hellen Modus wechseln",
"toDark": "In den dunklen Modus wechseln"
}
},
"lang": {
"bg": "Bulgarisch",
"de": "Deutsch",
"el": "Griechisch",
"en": "Englisch",
"es": "Spanisch",
"et": "Estnisch",
"fr": "Französisch",
"he": "Hebräisch",
"it": "Italienisch",
"nl": "Niederländisch",
"da": "Dänisch",
"fi": "Finnisch",
"no": "Norwegisch",
"sv": "Schwedisch",
"ar": "Arabisch",
"fa": "Persisch",
"sw": "Suaheli",
"ha": "Hausa",
"am": "Amharisch",
"yo": "Yoruba",
"zu": "Zulu",
"ma": "Darija",
"ig": "Igbo",
"ka": "Georgisch",
"kk": "Kasachisch",
"om": "Oromo",
"so": "Somali",
"ti": "Tigrinya",
"uz": "Usbekisch",
"wo": "Wolof",
"pl": "Polnisch",
"pt": "Portugiesisch",
"ro": "Rumänisch",
"ru": "Russisch",
"sr": "Serbisch",
"tr": "Türkisch",
"uk": "Ukrainisch"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Eine neue Form von Intelligenz",
"title": "Eine neue Form von Intelligenz",
"lead": "Helexa ist ein selbstorganisiertes KIMesh, betrieben von unabhängigen Operatoren. Offen. Dezentral. Im Wandel.",
"ctaJoinMesh": "Dem Mesh beitreten",
"ctaFollowProject": "Dem Projekt folgen",
"subcopy": "Geschaffen für Operatoren, Builder und Communities, die glauben, dass KI offen, widerstandsfähig und geteilt sein sollte.",
"imageAlt": "HelexaHelixVisualisierung"
},
"intent": {
"title": "Warum es Helexa gibt",
"p1": "KI wird zur mächtigsten Infrastruktur auf der Erde. Doch heute ist diese Macht in den Händen weniger Konzerne konzentriert geprägt von privaten Prioritäten, geografischen Grenzen und fragilen Geschäftsmodellen.",
"p2Intro": "Helexa stellt sich etwas anderes vor:",
"bullet1": "Intelligenz, die überall wächst nicht nur an einem Ort.",
"bullet2": "Ein Netzwerk, in dem jeder beitragen und profitieren kann.",
"bullet3": "Ein System, das sich an Nachfrage anpasst, nicht an Vorgaben.",
"bullet4": "Technologie, die Gemeinschaften stärkt, statt sie zu ersetzen.",
"closing": "Helexa ist keine Plattform. Es ist keine Cloud.\nEs ist ein Mesh ein lebendiges, sich entwickelndes Geflecht unabhängiger Operatoren, das eine neue Form von Intelligenz bildet."
},
"whyNow": {
"title": "Ein Wendepunkt für KI",
"problemTitle": "Das Problem",
"problemBullet1": "KI zentralisiert sich schneller als jede Technologie zuvor.",
"problemBullet2": "Zugang zu Rechenressourcen bestimmt die Fähigkeiten und dieser Zugang verengt sich.",
"problemBullet3": "Kostenbarrieren schließen Forschende, Startups und Communities aus.",
"problemBullet4": "Geopolitische und regulatorische Spannungen bedrohen die globale Verfügbarkeit.",
"problemBullet5": "Die Schöpfer von Modellen und die Betreiber von Hardware teilen selten den Wert, den sie erzeugen.",
"opportunityTitle": "Die Chance",
"opportunityIntro": "Aber eine verteilte Welt ist möglich.",
"opportunityBullet1": "Tausende GPUs weltweit sind heute bereits unterausgelastet.",
"opportunityBullet2": "Operatoren wollen eine faire Vergütung für ihre Rechenleistung.",
"opportunityBullet3": "Entwicklerinnen und Entwickler wünschen sich offene, zensurresistente Infrastruktur.",
"opportunityBullet4": "Communities wollen Souveränität und Resilienz in digitalen Systemen.",
"opportunityBullet5": "Das Wachstum der KI hat klassische Clouds überholt es braucht neue Formen.",
"opportunityClosing": "Helexa ist der Moment, in dem sich diese Kräfte ausrichten."
},
"howItWorks": {
"title": "Wie sich das Mesh bildet",
"operators": {
"eyebrow": "Operatoren betreiben Nodes",
"title": "Jede und jeder kann Rechenleistung beisteuern.",
"body": "Operatoren betreiben HelexaNodes. Sie entscheiden, welche Modelle sie hosten. Sie behalten die Kontrolle über ihre Hardware und ihre Wirtschaftlichkeit. Keine Genehmigungen, keine Gatekeeper."
},
"routing": {
"eyebrow": "Das Mesh leitet Intelligenz",
"title": "Nachfrage fließt durch das Netzwerk.",
"body": "Helexa lernt, wo Kapazität vorhanden ist, wo die Nachfrage steigt und welche Nodes sich am besten für Anfragen eignen. Das Mesh passt sich organisch an wie eine wachsende Helix."
},
"value": {
"eyebrow": "Wert fließt zurück",
"title": "Arbeit wird bewiesen. Bezahlung ist fair.",
"body": "Jeder Job trägt einen kryptografischen Beleg. Operatoren verdienen an der Intelligenz mit, die sie bereitstellen. Keine Plattformsteuer. Keine intransparente Abrechnung."
}
},
"principles": {
"title": "Auf Prinzipien gebaut, nicht auf Plattformen",
"distributed": {
"title": "Von Grund auf verteilt",
"body": "Kein Single Point of Failure. Keine zentrale Instanz. Ein Netzwerk, das mit jedem neuen Operator stärker wird."
},
"participation": {
"title": "Offene Teilhabe",
"body": "Wenn du Rechenleistung hast, kannst du beitragen. Das Mesh heißt alle willkommen Edge, Heimserver oder Rechenzentrum."
},
"fairness": {
"title": "Fairness & Transparenz",
"body": "Einnahmen basieren auf echter, kryptografisch verifizierter Arbeit. Keine Blackboxen. Keine versteckten Gebühren."
},
"evolving": {
"title": "Sich entwickelnde Intelligenz",
"body": "Das Mesh lernt aus der Nachfrage. Modelle landen dort, wo sie gebraucht werden. Intelligenz breitet sich durch Kooperation aus."
}
},
"roadAhead": {
"title": "Was Helexa werden will",
"p1": "Eine globale Intelligenzschicht, die allen gehört angetrieben von einer Helix aus Nodes und Communities.",
"p2": "Ein Netzwerk, das Ausfällen, Politik, Monopolen und Störungen standhält.",
"p3": "Ein neues Wirtschaftsmodell, in dem Operatoren, Builder und Nutzerinnen gleichermaßen profitieren.",
"p4": "Ein Ökosystem, in dem Innovation von den Rändern ausgeht nicht aus der Mitte.",
"card": {
"eyebrow": "VisionsSnapshot",
"title": "Auf dem Weg zu einem geteilten IntelligenzMesh",
"body": "Helexa steht am Anfang. Die Ideen sind größer als die aktuelle Implementierung und das ist beabsichtigt. Das Netzwerk wird schrittweise wachsen, während Operatoren und Builder seine Entwicklung mitgestalten."
}
},
"joinMesh": {
"badge": "Frühe Phase",
"title": "Das Mesh formt sich.",
"titleHighlight": "Du kannst Teil davon sein.",
"lead": "Ob du Hardware betreibst, Modelle baust oder dir einfach wichtig ist, wie KI gesteuert wird im Mesh gibt es einen Platz für dich.",
"ctaRunNode": "Node betreiben (bald)",
"ctaJoinAnnouncements": "Frühe Ankündigungen abonnieren",
"ctaExploreCode": "Code erkunden",
"footer": "Keine geschlossenen Gärten. Kein einzelner Besitzer. Nur ein Mesh aus Menschen, Hardware und Ideen das eine andere Zukunft für Intelligenz komponiert."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Χώρος συνομιλίας",
"badge": "Συνομιλία",
"lead": "Αυτή είναι η προβολή συνομιλίας. Σύνδεσε εδώ τη λογική της συζήτησης και τα components του περιβάλλοντος χρήστη σου.",
"transcriptPlaceholder": "Το ιστορικό της συνομιλίας θα εμφανίζεται εδώ. Απόδωσε τα μηνύματα του μοντέλου και του χρήστη σε ένα κυλιόμενο container, προαιρετικά ομαδοποιημένα ανά γύρο.",
"inputPlaceholder": "Πληκτρολόγησε ένα μήνυμα για να ξεκινήσεις τη συνομιλία…",
"send": "Αποστολή",
"clear": "Καθαρισμός",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Αρχική",
"docs": "Τεκμηρίωση",
"chat": "Συνομιλία",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Μετάβαση σε φωτεινή λειτουργία",
"toDark": "Μετάβαση σε σκοτεινή λειτουργία"
}
},
"lang": {
"bg": "Βουλγαρικά",
"de": "Γερμανικά",
"el": "Ελληνικά",
"en": "Αγγλικά",
"es": "Ισπανικά",
"et": "Εσθονικά",
"fr": "Γαλλικά",
"he": "Εβραϊκά",
"it": "Ιταλικά",
"nl": "Ολλανδικά",
"da": "Δανικά",
"fi": "Φινλανδικά",
"no": "Νορβηγικά",
"sv": "Σουηδικά",
"ar": "Αραβικά",
"fa": "Περσικά",
"sw": "Σουαχίλι",
"ha": "Χάουσα",
"am": "Αμχαρικά",
"yo": "Γιορούμπα",
"zu": "Ζουλού",
"ma": "Νταρίτζα",
"ig": "Ίγκμπο",
"ka": "Γεωργιανά",
"kk": "Καζακικά",
"om": "Ορόμο",
"so": "Σομαλικά",
"ti": "Τιγκρινια",
"uz": "Ουζμπεκικά",
"wo": "Γουόλοφ",
"pl": "Πολωνικά",
"pt": "Πορτογαλικά",
"ro": "Ρουμανικά",
"ru": "Ρωσικά",
"sr": "Σερβικά",
"tr": "Τουρκικά",
"uk": "Ουκρανικά"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Μια νέα μορφή νοημοσύνης",
"title": "Μια νέα μορφή νοημοσύνης",
"lead": "Η Helexa είναι ένα αυτο-οργανωμένο πλέγμα ΤΝ που τροφοδοτείται από ανεξάρτητους operators. Ανοιχτό. Κατανεμημένο. Εξελισσόμενο.",
"ctaJoinMesh": "Γίνε μέρος του πλέγματος",
"ctaFollowProject": "Παρακολούθησε το έργο",
"subcopy": "Φτιαγμένο για operators, builders και κοινότητες που πιστεύουν ότι η ΤΝ πρέπει να είναι ανοιχτή, ανθεκτική και κοινόχρηστη.",
"imageAlt": "Οπτικοποίηση της έλικας Helexa"
},
"intent": {
"title": "Γιατί υπάρχει η Helexa",
"p1": "Η ΤΝ γίνεται η πιο ισχυρή υποδομή στον κόσμο. Σήμερα όμως, αυτή η ισχύς είναι συγκεντρωμένη σε λίγες εταιρείες, διαμορφωμένη από ιδιωτικές προτεραιότητες, γεωγραφικούς περιορισμούς και εύθραυστες οικονομίες.",
"p2Intro": "Η Helexa φαντάζεται κάτι διαφορετικό:",
"bullet1": "Μια νοημοσύνη που αναπτύσσεται από παντού, όχι από ένα μόνο σημείο.",
"bullet2": "Ένα δίκτυο στο οποίο ο καθένας μπορεί να συνεισφέρει και να ωφεληθεί.",
"bullet3": "Ένα σύστημα που προσαρμόζεται στη ζήτηση, όχι στις εντολές.",
"bullet4": "Τεχνολογία που ενδυναμώνει τις κοινότητες αντί να τις αντικαθιστά.",
"closing": "Η Helexa δεν είναι πλατφόρμα. Δεν είναι cloud.\nΕίναι ένα πλέγμα — ένα ζωντανό, εξελισσόμενο δίκτυο ανεξάρτητων operators που σχηματίζουν ένα νέο είδος νοημοσύνης."
},
"whyNow": {
"title": "Ένα σημείο καμπής για την ΤΝ",
"problemTitle": "Το πρόβλημα",
"problemBullet1": "Η ΤΝ συγκεντρώνεται πιο γρήγορα από κάθε προηγούμενη τεχνολογία.",
"problemBullet2": "Η πρόσβαση σε υπολογιστική ισχύ καθορίζει τις δυνατότητες και αυτή η πρόσβαση περιορίζεται.",
"problemBullet3": "Τα κόστη αποκλείουν ερευνητές, startups και κοινότητες.",
"problemBullet4": "Γεωπολιτικές και ρυθμιστικές πιέσεις απειλούν τη συνολική διαθεσιμότητα.",
"problemBullet5": "Οι δημιουργοί μοντέλων και οι operators υλικού σπάνια μοιράζονται την αξία που παράγουν.",
"opportunityTitle": "Η ευκαιρία",
"opportunityIntro": "Όμως ένας κατανεμημένος κόσμος είναι εφικτός.",
"opportunityBullet1": "Χιλιάδες GPUs σε όλο τον κόσμο παραμένουν υποαπασχολημένες.",
"opportunityBullet2": "Οι operators θέλουν δίκαιη αποζημίωση για την υπολογιστική ισχύ.",
"opportunityBullet3": "Οι developers θέλουν ανοιχτή, ανθεκτική στη λογοκρισία υποδομή.",
"opportunityBullet4": "Οι κοινότητες θέλουν κυριαρχία και ανθεκτικότητα στα ψηφιακά τους συστήματα.",
"opportunityBullet5": "Η ανάπτυξη της ΤΝ έχει ξεπεράσει τα παραδοσιακά clouds — χρειάζονται νέες μορφές.",
"opportunityClosing": "Η Helexa είναι η στιγμή όπου αυτές οι δυνάμεις ευθυγραμμίζονται."
},
"howItWorks": {
"title": "Πώς σχηματίζεται το πλέγμα",
"operators": {
"eyebrow": "Οι operators τρέχουν nodes",
"title": "Ο καθένας μπορεί να συνεισφέρει υπολογιστική ισχύ.",
"body": "Οι operators τρέχουν nodes της Helexa. Αποφασίζουν ποια μοντέλα θα φιλοξενήσουν. Παραμένουν σε έλεγχο του υλικού και της οικονομίας τους. Χωρίς εγκρίσεις, χωρίς μεσάζοντες gatekeepers."
},
"routing": {
"eyebrow": "Το πλέγμα δρομολογεί νοημοσύνη",
"title": "Η ζήτηση ρέει μέσα από το δίκτυο.",
"body": "Η Helexa μαθαίνει πού υπάρχει διαθέσιμη ισχύς, πού αυξάνεται η ζήτηση και ποια nodes είναι πιο κατάλληλα για να εξυπηρετήσουν τα αιτήματα. Το πλέγμα προσαρμόζεται οργανικά — σαν μια αναπτυσσόμενη έλικα."
},
"value": {
"eyebrow": "Η αξία επιστρέφει πίσω",
"title": "Η εργασία αποδεικνύεται. Η πληρωμή είναι δίκαιη.",
"body": "Κάθε εργασία φέρει μια κρυπτογραφική απόδειξη. Οι operators αμείβονται για τη νοημοσύνη που βοηθούν να παραχθεί. Χωρίς φόρο πλατφόρμας. Χωρίς αδιαφανή τιμολόγηση."
}
},
"principles": {
"title": "Χτισμένη σε αρχές, όχι σε πλατφόρμες",
"distributed": {
"title": "Κατανεμημένη από σχεδιασμό",
"body": "Χωρίς μοναδικό σημείο αποτυχίας. Χωρίς κεντρική αρχή. Ένα δίκτυο που γίνεται ισχυρότερο με κάθε νέο operator."
},
"participation": {
"title": "Ανοιχτή συμμετοχή",
"body": "Αν έχεις υπολογιστική ισχύ, μπορείς να συνεισφέρεις. Το πλέγμα καλωσορίζει τους πάντες — edge, οικιακό server, datacenter."
},
"fairness": {
"title": "Δικαιοσύνη & διαφάνεια",
"body": "Τα έσοδα βασίζονται σε πραγματική, κρυπτογραφικά επαληθευμένη εργασία. Χωρίς «μαύρα κουτιά». Χωρίς κρυφές χρεώσεις."
},
"evolving": {
"title": "Εξελισσόμενη νοημοσύνη",
"body": "Το πλέγμα μαθαίνει από τη ζήτηση. Τα μοντέλα φορτώνονται εκεί όπου χρειάζονται. Η νοημοσύνη εξαπλώνεται μέσω συνεργασίας."
}
},
"roadAhead": {
"title": "Τι στοχεύει να γίνει η Helexa",
"p1": "Ένα παγκόσμιο επίπεδο νοημοσύνης που ανήκει σε όλους, τροφοδοτούμενο από μια έλικα από nodes και κοινότητες.",
"p2": "Ένα δίκτυο ανθεκτικό σε διακοπές, πολιτική, μονοπώλια και αστοχίες.",
"p3": "Ένα νέο οικονομικό μοντέλο όπου operators, builders και χρήστες ωφελούνται όλοι.",
"p4": "Ένα οικοσύστημα όπου η καινοτομία αναπτύσσεται από τις άκρες — όχι από το κέντρο.",
"card": {
"eyebrow": "Στιγμιότυπο του οράματος",
"title": "Προς ένα κοινό πλέγμα νοημοσύνης",
"body": "Η Helexa βρίσκεται σε πρώιμο στάδιο. Οι ιδέες είναι μεγαλύτερες από την υλοποίηση — και αυτό είναι σκόπιμο. Το δίκτυο θα μεγαλώνει βήμα‑βήμα, με τους operators και τους builders να διαμορφώνουν την εξέλιξή του."
}
},
"joinMesh": {
"badge": "Πρώιμη φάση",
"title": "Το πλέγμα σχηματίζεται.",
"titleHighlight": "Μπορείς να είσαι μέρος του.",
"lead": "Είτε τρέχεις hardware, είτε χτίζεις μοντέλα, είτε απλώς σε νοιάζει πώς κυβερνάται η ΤΝ, υπάρχει μια θέση για σένα μέσα στο πλέγμα.",
"ctaRunNode": "Τρέξε ένα node (σύντομα)",
"ctaJoinAnnouncements": "Μπες στις πρώιμες ανακοινώσεις",
"ctaExploreCode": "Εξερεύνησε τον κώδικα",
"footer": "Χωρίς περιφραγμένους κήπους. Χωρίς έναν μόνο ιδιοκτήτη. Μόνο ένα πλέγμα από ανθρώπους, hardware και ιδέες — που συνθέτουν ένα διαφορετικό μέλλον για τη νοημοσύνη."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Conversation workspace",
"badge": "Chat",
"lead": "This is the chat view. Plug your conversational logic and UI components into this page.",
"transcriptPlaceholder": "Chat transcript will appear here. Render messages from the model and user in a scrolling container, optionally grouped by turn.",
"inputPlaceholder": "Type a message to start chatting…",
"send": "Send",
"clear": "Clear",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Home",
"docs": "Docs",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Switch to light mode",
"toDark": "Switch to dark mode"
}
},
"lang": {
"bg": "Bulgarian",
"de": "German",
"el": "Greek",
"en": "English",
"es": "Spanish",
"et": "Estonian",
"fr": "French",
"he": "Hebrew",
"it": "Italian",
"nl": "Dutch",
"da": "Danish",
"fi": "Finnish",
"no": "Norwegian",
"sv": "Swedish",
"ar": "Arabic",
"fa": "Persian",
"sw": "Swahili",
"ha": "Hausa",
"am": "Amharic",
"yo": "Yoruba",
"zu": "Zulu",
"ma": "Darija",
"ig": "Igbo",
"ka": "Georgian",
"kk": "Kazakh",
"om": "Oromo",
"so": "Somali",
"ti": "Tigrinya",
"uz": "Uzbek",
"wo": "Wolof",
"pl": "Polish",
"pt": "Portuguese",
"ro": "Romanian",
"ru": "Russian",
"sr": "Serbian",
"tr": "Turkish",
"uk": "Ukrainian"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "European digital sovereignty",
"title": "Sovereign AI, Run by Europe",
"lead": "helexa is a sovereign AI mesh: near-frontier models served by independent European operators, on European hardware, under European law. Open. Distributed. Yours.",
"ctaJoinMesh": "Start chatting",
"ctaFollowProject": "Follow the project",
"subcopy": "For people and organisations who refuse to make their thinking a dependency of a foreign hyperscaler.",
"imageAlt": "helexa helix visual"
},
"intent": {
"title": "Why helexa Exists",
"p1": "AI is becoming the most consequential infrastructure on Earth — and Europe runs almost none of it. The models, the GPUs, the clouds, and the terms of service belong to a handful of US corporations, leaving European users, researchers, and businesses renting their own intelligence back from someone else's jurisdiction.",
"p2Intro": "helexa is built for a different settlement:",
"bullet1": "Data residency by default — your prompts stay on operators you can locate on a map.",
"bullet2": "GDPR-native, not GDPR-retrofitted: no server-side chat history, ever.",
"bullet3": "Capacity owned by independent operators, not a single hyperscaler.",
"bullet4": "Infrastructure that strengthens European autonomy instead of deepening dependency.",
"closing": "helexa is not a platform. It is not a US cloud with an EU region.\nIt is a mesh — a lattice of independent European operators serving frontier-class intelligence under European law."
},
"whyNow": {
"title": "A Turning Point for European AI",
"problemTitle": "The Dependency",
"problemBullet1": "Frontier AI is centralising faster than any technology before it — almost entirely outside Europe.",
"problemBullet2": "Compute access defines capability, and that access is gated by a few foreign providers.",
"problemBullet3": "Cross-border data flows and shifting US policy put European data and availability at risk.",
"problemBullet4": "Terms, prices, and model availability can change overnight, decided elsewhere.",
"problemBullet5": "European operators who own capable hardware rarely share in the value it could produce.",
"opportunityTitle": "The Sovereign Opportunity",
"opportunityIntro": "A European alternative is already within reach.",
"opportunityBullet1": "Capable consumer GPUs sit underutilised across European homes, labs, and datacentres.",
"opportunityBullet2": "Operators want fair compensation for serving compute close to users.",
"opportunityBullet3": "Developers want open, censorship-resistant infrastructure under known law.",
"opportunityBullet4": "Communities and institutions want sovereignty and resilience in their digital systems.",
"opportunityBullet5": "Near-frontier open-weight models now run well on consumer hardware — no hyperscaler required.",
"opportunityClosing": "helexa is the moment these forces align — in Europe's favour."
},
"howItWorks": {
"title": "How the Mesh Forms",
"operators": {
"eyebrow": "Operators Run Nodes",
"title": "European compute, locally owned.",
"body": "Independent operators run helexa nodes on their own hardware, in their own jurisdiction. They choose what to host and keep control of their economics. No approvals, no gatekeepers, no offshore dependency."
},
"routing": {
"eyebrow": "The Mesh Routes Intelligence",
"title": "Requests stay close to home.",
"body": "helexa routes each request to an operator with capacity, preferring region affinity — so traffic and data stay where you expect. The mesh adapts organically, like a growing helix."
},
"value": {
"eyebrow": "Value Flows Back",
"title": "Work is proven. Payment is fair.",
"body": "Usage is metered transparently and operators are compensated for the intelligence they serve. No platform taxation, no opaque billing, no lock-in."
}
},
"principles": {
"title": "Built on Sovereignty, Not Platforms",
"distributed": {
"title": "Sovereign by Design",
"body": "European hardware, European operators, European law. No single point of failure and no foreign control plane your access depends on."
},
"participation": {
"title": "Open Participation",
"body": "If you have capable compute, you can contribute — edge, home server, or datacentre. The mesh welcomes every European operator."
},
"fairness": {
"title": "Privacy & Transparency",
"body": "GDPR-native: chat history lives only in your browser, never on a server. Usage metering is transparent; there are no black boxes and no hidden fees."
},
"evolving": {
"title": "Resilient Intelligence",
"body": "The mesh learns from demand and loads models where they're needed. Capacity spread across many operators is harder to censor, throttle, or switch off."
}
},
"roadAhead": {
"title": "What helexa Aims to Become",
"p1": "A European intelligence layer that belongs to its users and operators, powered by a helix of independent nodes.",
"p2": "A network resilient to outages, foreign policy shifts, monopolies, and single-vendor failure.",
"p3": "A fair economic model where European operators, builders, and users all benefit.",
"p4": "An ecosystem where innovation grows from the edges of Europe — not the centre of someone else's market.",
"card": {
"eyebrow": "Vision snapshot",
"title": "Toward a sovereign intelligence mesh",
"body": "helexa is early, and deliberately so. The network grows iteratively, with European operators and builders shaping its evolution — not a roadmap dictated from abroad."
}
},
"joinMesh": {
"badge": "Early phase",
"title": "The Mesh Is Forming.",
"titleHighlight": "You Can Be Part of It.",
"lead": "Whether you run hardware, build models, or simply care about who governs your AI, there is a place for you in the mesh.",
"ctaRunNode": "Run a node (soon)",
"ctaJoinAnnouncements": "Join early announcements",
"ctaExploreCode": "Explore the code",
"footer": "No walled gardens. No foreign owner. A mesh of European people, hardware, and ideas — composing a sovereign future for intelligence."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Espacio de conversación",
"badge": "Chat",
"lead": "Esta es la vista de chat. Conecta aquí tu lógica conversacional y los componentes de interfaz que desees.",
"transcriptPlaceholder": "La transcripción del chat aparecerá aquí. Renderiza los mensajes del modelo y de la persona usuaria en un contenedor desplazable, opcionalmente agrupados por turno.",
"inputPlaceholder": "Escribe un mensaje para comenzar a chatear…",
"send": "Enviar",
"clear": "Limpiar",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Inicio",
"docs": "Documentación",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Cambiar a modo claro",
"toDark": "Cambiar a modo oscuro"
}
},
"lang": {
"bg": "Búlgaro",
"de": "Alemán",
"el": "Griego",
"en": "Inglés",
"es": "Español",
"et": "Estonio",
"fr": "Francés",
"he": "Hebreo",
"it": "Italiano",
"nl": "Neerlandés",
"da": "Danés",
"fi": "Finés",
"no": "Noruego",
"sv": "Sueco",
"ar": "Árabe",
"fa": "Persa",
"sw": "Suajili",
"ha": "Hausa",
"am": "Amhárico",
"yo": "Yoruba",
"zu": "Zulu",
"ma": "Darija",
"ig": "Igbo",
"ka": "Georgiano",
"kk": "Kazajo",
"om": "Oromo",
"so": "Somalí",
"ti": "Tigriña",
"uz": "Uzbeko",
"wo": "Wólof",
"pl": "Polaco",
"pt": "Portugués",
"ro": "Rumano",
"ru": "Ruso",
"sr": "Serbio",
"tr": "Turco",
"uk": "Ucraniano"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Una nueva forma de inteligencia",
"title": "Una nueva forma de inteligencia",
"lead": "Helexa es una malla de IA autoorganizada impulsada por operadores independientes. Abierta. Distribuida. En evolución.",
"ctaJoinMesh": "Únete a la malla",
"ctaFollowProject": "Sigue el proyecto",
"subcopy": "Construida para operadores, desarrolladores y comunidades que creen que la IA debe ser abierta, resiliente y compartida.",
"imageAlt": "Visual de la hélice de Helexa"
},
"intent": {
"title": "Por qué existe Helexa",
"p1": "La IA se está convirtiendo en la infraestructura más poderosa de la Tierra. Pero hoy, ese poder está concentrado en un puñado de corporaciones, moldeado por prioridades privadas, limitaciones geográficas y economías frágiles.",
"p2Intro": "Helexa imagina algo diferente:",
"bullet1": "Una inteligencia que crece desde todas partes, no desde un solo lugar.",
"bullet2": "Una red donde cualquiera puede contribuir y beneficiarse.",
"bullet3": "Un sistema que se adapta a la demanda, no a directivas.",
"bullet4": "Tecnología que fortalece a las comunidades en lugar de reemplazarlas.",
"closing": "Helexa no es una plataforma. No es una nube.\nEs una malla: una red viva y evolutiva de operadores independientes que forman un nuevo tipo de inteligencia."
},
"whyNow": {
"title": "Un punto de inflexión para la IA",
"problemTitle": "El problema",
"problemBullet1": "La IA se está centralizando más rápido que cualquier tecnología anterior.",
"problemBullet2": "El acceso al cómputo define la capacidad, y ese acceso se está reduciendo.",
"problemBullet3": "Las barreras de coste excluyen a investigadores, startups y comunidades.",
"problemBullet4": "Las presiones geopolíticas y regulatorias amenazan la disponibilidad global.",
"problemBullet5": "Los creadores de modelos y los operadores de hardware rara vez comparten el valor que producen.",
"opportunityTitle": "La oportunidad",
"opportunityIntro": "Pero un mundo distribuido es posible.",
"opportunityBullet1": "Miles de GPU ya están subutilizadas en todo el mundo.",
"opportunityBullet2": "Los operadores quieren una compensación justa por el cómputo.",
"opportunityBullet3": "Los desarrolladores quieren infraestructura abierta y resistente a la censura.",
"opportunityBullet4": "Las comunidades quieren soberanía y resiliencia en los sistemas digitales.",
"opportunityBullet5": "El crecimiento de la IA ha superado a las nubes tradicionales: se necesitan nuevas formas.",
"opportunityClosing": "Helexa es el momento en que estas fuerzas se alinean."
},
"howItWorks": {
"title": "Cómo se forma la malla",
"operators": {
"eyebrow": "Los operadores ejecutan nodos",
"title": "Cualquiera puede aportar cómputo.",
"body": "Los operadores ejecutan nodos de Helexa. Deciden qué modelos alojar. Mantienen el control de su hardware y su economía. Sin aprobaciones, sin guardianes."
},
"routing": {
"eyebrow": "La malla enruta la inteligencia",
"title": "La demanda fluye a través de la red.",
"body": "Helexa aprende dónde existe capacidad, dónde está creciendo la demanda y qué nodos están mejor preparados para atender las solicitudes. La malla se adapta de forma orgánica, como una hélice en crecimiento."
},
"value": {
"eyebrow": "El valor fluye de vuelta",
"title": "El trabajo se prueba. El pago es justo.",
"body": "Cada tarea lleva un recibo criptográfico. Los operadores ganan por la inteligencia que ayudan a proporcionar. Sin impuestos de plataforma. Sin facturación opaca."
}
},
"principles": {
"title": "Basada en principios, no en plataformas",
"distributed": {
"title": "Distribuida por diseño",
"body": "Sin un único punto de fallo. Sin autoridad central. Una red que se fortalece con cada nuevo operador."
},
"participation": {
"title": "Participación abierta",
"body": "Si tienes cómputo, puedes contribuir. La malla da la bienvenida a todos: edge, servidor doméstico o centro de datos."
},
"fairness": {
"title": "Equidad y transparencia",
"body": "Las ganancias se basan en trabajo real, verificado criptográficamente. Sin cajas negras. Sin comisiones ocultas."
},
"evolving": {
"title": "Inteligencia en evolución",
"body": "La malla aprende de la demanda. Los modelos se cargan donde se necesitan. La inteligencia se extiende mediante la cooperación."
}
},
"roadAhead": {
"title": "Lo que Helexa aspira a ser",
"p1": "Una capa de inteligencia global que pertenece a todos, impulsada por una hélice de nodos y comunidades.",
"p2": "Una red resiliente frente a cortes, política, monopolios y fallos.",
"p3": "Un nuevo modelo económico donde operadores, constructores y usuarios se benefician por igual.",
"p4": "Un ecosistema donde la innovación crece desde los bordes, no desde el centro.",
"card": {
"eyebrow": "Instantánea de la visión",
"title": "Hacia una malla de inteligencia compartida",
"body": "Helexa está en una fase temprana. Las ideas son más grandes que la implementación, y eso es intencional. La red crecerá de forma iterativa, con operadores y desarrolladores dando forma a su evolución."
}
},
"joinMesh": {
"badge": "Fase inicial",
"title": "La malla se está formando.",
"titleHighlight": "Tú puedes ser parte de ella.",
"lead": "Tanto si gestionas hardware, construyes modelos o simplemente te importa cómo se gobierna la IA, hay un lugar para ti en la malla.",
"ctaRunNode": "Ejecutar un nodo (pronto)",
"ctaJoinAnnouncements": "Únete a los anuncios iniciales",
"ctaExploreCode": "Explora el código",
"footer": "Sin jardines amurallados. Sin un único propietario. Solo una malla de personas, hardware e ideas que componen un futuro distinto para la inteligencia."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Vestluse tööruum",
"badge": "Vestlus",
"lead": "See on vestlusvaade. Siia saad ühendada oma vestlusloogika ja kasutajaliidese komponendid.",
"transcriptPlaceholder": "Vestluse logi kuvatakse siin. Esita mudeli ja kasutaja sõnumeid keritavas konteineris, soovi korral sammude kaupa rühmitatuna.",
"inputPlaceholder": "Alusta vestlust, kirjutades siia sõnumi…",
"send": "Saada",
"clear": "Tühjenda",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Avaleht",
"docs": "Dokumentatsioon",
"chat": "Vestlus",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Lülita heledale teemale",
"toDark": "Lülita tumedale teemale"
}
},
"lang": {
"bg": "bulgaaria",
"de": "saksa",
"el": "kreeka",
"en": "inglise",
"es": "hispaania",
"et": "eesti",
"fr": "prantsuse",
"he": "heebrea",
"it": "itaalia",
"nl": "hollandi",
"da": "taani",
"fi": "soome",
"no": "norra",
"sv": "rootsi",
"ar": "araabia",
"fa": "pärsia",
"sw": "suahiili",
"ha": "hausa",
"am": "amhara",
"yo": "joruba",
"zu": "isuulu",
"ma": "darija",
"ig": "igbo",
"ka": "gruusia",
"kk": "kasahhi",
"om": "oromo",
"so": "soomeeli",
"ti": "tigrinja",
"uz": "usbeki",
"wo": "wolofi",
"pl": "poola",
"pt": "portugali",
"ro": "rumeenia",
"ru": "vene",
"sr": "serbia",
"tr": "türgi",
"uk": "ukraina"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Uus intelligentsuse vorm",
"title": "Uus intelligentsuse vorm",
"lead": "Helexa on iseorganiseeruv tehisintellekti võrk, mida juhivad sõltumatud operaatorid. Avatud. Hajutatud. Arenev.",
"ctaJoinMesh": "Liitu võrgustikuga",
"ctaFollowProject": "Jälgi projekti",
"subcopy": "Loodud operaatoritele, ehitajatele ja kogukondadele, kes usuvad, et tehisintellekt peab olema avatud, vastupidav ja jagatud.",
"imageAlt": "Helexa heeliksi visuaal"
},
"intent": {
"title": "Miks Helexa olemas on",
"p1": "Tehisintellektist saab Maa võimsaim taristu. Kuid täna on see jõud koondunud väheste korporatsioonide kätte, mida kujundavad eraeesmärgid, geograafilised piirangud ja habras majandus.",
"p2Intro": "Helexa kujutab ette midagi teistsugust:",
"bullet1": "Intelligents, mis kasvab igalt poolt, mitte ühest kohast.",
"bullet2": "Võrk, kus igaüks saab panustada ja kasu saada.",
"bullet3": "Süsteem, mis kohaneb nõudluse, mitte korralduste järgi.",
"bullet4": "Tehnoloogia, mis tugevdab kogukondi, mitte ei asenda neid.",
"closing": "Helexa ei ole platvorm. See ei ole pilv.\nSee on võrk — elav, arenev iseseisvate operaatorite võrgustik, mis loob uutmoodi intelligentsi."
},
"whyNow": {
"title": "Pöördepunkt tehisintellekti jaoks",
"problemTitle": "Probleem",
"problemBullet1": "Tehisintellekt tsentraliseerub kiiremini kui ükski varasem tehnoloogia.",
"problemBullet2": "Arvutusvõimsuse kättesaadavus määrab võimekuse ja ligipääs kitseneb.",
"problemBullet3": "Kulubarjäärid jätavad kõrvale uurijad, idufirmad ja kogukonnad.",
"problemBullet4": "Geopoliitiline ja regulatiivne surve ohustab globaalset kättesaadavust.",
"problemBullet5": "Mudeliloojatel ja riistvara operaatoritel on harva osa väärtusest, mille nad loovad.",
"opportunityTitle": "Võimalus",
"opportunityIntro": "Kuid hajutatud maailm on võimalik.",
"opportunityBullet1": "Tuhanded GPU-d on juba täna üle maailma alakasutatud.",
"opportunityBullet2": "Operaatorid soovivad arvutusvõimsuse eest õiglast tasu.",
"opportunityBullet3": "Arendajad tahavad avatud, tsensuurikindlat infrastruktuuri.",
"opportunityBullet4": "Kogukonnad soovivad suveräänsust ja vastupidavust digitaalsüsteemides.",
"opportunityBullet5": "Tehisintellekti kasv on traditsioonilisi pilvi edestanud — vaja on uusi vorme.",
"opportunityClosing": "Helexa on hetk, mil need jõud joondavad end."
},
"howItWorks": {
"title": "Kuidas võrk kujuneb",
"operators": {
"eyebrow": "Operaatorid käitavad sõlmi",
"title": "Igaüks saab panustada arvutusvõimsusega.",
"body": "Operaatorid käitavad Helexa sõlmi. Nad otsustavad, milliseid mudeleid hostida. Nad jäävad kontrollima oma riistvara ja oma majandust. Ilma kinnitusteta, ilma väravavahtideta."
},
"routing": {
"eyebrow": "Võrk suunab intelligentsi",
"title": "Nõudlus liigub läbi võrgustiku.",
"body": "Helexa õpib, kus on vaba võimsust, kus nõudlus kasvab ja millised sõlmed sobivad päringutele kõige paremini. Võrk kohaneb orgaaniliselt — nagu kasvav spiraal."
},
"value": {
"eyebrow": "Väärtus voolab tagasi",
"title": "Töö on tõendatav. Tasustamine on õiglane.",
"body": "Iga tööülesanne kannab endas krüptograafilist kviitungit. Operaatorid teenivad intelligentsi eest, mille loomises nad osalevad. Ilma platvormimaksuta. Ilma läbipaistmatu arvelduseta."
}
},
"principles": {
"title": "Ehitame põhimõtete, mitte platvormide peale",
"distributed": {
"title": "Hajutatud juba disainist",
"body": "Ei mingeid üksikuid tõrkepunkte. Ei mingit keskust. Võrk, mis muutub iga uue operaatoriga tugevamaks."
},
"participation": {
"title": "Avatud osalus",
"body": "Kui sul on arvutusressursse, saad panustada. Võrk tervitab kõiki — servaseadmeid, koduservereid ja andmekeskusi."
},
"fairness": {
"title": "Õiglus ja läbipaistvus",
"body": "Tulu põhineb tegelikul, krüptograafiliselt tõendatud tööl. Ei mingeid musti kaste. Ei mingeid varjatud tasusid."
},
"evolving": {
"title": "Arenev intelligentsus",
"body": "Võrk õpib nõudlusest. Mudeleid laetakse sinna, kus neid on vaja. Intelligentsus levib koostöö kaudu."
}
},
"roadAhead": {
"title": "Milleks Helexa pürgib",
"p1": "Globaalne intelligentsuskiht, mis kuulub kõigile, mida kannab sõlmede ja kogukondade heeliks.",
"p2": "Võrk, mis on vastupidav katkestustele, poliitikale, monopooliale ja riketele.",
"p3": "Uus majandusmudel, kus operaatorid, loojad ja kasutajad võidavad koos.",
"p4": "Ökosüsteem, kus uuendused kasvavad servast — mitte keskmest.",
"card": {
"eyebrow": "Visiooni hetkeseis",
"title": "Teel jagatud intelligentsusvõrguni",
"body": "Helexa on algusjärgus. Ideed on suuremad kui praegune teostus — ja see on taotluslik. Võrk kasvab järk-järgult, kujundatuna operaatorite ja ehitajate poolt."
}
},
"joinMesh": {
"badge": "Varajane etapp",
"title": "Võrk on kujunemas.",
"titleHighlight": "Sina võid olla selle osa.",
"lead": "Olenemata sellest, kas haldad riistvara, ehitad mudeleid või hoolid lihtsalt sellest, kuidas tehisintellekti juhitakse, sinu jaoks on võrgus koht.",
"ctaRunNode": "Käivita sõlm (varsti)",
"ctaJoinAnnouncements": "Liitu varajaste teadetega",
"ctaExploreCode": "Uuri koodi",
"footer": "Ilma suletud aedadeta. Ilma ühe omanikuta. Vaid võrk inimestest, riistvarast ja ideedest — mis koos loovad teistsuguse tuleviku intelligentsusele."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "محیط گفتگو",
"badge": "گفتگو",
"lead": "این نمای گفتگوی شماست. منطق مکالمه و مؤلفه‌های رابط کاربری خود را در این صفحه متصل کنید.",
"transcriptPlaceholder": "رونوشت گفتگو در اینجا نمایش داده می‌شود. پیام‌های مدل و کاربر را در یک محفظه قابل اسکرول رندر کنید؛ در صورت تمایل آن‌ها را بر اساس نوبت گروه‌بندی کنید.",
"inputPlaceholder": "برای شروع گفتگو یک پیام بنویسید…",
"send": "ارسال",
"clear": "پاک کردن",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "صفحه اصلی",
"docs": "مستندات",
"chat": "گفتگو",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "تغییر به حالت روشن",
"toDark": "تغییر به حالت تیره"
}
},
"lang": {
"bg": "بلغاری",
"de": "آلمانی",
"el": "یونانی",
"en": "انگلیسی",
"es": "اسپانیایی",
"et": "استونیایی",
"fr": "فرانسوی",
"he": "عبری",
"it": "ایتالیایی",
"nl": "هلندی",
"am": "امهری",
"ar": "عربی",
"da": "دانمارکی",
"fa": "فارسی",
"fi": "فنلاندی",
"ha": "هوسا",
"ig": "ایگبو",
"ka": "گرجی",
"kk": "قزاقی",
"ma": "داریجه",
"no": "نروژی",
"om": "اورومو",
"pl": "لهستانی",
"pt": "پرتغالی",
"ro": "رومانیایی",
"ru": "روسی",
"so": "سومالیایی",
"sr": "صربی",
"sv": "سوئدی",
"sw": "سواحیلی",
"ti": "تیگرینیایی",
"tr": "ترکی",
"uk": "اوکراینی",
"uz": "ازبکی",
"wo": "ولوف",
"yo": "یوروبا",
"zu": "زولو"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "شکل جدیدی از هوش",
"title": "شکل جدیدی از هوش",
"lead": "هلیکسا یک شبکه هوش مصنوعی خودسازمان‌ده است که توسط اپراتورهای مستقل راه‌اندازی می‌شود. باز. توزیع‌شده. در حال تکامل.",
"ctaJoinMesh": "به شبکه بپیوندید",
"ctaFollowProject": "پروژه را دنبال کنید",
"subcopy": "ساخته شده برای اپراتورها، سازندگان و جوامعی که معتقدند هوش مصنوعی باید باز، مقاوم و مشترک باشد.",
"imageAlt": "نمای بصری هلیکسی هلیکسا"
},
"intent": {
"title": "چرا هلیکسا وجود دارد",
"p1": "هوش مصنوعی در حال تبدیل شدن به قدرتمندترین زیرساخت روی زمین است. اما امروزه این قدرت در دست تعداد اندکی از شرکت‌ها متمرکز شده که توسط اولویت‌های خصوصی، محدودیت‌های جغرافیایی و اقتصادهای شکننده شکل گرفته است.",
"p2Intro": "هلیکسا چیز دیگری را تصور می‌کند:",
"bullet1": "هوشی که از همه‌جا رشد می‌کند، نه فقط از یک نقطه.",
"bullet2": "یک شبکه که در آن هر کس می‌تواند مشارکت کند و بهره‌مند شود.",
"bullet3": "سیستمی که خود را با تقاضا تطبیق می‌دهد، نه با دستورهای از بالا.",
"bullet4": "فناوری‌ای که جوامع را تقویت می‌کند، نه این‌که جایگزین آن‌ها شود.",
"closing": "هلیکسا یک پلتفرم نیست. یک «ابر» هم نیست.\nاین یک شبکه است ـ یک شبکه زنده و در حال تکامل از اپراتورهای مستقل که نوع جدیدی از هوش را شکل می‌دهد."
},
"whyNow": {
"title": "نقطه عطفی برای هوش مصنوعی",
"problemTitle": "مسئله",
"problemBullet1": "هوش مصنوعی سریع‌تر از هر فناوری پیشین در حال تمرکزگرایی است.",
"problemBullet2": "دسترسی به توان محاسباتی، قابلیت‌ها را تعریف می‌کند و این دسترسی رو به محدودتر شدن است.",
"problemBullet3": "مانع‌های هزینه، پژوهشگران، استارتاپ‌ها و جوامع را کنار می‌گذارد.",
"problemBullet4": "فشارهای ژئوپلیتیک و رگولاتوری، دسترسی جهانی را تهدید می‌کنند.",
"problemBullet5": "سازندگان مدل‌ها و اپراتورهای سخت‌افزار به ندرت در ارزشی که ایجاد می‌کنند سهیم می‌شوند.",
"opportunityTitle": "فرصت",
"opportunityIntro": "اما دنیایی توزیع‌شده ممکن است.",
"opportunityBullet1": "هزاران GPU در سراسر جهان هم‌اکنون کمتر از ظرفیت واقعی خود استفاده می‌شوند.",
"opportunityBullet2": "اپراتورها خواهان جبران منصفانه برای توان محاسباتی هستند.",
"opportunityBullet3": "توسعه‌دهندگان به دنبال زیرساختی باز و مقاوم در برابر سانسور هستند.",
"opportunityBullet4": "جوامع خواهان حاکمیت و تاب‌آوری در سیستم‌های دیجیتال خود هستند.",
"opportunityBullet5": "رشد هوش مصنوعی از ابرهای سنتی جلو زده است ـ شکل‌های جدیدی لازم است.",
"opportunityClosing": "هلیکسا لحظه‌ای است که این نیروها در آن همسو می‌شوند."
},
"howItWorks": {
"title": "شبکه چگونه شکل می‌گیرد",
"operators": {
"eyebrow": "اپراتورها نودها را اجرا می‌کنند",
"title": "هر کسی می‌تواند توان محاسباتی فراهم کند.",
"body": "اپراتورها نودهای هلیکسا را اجرا می‌کنند. آن‌ها تصمیم می‌گیرند چه مدل‌هایی را میزبانی کنند. کنترل سخت‌افزار و اقتصاد خود را در دست دارند. بدون تأییدیه، بدون دربان."
},
"routing": {
"eyebrow": "شبکه هوش را مسیردهی می‌کند",
"title": "تقاضا در شبکه جریان دارد.",
"body": "هلیکسا می‌آموزد کجا ظرفیت وجود دارد، کجا تقاضا در حال افزایش است و کدام نودها برای پاسخ‌گویی به درخواست‌ها مناسب‌ترند. شبکه به شکل ارگانیک ـ مانند یک هلیکسی که رشد می‌کند ـ خود را تطبیق می‌دهد."
},
"value": {
"eyebrow": "ارزش بازمی‌گردد",
"title": "کار اثبات‌شده است. پرداخت منصفانه است.",
"body": "هر کار، یک رسید رمزنگاری‌شده همراه دارد. اپراتورها بابت هوشی که در فراهم کردن آن مشارکت دارند، درآمد کسب می‌کنند. بدون مالیات پلتفرمی. بدون صورت‌حساب مبهم."
}
},
"principles": {
"title": "بر پایه اصول، نه پلتفرم‌ها",
"distributed": {
"title": "توزیع‌شده در طراحی",
"body": "هیچ نقطه تک‌نقطه‌ای شکست وجود ندارد. هیچ مرجع مرکزی‌ای در کار نیست. شبکه‌ای که با هر اپراتور جدید، قوی‌تر می‌شود."
},
"participation": {
"title": "مشارکت باز",
"body": "اگر توان محاسباتی دارید، می‌توانید مشارکت کنید. شبکه از همه استقبال می‌کند ـ چه لبه، چه سرور خانگی، چه دیتاسنتر."
},
"fairness": {
"title": "عدالت و شفافیت",
"body": "درآمدها بر اساس کار واقعی و به‌طور رمزنگاری‌شده تأیید می‌شوند. بدون جعبه سیاه. بدون کارمزد پنهان."
},
"evolving": {
"title": "هوشی در حال تکامل",
"body": "شبکه از دل تقاضا می‌آموزد. مدل‌ها در جایی بارگذاری می‌شوند که به آن‌ها نیاز است. هوش از طریق همکاری گسترش می‌یابد."
}
},
"roadAhead": {
"title": "هلیکسا می‌خواهد به چه چیزی تبدیل شود",
"p1": "لایه‌ای از هوش جهانی که متعلق به همه است، با یک هلیکسی از نودها و جوامع نیرو می‌گیرد.",
"p2": "شبکه‌ای مقاوم در برابر قطعی‌ها، سیاست، انحصارها و خطا.",
"p3": "مدل اقتصادی جدیدی که در آن اپراتورها، سازندگان و کاربران همگی منتفع می‌شوند.",
"p4": "اکوسیستمی که در آن نوآوری از لبه‌ها رشد می‌کند ـ نه از مرکز.",
"card": {
"eyebrow": "برشی از چشم‌انداز",
"title": "به سوی یک شبکه هوش مشترک",
"body": "هلیکسا در مرحله‌ای اولیه قرار دارد. ایده‌ها بزرگ‌تر از پیاده‌سازی فعلی‌اند ـ و این عمدی است. شبکه به‌صورت تدریجی رشد خواهد کرد و اپراتورها و سازندگان در شکل‌دهی به تکامل آن نقش خواهند داشت."
}
},
"joinMesh": {
"badge": "مرحله اولیه",
"title": "شبکه در حال شکل‌گیری است.",
"titleHighlight": "شما می‌توانید بخشی از آن باشید.",
"lead": "فرقی نمی‌کند سخت‌افزار اجرا می‌کنید، مدل می‌سازید، یا فقط برایتان مهم است که چگونه بر هوش مصنوعی حکمرانی می‌شود ـ در این شبکه جایی برای شما وجود دارد.",
"ctaRunNode": "اجرای یک نود (به‌زودی)",
"ctaJoinAnnouncements": "پیوستن به اعلان‌های اولیه",
"ctaExploreCode": "کد را کاوش کنید",
"footer": "بدون باغ‌های محصور. بدون یک مالک یکتا. تنها شبکه‌ای از انسان‌ها، سخت‌افزار و ایده‌ها ـ که آینده‌ای متفاوت برای هوش می‌آفرینند."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Keskustelutila",
"badge": "Keskustelu",
"lead": "Tämä on keskustelunäkymä. Liitä oma keskustelulogiikkasi ja käyttöliittymäkomponenttisi tälle sivulle.",
"transcriptPlaceholder": "Keskusteluloki näkyy tässä. Näytä mallin ja käyttäjän viestit vieritettävässä säiliössä, halutessasi vuoroittain ryhmiteltynä.",
"inputPlaceholder": "Kirjoita viesti aloittaaksesi keskustelun…",
"send": "Lähetä",
"clear": "Tyhjennä",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Etusivu",
"docs": "Dokumentaatio",
"chat": "Keskustelu",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Vaihda vaaleaan tilaan",
"toDark": "Vaihda tummaan tilaan"
}
},
"lang": {
"am": "amhara",
"ar": "arabia",
"bg": "bulgaria",
"da": "tanska",
"de": "saksa",
"el": "kreikka",
"en": "englanti",
"es": "espanja",
"et": "viro",
"fa": "farsi",
"fi": "suomi",
"fr": "ranska",
"ha": "hausa",
"he": "heprea",
"ig": "igbo",
"it": "italia",
"ka": "georgia",
"kk": "kazakki",
"ma": "darija",
"nl": "hollanti",
"no": "norja",
"om": "oromo",
"pl": "puola",
"pt": "portugali",
"ro": "romania",
"ru": "venäjä",
"so": "somali",
"sr": "serbia",
"sv": "ruotsi",
"sw": "swahili",
"ti": "tigrinja",
"tr": "turkki",
"uk": "ukraina",
"uz": "uzbekki",
"wo": "wolof",
"yo": "joruba",
"zu": "zulu"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Uudenlainen älykkyys",
"title": "Uudenlainen älykkyys",
"lead": "Helexa on itseorganisoituva AIverkko, jota pyörittävät riippumattomat operaattorit. Avoin. Hajautettu. Jatkuvasti kehittyvä.",
"ctaJoinMesh": "Liity verkkoon",
"ctaFollowProject": "Seuraa projektia",
"subcopy": "Rakennettu operaattoreille, rakentajille ja yhteisöille, jotka uskovat, että tekoälyn tulee olla avointa, kestävää ja jaettua.",
"imageAlt": "Helexaheliksin visualisointi"
},
"intent": {
"title": "Miksi Helexa on olemassa",
"p1": "Tekoälystä on tulossa maailman voimakkain infrastruktuuri. Tällä hetkellä valta on kuitenkin keskittynyt harvoille yrityksille, joita ohjaavat yksityiset intressit, maantieteelliset rajoitteet ja hauras talous.",
"p2Intro": "Helexa kuvittelee jotain muuta:",
"bullet1": "Älykkyys, joka kasvaa kaikkialta ei yhdestä paikasta.",
"bullet2": "Verkko, johon kuka tahansa voi osallistua ja josta kuka tahansa voi hyötyä.",
"bullet3": "Järjestelmä, joka mukautuu kysyntään, ei käskyihin.",
"bullet4": "Teknologia, joka vahvistaa yhteisöjä sen sijaan, että korvaisi ne.",
"closing": "Helexa ei ole alusta. Se ei ole pilvi.\nSe on verkko elävä, kehittyvä kudelma riippumattomia operaattoreita, jotka muodostavat uudenlaisen älykkyyden."
},
"whyNow": {
"title": "Käännekohta tekoälylle",
"problemTitle": "Ongelma",
"problemBullet1": "Tekoäly keskittyy nopeammin kuin mikään aiempi teknologia.",
"problemBullet2": "Laskentateho määrittää kyvykkyyden, ja siihen pääsy kapenee.",
"problemBullet3": "Kustannuskynnykset sulkevat ulos tutkijoita, startupyrityksiä ja yhteisöjä.",
"problemBullet4": "Geopoliittiset ja sääntelyyn liittyvät paineet uhkaavat globaalia saatavuutta.",
"problemBullet5": "Mallien kehittäjät ja laitteiston operaattorit pääsevät harvoin osallisiksi arvosta, jonka he luovat.",
"opportunityTitle": "Mahdollisuus",
"opportunityIntro": "Mutta hajautettu maailma on mahdollinen.",
"opportunityBullet1": "Tuhannet GPU:t ovat jo nyt vajaakäytöllä ympäri maailmaa.",
"opportunityBullet2": "Operaattorit haluavat reilun korvauksen laskentatehostaan.",
"opportunityBullet3": "Kehittäjät kaipaavat avointa, sensuurinkestävää infrastruktuuria.",
"opportunityBullet4": "Yhteisöt haluavat itsemääräämisoikeutta ja joustavuutta digitaalisiin järjestelmiin.",
"opportunityBullet5": "Tekoälyn kasvu on ohittanut perinteiset pilvet tarvitaan uudenlaisia rakenteita.",
"opportunityClosing": "Helexa on hetki, jolloin nämä voimat kohtaavat."
},
"howItWorks": {
"title": "Näin verkko muodostuu",
"operators": {
"eyebrow": "Operaattorit ajavat solmuja",
"title": "Kuka tahansa voi tarjota laskentatehoa.",
"body": "Operaattorit ajavat Helexasolmuja. He päättävät, mitä malleja ajetaan. He pitävät hallinnan omasta laitteistostaan ja taloudestaan. Ei hyväksyntäprosesseja, ei portinvartijoita."
},
"routing": {
"eyebrow": "Verkko ohjaa älykkyyttä",
"title": "Kysyntä virtaa verkon läpi.",
"body": "Helexa oppii, missä kapasiteettia on, missä kysyntä kasvaa ja mitkä solmut sopivat parhaiten pyyntöjen käsittelyyn. Verkko mukautuu orgaanisesti kuin kasvava heliksi."
},
"value": {
"eyebrow": "Arvo virtaa takaisin",
"title": "Työ todistetaan. Korvaus on reilu.",
"body": "Jokaisella tehtävällä on kryptografinen kuitti. Operaattorit saavat tuloa siitä älykkyydestä, jonka tuottamiseen he osallistuvat. Ei alustaveroa. Ei läpinäkymäistä laskutusta."
}
},
"principles": {
"title": "Rakennettu periaatteiden, ei alustojen varaan",
"distributed": {
"title": "Hajautettu suunnittelusta lähtien",
"body": "Ei yhtä kriittistä vikaantumispistettä. Ei keskitettyä auktoriteettia. Verkko, joka vahvistuu jokaisen uuden operaattorin myötä."
},
"participation": {
"title": "Avoin osallistuminen",
"body": "Jos sinulla on laskentatehoa, voit osallistua. Verkko toivottaa tervetulleeksi kaikki reunalaitteet, kotipalvelimet ja konesalit."
},
"fairness": {
"title": "Reiluus ja läpinäkyvyys",
"body": "Tulot perustuvat todelliseen työhön, kryptografisesti varmennettuna. Ei mustia laatikoita. Ei piilokuluja."
},
"evolving": {
"title": "Kehittyvä älykkyys",
"body": "Verkko oppii kysynnästä. Malit latautuvat sinne, missä niitä tarvitaan. Älykkyys leviää yhteistyön kautta."
}
},
"roadAhead": {
"title": "Mihin Helexa pyrkii",
"p1": "Globaali älykkyyskerros, joka kuuluu kaikille ja jota pyörittää solmujen ja yhteisöjen heliksi.",
"p2": "Verkko, joka kestää katkoksia, politiikkaa, monopoleja ja virheitä.",
"p3": "Uusi taloudellinen malli, jossa operaattorit, rakentajat ja käyttäjät hyötyvät kaikki.",
"p4": "Ekosysteemi, jossa innovaatio kasvaa reunoilta ei keskuksesta.",
"card": {
"eyebrow": "Vision yhteenveto",
"title": "Kohti jaettua älyverkkoa",
"body": "Helexa on varhaisessa vaiheessa. Ideat ovat suurempia kuin toteutus ja se on tarkoituksellista. Verkko kasvaa vaiheittain, kun operaattorit ja rakentajat muovaavat sen kehitystä yhdessä."
}
},
"joinMesh": {
"badge": "Varhainen vaihe",
"title": "Verkko on muodostumassa.",
"titleHighlight": "Voit olla osa sitä.",
"lead": "Olipa roolisi laitteiston ylläpitäjänä, mallien rakentajana tai vain tekoälyn hallinnasta kiinnostuneena, sinulle on paikka verkossa.",
"ctaRunNode": "Aja solmu (tulossa pian)",
"ctaJoinAnnouncements": "Liity varhaisiin ilmoituksiin",
"ctaExploreCode": "Tutustu koodiin",
"footer": "Ei suljettuja puutarhoja. Ei yhtä omistajaa. Vain verkko ihmisiä, laitteistoa ja ideoita rakentamassa erilaista tulevaisuutta älykkyydelle."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Espace de conversation",
"badge": "Chat",
"lead": "Ceci est la vue de chat. Branchez ici votre logique conversationnelle et vos composants dinterface.",
"transcriptPlaceholder": "La transcription du chat apparaîtra ici. Affichez les messages du modèle et de lutilisateur dans un conteneur défilant, éventuellement groupés par tour.",
"inputPlaceholder": "Tapez un message pour commencer à discuter…",
"send": "Envoyer",
"clear": "Effacer",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Accueil",
"docs": "Documentation",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Passer en mode clair",
"toDark": "Passer en mode sombre"
}
},
"lang": {
"bg": "Bulgare",
"de": "Allemand",
"el": "Grec",
"en": "Anglais",
"es": "Espagnol",
"et": "Estonien",
"fr": "Français",
"he": "Hébreu",
"it": "Italien",
"nl": "Néerlandais",
"da": "Danois",
"fi": "Finnois",
"no": "Norvégien",
"sv": "Suédois",
"ar": "Arabe",
"fa": "Persan",
"sw": "Swahili",
"ha": "Haoussa",
"am": "Amharique",
"yo": "Yorouba",
"zu": "Zoulou",
"ma": "Darija",
"ig": "Igbo",
"ka": "Géorgien",
"kk": "Kazakh",
"om": "Oromo",
"so": "Somali",
"ti": "Tigrigna",
"uz": "Ouzbek",
"wo": "Wolof",
"pl": "Polonais",
"pt": "Portugais",
"ro": "Roumain",
"ru": "Russe",
"sr": "Serbe",
"tr": "Turc",
"uk": "Ukrainien"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Une nouvelle forme dintelligence",
"title": "Une nouvelle forme dintelligence",
"lead": "Helexa est un maillage dIA auto-organisé, animé par des opérateurs indépendants. Ouvert. Distribué. En évolution.",
"ctaJoinMesh": "Rejoindre le maillage",
"ctaFollowProject": "Suivre le projet",
"subcopy": "Conçu pour les opérateurs, les bâtisseurs et les communautés qui pensent que lIA doit être ouverte, résiliente et partagée.",
"imageAlt": "Visuel de lhélice Helexa"
},
"intent": {
"title": "Pourquoi Helexa existe",
"p1": "LIA devient linfrastructure la plus puissante sur Terre. Mais aujourdhui, ce pouvoir est concentré entre les mains de quelques entreprises, façonné par des priorités privées, des limites géographiques et des modèles économiques fragiles.",
"p2Intro": "Helexa imagine autre chose :",
"bullet1": "Une intelligence qui se développe partout, pas en un seul endroit.",
"bullet2": "Un réseau où chacun peut contribuer et en bénéficier.",
"bullet3": "Un système qui sadapte à la demande, pas aux directives.",
"bullet4": "Une technologie qui renforce les communautés au lieu de les remplacer.",
"closing": "Helexa nest pas une plateforme. Ce nest pas un cloud.\nCest un maillage — un réseau vivant et évolutif dopérateurs indépendants formant un nouveau type dintelligence."
},
"whyNow": {
"title": "Un tournant pour lIA",
"problemTitle": "Le problème",
"problemBullet1": "LIA se centralise plus vite que nimporte quelle technologie auparavant.",
"problemBullet2": "Laccès au calcul définit les capacités, et cet accès se restreint.",
"problemBullet3": "Les barrières de coûts excluent les chercheurs, les startups et les communautés.",
"problemBullet4": "Les pressions géopolitiques et réglementaires menacent la disponibilité mondiale.",
"problemBullet5": "Les créateurs de modèles et les opérateurs de matériel partagent rarement la valeur quils produisent.",
"opportunityTitle": "Lopportunité",
"opportunityIntro": "Mais un monde distribué est possible.",
"opportunityBullet1": "Des milliers de GPU sont déjà sous-utilisés dans le monde.",
"opportunityBullet2": "Les opérateurs souhaitent une rémunération équitable pour leur puissance de calcul.",
"opportunityBullet3": "Les développeurs veulent une infrastructure ouverte et résistante à la censure.",
"opportunityBullet4": "Les communautés veulent de la souveraineté et de la résilience dans les systèmes numériques.",
"opportunityBullet5": "La croissance de lIA a dépassé les clouds traditionnels — de nouvelles formes sont nécessaires.",
"opportunityClosing": "Helexa est le moment où ces forces salignent."
},
"howItWorks": {
"title": "Comment le maillage se forme",
"operators": {
"eyebrow": "Les opérateurs font tourner les nœuds",
"title": "Tout le monde peut contribuer du calcul.",
"body": "Les opérateurs font tourner des nœuds Helexa. Ils décident quels modèles héberger. Ils gardent le contrôle de leur matériel et de leur économie. Pas dapprobations, pas de gardiens."
},
"routing": {
"eyebrow": "Le maillage route lintelligence",
"title": "La demande circule dans le réseau.",
"body": "Helexa apprend où se trouve la capacité, où la demande augmente et quels nœuds sont les mieux placés pour traiter les requêtes. Le maillage sadapte de façon organique — comme une hélice en croissance."
},
"value": {
"eyebrow": "La valeur revient vers la source",
"title": "Le travail est prouvé. Le paiement est juste.",
"body": "Chaque tâche porte un reçu cryptographique. Les opérateurs gagnent grâce à lintelligence quils contribuent à fournir. Pas de taxe de plateforme. Pas de facturation opaque."
}
},
"principles": {
"title": "Fondé sur des principes, pas sur des plateformes",
"distributed": {
"title": "Distribué par conception",
"body": "Aucun point de défaillance unique. Aucune autorité centrale. Un réseau qui se renforce à chaque nouvel opérateur."
},
"participation": {
"title": "Participation ouverte",
"body": "Si vous disposez de puissance de calcul, vous pouvez contribuer. Le maillage accueille tout le monde — périphérie, serveur domestique, centre de données."
},
"fairness": {
"title": "Équité et transparence",
"body": "Les revenus sont basés sur un travail réel, vérifié cryptographiquement. Pas de boîtes noires. Pas de frais cachés."
},
"evolving": {
"title": "Une intelligence en évolution",
"body": "Le maillage apprend de la demande. Les modèles se chargent là où ils sont nécessaires. Lintelligence se diffuse par la coopération."
}
},
"roadAhead": {
"title": "Ce que Helexa aspire à devenir",
"p1": "Une couche dintelligence globale qui appartient à tous, animée par une hélice de nœuds et de communautés.",
"p2": "Un réseau résilient face aux pannes, à la politique, aux monopoles et aux défaillances.",
"p3": "Un nouveau modèle économique où opérateurs, bâtisseurs et utilisateurs bénéficient tous de la valeur créée.",
"p4": "Un écosystème où linnovation grandit depuis les bords — pas depuis le centre.",
"card": {
"eyebrow": "Instantané de la vision",
"title": "Vers un maillage dintelligence partagé",
"body": "Helexa en est à ses débuts. Les idées dépassent limplémentation — et cest intentionnel. Le réseau grandira itérativement, avec les opérateurs et les bâtisseurs qui façonneront son évolution."
}
},
"joinMesh": {
"badge": "Phase initiale",
"title": "Le maillage se forme.",
"titleHighlight": "Vous pouvez en faire partie.",
"lead": "Que vous fassiez tourner du matériel, construisiez des modèles ou vous souciiez simplement de la gouvernance de lIA, il y a une place pour vous dans le maillage.",
"ctaRunNode": "Lancer un nœud (bientôt)",
"ctaJoinAnnouncements": "Rejoindre les annonces précoces",
"ctaExploreCode": "Explorer le code",
"footer": "Pas de jardins fermés. Pas de propriétaire unique. Juste un maillage de personnes, de matériel et didées — composant un futur différent pour lintelligence."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Wurin tattaunawa",
"badge": "Tattaunawa",
"lead": "Wannan shi ne shafin tattaunawa. Haɗa mantikarka ta hirar AI da abubuwan ginin UI a wannan shafi.",
"transcriptPlaceholder": "Rubutaccen tarihin tattaunawa zai bayyana a nan. Nuna saƙonnin samfurin AI da na mai amfani a cikin akwatin da ake iya gungurawa, kana iya rarrabe su bisa jujjuyawar magana.",
"inputPlaceholder": "Rubuta saƙo don fara tattaunawa…",
"send": "Aika",
"clear": "Share",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Gida",
"docs": "Takardu",
"chat": "Tattaunawa",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Canjawa zuwa yanayin haske",
"toDark": "Canjawa zuwa yanayin duhu"
}
},
"lang": {
"bg": "Bulgarian",
"de": "Jamusanci",
"el": "Girkanci",
"en": "Turanci",
"es": "Ispaniyanci",
"et": "Estonian",
"fr": "Faransanci",
"he": "Ibrananci",
"it": "Italiyanci",
"nl": "Holandanci",
"am": "Amharic",
"ar": "Larabci",
"da": "Danish",
"fa": "Farsanci",
"fi": "Finnish",
"ha": "Hausa",
"ig": "Igbo",
"ka": "Georgian",
"kk": "Kazakh",
"ma": "Darija",
"no": "Norwegian",
"om": "Oromo",
"pl": "Polish",
"pt": "Fotugis",
"ro": "Romaniyanci",
"ru": "Rashanci",
"so": "Somali",
"sr": "Serbian",
"sv": "Swedish",
"sw": "Kiswahili",
"ti": "Tigrinya",
"tr": "Turkish",
"uk": "Ukrainian",
"uz": "Uzbek",
"wo": "Wolof",
"yo": "Yorùbá",
"zu": "isiZulu"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Sabon siffar hankali",
"title": "Sabon Siffar Hankali",
"lead": "Helexa naurar sadarwar AI ce mai tsara kanta wacce yan kasuwa masu zaman kansu ke sarrafawa. Bude take. Tana rarrabewa. Tana ci gaba da canzawa.",
"ctaJoinMesh": "Shiga cikin cibiyar",
"ctaFollowProject": "Bi aikin",
"subcopy": "An gina ta ne don masu aiki da naura, masu gina tsarin da alummomi da ke ganin cewa AI ya kamata ta kasance a bude, mai ɗorewa kuma mai rabawa.",
"imageAlt": "Hoton helix na Helexa"
},
"intent": {
"title": "Dalilin Da Ya Sa Helexa Take Aiki",
"p1": "AI na zama mafi ƙarfi cikin dukkanin tsare-tsaren kayan aiki a doron kasa. Amma a yau, wannan ƙarfi ya takaita ga yan kamfanoni kaɗan, yana bin son ran masu su, iyakokin ƙasa da tsarin tattalin arziki masu rauni.",
"p2Intro": "Helexa tana tunanin wani abu dabam:",
"bullet1": "Hankali da ke tashi daga koina, ba daga wuri ɗaya ba.",
"bullet2": "Cibiyar sadarwa wacce kowa zai iya bada gudummawa kuma ya amfana.",
"bullet3": "Tsarin da ke daidaitawa da bukata, ba da umarni ba.",
"bullet4": "Fasaha wacce ke ƙarfafa alummomi maimakon maye gurbinsu.",
"closing": "Helexa ba dandali ba ce. Ba “gajimare” ba ce ma.\nCibiya ce — hanyar sadarwa mai rai, mai canzawa, ta masu aiki masu zaman kansu da ke samar da wani sabon nauin hankali."
},
"whyNow": {
"title": "Mahimmin Lokaci Ga AI",
"problemTitle": "Matsalar",
"problemBullet1": "AI na tattaruwa a wuri guda da sauri fiye da kowace fasaha da ta gabata.",
"problemBullet2": "Samun damar amfani da ƙarfi na komfuta shi ne ke ayyana abin da za a iya yi, kuma wannan damar na ƙara raguwa.",
"problemBullet3": "Tsammanin tsada na hana masu bincike, ƙananan kamfanoni da alummomi shiga.",
"problemBullet4": "Matsin lamba na siyasa da ƙaidoji na barazana ga samun dama a duniya baki ɗaya.",
"problemBullet5": "Masu ƙirƙirar samfurai da masu aiki da kayan aiki ba su cika samun rabo daga darajar da suke kunnawa ba.",
"opportunityTitle": "Damar",
"opportunityIntro": "Amma ana iya samun duniya mai rabewa.",
"opportunityBullet1": "Dubban GPU suna zaune ba tare da cikakken amfani ba a koina cikin duniya.",
"opportunityBullet2": "Masu aiki suna son lada mai adalci ga ƙarfinsu na komfuta.",
"opportunityBullet3": "Masu ci-gaba suna so suga tsarin da ba a rufe ba, kuma mai jure takunkumi.",
"opportunityBullet4": "Alummomi suna neman iko da ɗorewa cikin tsarin su na dijital.",
"opportunityBullet5": "Ci gaban AI ya rigaya ya zarce gajimaren gargajiya — ana buƙatar sabbin siffofi.",
"opportunityClosing": "Helexa ita ce lokacin da waɗannan ƙarfi ke haduwa wuri guda."
},
"howItWorks": {
"title": "Yadda Cibiyar Ke Samuwa",
"operators": {
"eyebrow": "Masu aiki na sarrafa nodi",
"title": "Kowa na iya bayar da ƙarfi na komfuta.",
"body": "Masu aiki suna gudanar da nodi na Helexa. Su ne suke yanke shawarar wane samfurin za su karɓa. Suna ci gaba da riƙe iko da kayan aiki da tattalin arzikinsu. Babu izini na musamman, babu masu tsare ƙofa."
},
"routing": {
"eyebrow": "Cibiyar na tura hankali",
"title": "Buƙata na yawo a cikin cibiyar sadarwa.",
"body": "Helexa na koya inda ake da isasshen ƙarfi, inda bukata ke ƙaruwa, da wane nodi ya fi dacewa don amsa buƙatu. Cibiyar na daidaitawa a hankali — kamar helix ɗin da ke ci gaba da girma."
},
"value": {
"eyebrow": "Daraja na dawowa baya",
"title": "Aikin ya tabbata. Biyan kudi adalci ne.",
"body": "Kowane aiki na zuwa da takardar shaidar sirri ta hanyar ƙididdigar tsaro. Masu aiki suna samun kuɗi saboda a cikin hankalin da suka taimaka wajen samarwa. Babu harajin dandali. Babu biyan kudi mai duhu."
}
},
"principles": {
"title": "An Gina Ta Kan Kaidoji, Ba Dandali Kawai Ba",
"distributed": {
"title": "An Rarraba ta Tun Basira",
"body": "Babu wuri guda na gazawa. Babu hukuma guda da ke iko. Wani cibiyar sadarwa ce da ke ƙara ƙarfi da kowane sabon mai aiki."
},
"participation": {
"title": "Halarta a Bude",
"body": "Idan kana da ikon komfuta, zaka iya bada gudummawa. Cibiyar na maraba da kowa — daga naurar gefe, zuwa uwar garken gida, zuwa cibiyar bayanai."
},
"fairness": {
"title": "Adalci da Bayyanawa",
"body": "Samun riba ya dogara ne ga aiki na gaskiya, wanda aka tabbatar da shi ta hanyar sirrin ƙididdiga. Babu “akwatu baƙaƙe”. Babu ɓoyayyun kuɗaɗe."
},
"evolving": {
"title": "Hankali Mai Ci gaba da Sauyawa",
"body": "Cibiyar na koyo daga bukata. Ana sauke samfurori a inda ake buƙatarsu. Hankali na yaduwa ta hanyar haɗin kai."
}
},
"roadAhead": {
"title": "Me Helexa Ke Nufin Ta Zama",
"p1": "Matakin hankali na duniya wanda kowa ke da nasa, ana turawa da helix na nodi da alummomi.",
"p2": "Cibiyar sadarwa mai jure katsewa, siyasa, mamallaka da gazawa.",
"p3": "Sabon tsari na tattalin arziki wanda masu aiki, masu gini da masu amfani duk za su amfana.",
"p4": "Muhallin da kirkirekirkire ke fitowa daga gefe — ba daga tsakiya ba.",
"card": {
"eyebrow": "Hoton hangen nesa",
"title": "Zuƙowa zuwa wutan hankali da aka raba",
"body": "Helexa tana matakin farko. Tunani ya fi girma fiye da abin da aka aiwatar yanzu — kuma hakan a riga aka nufa. Cibiyar zata bunƙasa a hankali, ta hanyar tasirin masu aiki da masu gini."
}
},
"joinMesh": {
"badge": "Mataki na farko",
"title": "Cibiyar na Samuwa.",
"titleHighlight": "Kuma Kai ma Za ka Iya Zama Cikinta.",
"lead": "Ko kana sarrafa kayan aiki ne, kana gina samfurori, ko kuma kawai kana damuwa da yadda ake tafiyar da AI, akwai wuri a cikin wannan cibiyar a gare ka.",
"ctaRunNode": "Gudanar da node (nan gaba kadan)",
"ctaJoinAnnouncements": "Shiga jerin sanarwar farko",
"ctaExploreCode": "Duba lambar tushe",
"footer": "Babu lambun da aka kewaye da katanga. Babu mai shi guda. Kawai wutan mutane, kayan aiki da tunani — suna tsara wani irin makomar hankali dabam."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "מרחב שיחה",
"badge": "צ'אט",
"lead": "זהו מסך הצ'אט. חברו לכאן את לוגיקת השיחה שלכם ורכיבי ה־UI המתאימים.",
"transcriptPlaceholder": "תמליל הצ'אט יופיע כאן. הציגו את ההודעות של המודל ושל המשתמש/ת במכל גלילה, אפשר גם לקבץ לפי סבב שיחה.",
"inputPlaceholder": "כתבו הודעה כדי להתחיל שיחה…",
"send": "שליחה",
"clear": "ניקוי",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "דף הבית",
"docs": "תיעוד",
"chat": "צ'אט",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "החלפה למצב בהיר",
"toDark": "החלפה למצב כהה"
}
},
"lang": {
"am": "אמהרית",
"ar": "ערבית",
"bg": "בולגרית",
"da": "דנית",
"de": "גרמנית",
"el": "יוונית",
"en": "אנגלית",
"es": "ספרדית",
"et": "אסטונית",
"fa": "פרסית",
"fi": "פינית",
"fr": "צרפתית",
"ha": "האוסה",
"he": "עברית",
"ig": "איגבו",
"it": "איטלקית",
"ka": "גאורגית",
"kk": "קזחית",
"ma": "דאריג'ה",
"nl": "הולנדית",
"no": "נורווגית",
"om": "אורומו",
"pl": "פולנית",
"pt": "פורטוגזית",
"ro": "רומנית",
"ru": "רוסית",
"so": "סומלית",
"sr": "סרבית",
"sv": "שוודית",
"sw": "סווהילית",
"ti": "טיגרינית",
"tr": "טורקית",
"uk": "אוקראינית",
"uz": "אוזבקית",
"wo": "וולוף",
"yo": "יורובה",
"zu": "זולו"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "צורה חדשה של אינטליגנציה",
"title": "צורה חדשה של אינטליגנציה",
"lead": "Helexa היא רשת בינה מלאכותית המתארגנת מעצמה, המופעלת על‑ידי אופרטורים עצמאיים. פתוחה. מבוזרת. מתפתחת.",
"ctaJoinMesh": "הצטרפות לרשת",
"ctaFollowProject": "מעקב אחר הפרויקט",
"subcopy": "נבנתה עבור אופרטורים, בונים וקהילות המאמינים שבינה מלאכותית צריכה להיות פתוחה, עמידה ומשותפת.",
"imageAlt": "הדמיית הליקס של Helexa"
},
"intent": {
"title": "למה Helexa קיימת",
"p1": "בינה מלאכותית הופכת להיות התשתית החזקה ביותר על פני כדור הארץ. אבל היום, הכוח הזה מרוכז בידי קומץ חברות, מעוצב על‑פי סדרי עדיפויות פרטיים, מגבלות גאוגרפיות וכלכלה שברירית.",
"p2Intro": "Helexa מדמיינת משהו אחר:",
"bullet1": "אינטליגנציה שצומחת מכל מקום, לא מנקודה אחת.",
"bullet2": "רשת שבה כל אחד יכול לתרום ולהרוויח.",
"bullet3": "מערכת שמסתגלת לביקוש, לא להוראות מלמעלה.",
"bullet4": "טכנולוגיה שמחזקת קהילות במקום להחליף אותן.",
"closing": "Helexa איננה פלטפורמה. היא איננה ענן.\nזו רשת — סריג חי ומתפתח של אופרטורים עצמאיים שיוצרים צורה חדשה של אינטליגנציה."
},
"whyNow": {
"title": "נקודת מפנה עבור הבינה המלאכותית",
"problemTitle": "הבעיה",
"problemBullet1": "בינה מלאכותית מתרכזת מהר יותר מכל טכנולוגיה שקודמתה.",
"problemBullet2": "הגישה לחישוב מגדירה את היכולת, והגישה הזאת הולכת ונסגרת.",
"problemBullet3": "חסמי עלות מדירים חוקרים, סטארטאפים וקהילות.",
"problemBullet4": "לחצים גאופוליטיים ורגולטוריים מאיימים על זמינות גלובלית.",
"problemBullet5": "יוצרי המודלים ומפעילי החומרה כמעט אף פעם לא נהנים מהערך שהם יוצרים.",
"opportunityTitle": "ההזדמנות",
"opportunityIntro": "אבל עולם מבוזר הוא אפשרי.",
"opportunityBullet1": "אלפי כרטיסי GPU ברחבי העולם נמצאים כבר היום בתת‑ניצול.",
"opportunityBullet2": "אופרטורים רוצים תגמול הוגן על החישוב שהם מספקים.",
"opportunityBullet3": "מפתחים מחפשים תשתית פתוחה ועמידה בפני צנזורה.",
"opportunityBullet4": "קהילות רוצות ריבונות ועמידות במערכות הדיגיטליות שלהן.",
"opportunityBullet5": "קצב הצמיחה של הבינה המלאכותית עקף את העננים המסורתיים — נדרשות צורות חדשות.",
"opportunityClosing": "Helexa היא הרגע שבו הכוחות האלה מתיישרים."
},
"howItWorks": {
"title": "איך הרשת נוצרת",
"operators": {
"eyebrow": "אופרטורים מריצים צמתים",
"title": "כל אחד יכול לתרום כוח חישוב.",
"body": "אופרטורים מריצים צמתים של Helexa. הם מחליטים אילו מודלים לארח. הם נשארים בשליטה על החומרה ועל הכלכלה שלהם. בלי אישורים, בלי שומרי‑סף."
},
"routing": {
"eyebrow": "הרשת מנתבת אינטליגנציה",
"title": "הביקוש זורם דרך הרשת.",
"body": "Helexa לומדת איפה יש קיבולת, איפה הביקוש גדל ואילו צמתים מתאימים ביותר לטפל בבקשות. הרשת מסתגלת באופן אורגני — כמו הליקס שממשיך לצמוח."
},
"value": {
"eyebrow": "הערך זורם בחזרה",
"title": "העבודה מוכחת. התגמול הוגן.",
"body": "כל משימה נושאת איתה קבלה קריפטוגרפית. האופרטורים מרוויחים עבור האינטליגנציה שהם מסייעים לספק. בלי מס פלטפורמה. בלי חיוב לא שקוף."
}
},
"principles": {
"title": "מבוססת על עקרונות, לא על פלטפורמות",
"distributed": {
"title": "מבוזרת כבר בתכנון",
"body": "אין נקודת כשל יחידה. אין סמכות מרכזית. רשת שהופכת חזקה יותר עם כל אופרטור חדש."
},
"participation": {
"title": "השתתפות פתוחה",
"body": "אם יש לך כוח חישוב — אתה יכול לתרום. הרשת מקבלת אליה את כולם: מהקצה, דרך שרת ביתי ועד מרכז נתונים."
},
"fairness": {
"title": "הוגנות ושקיפות",
"body": "ההכנסות מבוססות על עבודה אמיתית, מאומתת קריפטוגרפית. בלי קופסאות שחורות. בלי עמלות נסתרות."
},
"evolving": {
"title": "אינטליגנציה מתפתחת",
"body": "הרשת לומדת מהביקוש. מודלים נטענים היכן שהם נדרשים. האינטליגנציה מתפשטת דרך שיתוף פעולה."
}
},
"roadAhead": {
"title": "לאן Helexa שואפת להגיע",
"p1": "שכבת אינטליגנציה גלובלית ששייכת לכולם, המונעת על‑ידי הליקס של צמתים וקהילות.",
"p2": "רשת עמידה בפני נפילות, פוליטיקה, מונופולים וכשלים.",
"p3": "מודל כלכלי חדש שבו אופרטורים, בונים ומשתמשים כולם מרוויחים.",
"p4": "אקוסיסטם שבו החדשנות צומחת מהשוליים — לא מהמרכז.",
"card": {
"eyebrow": "מבט מהיר על החזון",
"title": "בדרך לרשת אינטליגנציה משותפת",
"body": "Helexa נמצאת בשלב מוקדם. הרעיונות גדולים מהיישום הנוכחי — וזה בכוונה. הרשת תצמח באופן הדרגתי, כאשר אופרטורים ובונים יעצבו יחד את האבולוציה שלה."
}
},
"joinMesh": {
"badge": "שלב מוקדם",
"title": "הרשת נוצרת.",
"titleHighlight": "אתם יכולים להיות חלק ממנה.",
"lead": "בין אם אתם מריצים חומרה, בונים מודלים או פשוט אכפת לכם מאיך שבינה מלאכותית מנוהלת — יש לכם מקום בתוך הרשת.",
"ctaRunNode": "הפעלת צומת (בקרוב)",
"ctaJoinAnnouncements": "הצטרפות לעדכונים מוקדמים",
"ctaExploreCode": "חקירת הקוד",
"footer": "בלי גנים מוקפים חומה. בלי בעלים יחיד. רק רשת של אנשים, חומרה ורעיונות — שמרכיבה עתיד אחר לאינטליגנציה."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Spazio di conversazione",
"badge": "Chat",
"lead": "Questa è la vista di chat. Collega qui la tua logica conversazionale e i componenti dellinterfaccia utente.",
"transcriptPlaceholder": "La trascrizione della chat apparirà qui. Visualizza i messaggi del modello e dellutente in un contenitore scorrevole, eventualmente raggruppati per turno.",
"inputPlaceholder": "Scrivi un messaggio per iniziare a chattare…",
"send": "Invia",
"clear": "Pulisci",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Home",
"docs": "Documentazione",
"chat": "Chat",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Passa alla modalità chiara",
"toDark": "Passa alla modalità scura"
}
},
"lang": {
"bg": "Bulgaro",
"de": "Tedesco",
"el": "Greco",
"en": "Inglese",
"es": "Spagnolo",
"et": "Estone",
"fr": "Francese",
"he": "Ebraico",
"it": "Italiano",
"nl": "Olandese",
"da": "Danese",
"fi": "Finlandese",
"no": "Norvegese",
"sv": "Svedese",
"ar": "Arabo",
"fa": "Persiano",
"sw": "Swahili",
"ha": "Hausa",
"am": "Amarico",
"yo": "Yoruba",
"zu": "Zulu",
"ma": "Darija",
"ig": "Igbo",
"ka": "Georgiano",
"kk": "Kazako",
"om": "Oromo",
"so": "Somalo",
"ti": "Tigrino",
"uz": "Uzbeco",
"wo": "Wolof",
"pl": "Polacco",
"pt": "Portoghese",
"ro": "Rumeno",
"ru": "Russo",
"sr": "Serbo",
"tr": "Turco",
"uk": "Ucraino"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Una nuova forma di intelligenza",
"title": "Una nuova forma di intelligenza",
"lead": "Helexa è una mesh di IA autoorganizzata alimentata da operatori indipendenti. Aperta. Distribuita. In evoluzione.",
"ctaJoinMesh": "Unisciti alla mesh",
"ctaFollowProject": "Segui il progetto",
"subcopy": "Creata per operatori, builder e comunità che credono che lIA debba essere aperta, resiliente e condivisa.",
"imageAlt": "Visuale dellelica Helexa"
},
"intent": {
"title": "Perché esiste Helexa",
"p1": "LIA sta diventando linfrastruttura più potente sulla Terra. Ma oggi questo potere è concentrato in poche aziende, modellato da priorità private, limiti geografici ed economie fragili.",
"p2Intro": "Helexa immagina qualcosa di diverso:",
"bullet1": "Unintelligenza che cresce da ovunque, non da un solo luogo.",
"bullet2": "Una rete in cui chiunque può contribuire e trarre beneficio.",
"bullet3": "Un sistema che si adatta alla domanda, non alle direttive.",
"bullet4": "Tecnologia che rafforza le comunità invece di sostituirle.",
"closing": "Helexa non è una piattaforma. Non è un cloud.\nÈ una mesh — una trama viva ed evolutiva di operatori indipendenti che formano un nuovo tipo di intelligenza."
},
"whyNow": {
"title": "Un punto di svolta per lIA",
"problemTitle": "Il problema",
"problemBullet1": "LIA si sta centralizzando più velocemente di qualsiasi tecnologia precedente.",
"problemBullet2": "Laccesso al calcolo definisce le capacità, e questo accesso si sta restringendo.",
"problemBullet3": "Le barriere di costo escludono ricercatori, startup e comunità.",
"problemBullet4": "Pressioni geopolitiche e normative minacciano la disponibilità globale.",
"problemBullet5": "I creatori di modelli e gli operatori dellhardware raramente condividono il valore che producono.",
"opportunityTitle": "Lopportunità",
"opportunityIntro": "Ma un mondo distribuito è possibile.",
"opportunityBullet1": "Migliaia di GPU sono già sottoutilizzate, in tutto il mondo.",
"opportunityBullet2": "Gli operatori vogliono una compensazione equa per il calcolo.",
"opportunityBullet3": "Gli sviluppatori vogliono uninfrastruttura aperta e resistente alla censura.",
"opportunityBullet4": "Le comunità vogliono sovranità e resilienza nei sistemi digitali.",
"opportunityBullet5": "La crescita dellIA ha superato i cloud tradizionali — servono nuove forme.",
"opportunityClosing": "Helexa è il momento in cui queste forze si allineano."
},
"howItWorks": {
"title": "Come si forma la mesh",
"operators": {
"eyebrow": "Gli operatori eseguono i nodi",
"title": "Chiunque può contribuire con calcolo.",
"body": "Gli operatori eseguono nodi Helexa. Decidono quali modelli ospitare. Mantengono il controllo del proprio hardware e della propria economia. Nessuna approvazione, nessun gatekeeper."
},
"routing": {
"eyebrow": "La mesh instrada lintelligenza",
"title": "La domanda scorre attraverso la rete.",
"body": "Helexa impara dove esiste capacità, dove la domanda sta crescendo e quali nodi sono più adatti a servire le richieste. La mesh si adatta in modo organico — come unelica in crescita."
},
"value": {
"eyebrow": "Il valore ritorna indietro",
"title": "Il lavoro è dimostrabile. Il pagamento è equo.",
"body": "Ogni job porta con sé una ricevuta crittografica. Gli operatori guadagnano per lintelligenza che contribuiscono a fornire. Nessuna tassazione di piattaforma. Nessuna fatturazione opaca."
}
},
"principles": {
"title": "Costruita su principi, non su piattaforme",
"distributed": {
"title": "Distribuita per progettazione",
"body": "Nessun singolo punto di fallimento. Nessuna autorità centrale. Una rete che diventa più forte con ogni nuovo operatore."
},
"participation": {
"title": "Partecipazione aperta",
"body": "Se hai capacità di calcolo, puoi contribuire. La mesh accoglie tutti — edge, server domestico, data center."
},
"fairness": {
"title": "Equità e trasparenza",
"body": "I guadagni si basano su lavoro reale, verificato crittograficamente. Niente scatole nere. Nessun costo nascosto."
},
"evolving": {
"title": "Intelligenza in evoluzione",
"body": "La mesh impara dalla domanda. I modelli vengono caricati dove servono. Lintelligenza si diffonde attraverso la cooperazione."
}
},
"roadAhead": {
"title": "Cosa aspira a diventare Helexa",
"p1": "Un livello di intelligenza globale che appartiene a tutti, alimentato da unelica di nodi e comunità.",
"p2": "Una rete resiliente a interruzioni, politica, monopoli e guasti.",
"p3": "Un nuovo modello economico in cui operatori, builder e utenti traggono tutti beneficio.",
"p4": "Un ecosistema in cui linnovazione cresce dai bordi — non dal centro.",
"card": {
"eyebrow": "Istantanea della visione",
"title": "Verso una mesh di intelligenza condivisa",
"body": "Helexa è agli inizi. Le idee sono più grandi dellimplementazione — e questo è intenzionale. La rete crescerà in modo iterativo, con operatori e builder che ne plasmeranno levoluzione."
}
},
"joinMesh": {
"badge": "Fase iniziale",
"title": "La mesh si sta formando.",
"titleHighlight": "Puoi farne parte.",
"lead": "Che tu gestisca hardware, costruisca modelli o ti interessi semplicemente a come viene governata lIA, nella mesh cè un posto per te.",
"ctaRunNode": "Esegui un nodo (presto)",
"ctaJoinAnnouncements": "Unisciti agli annunci iniziali",
"ctaExploreCode": "Esplora il codice",
"footer": "Niente giardini recintati. Nessun unico proprietario. Solo una mesh di persone, hardware e idee — che compone un futuro diverso per lintelligenza."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "საუბრის სივრცე",
"badge": "ჩატი",
"lead": "ეს არის ჩატის გვერდი. ჩასვი შენი დიალოგის ლოგიკა და UI კომპონენტები ამ გვერდზე.",
"transcriptPlaceholder": "ჩატის ისტორია გამოჩნდება აქ. ნაჩვენებია მოდელისა და მომხმარებლის შეტყობინებები გადახვევად კონტეინერში, სურვილის შემთხვევაში ტურნების მიხედვით დაჯგუფებით.",
"inputPlaceholder": "დაიწყე საუბარი — მიწერე შეტყობინება…",
"send": "გაგზავნა",
"clear": "გასუფთავება",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "მთავარი",
"docs": "დოკუმენტაცია",
"chat": "ჩატი",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "გადართვა ნათელ რეჟიმზე",
"toDark": "გადართვა მუქ რეჟიმზე"
}
},
"lang": {
"bg": "ბულგარული",
"de": "გერმანული",
"el": "ბერძნული",
"en": "ინგლისური",
"es": "ესპანური",
"et": "ესტონური",
"fr": "ფრანგული",
"he": "ივრითი",
"it": "იტალიური",
"nl": "ჰოლანდიური",
"da": "დანიური",
"fi": "ფინური",
"no": "ნორვეგიული",
"sv": "შვედური",
"ar": "არაბული",
"fa": "სპარსული",
"sw": "სუაჰილი",
"ha": "ჰაუფსა",
"am": "ამჰარული",
"yo": "იორუბა",
"zu": "ზულუ",
"ma": "დარიჯა",
"ig": "იგბო",
"ka": "ქართული",
"kk": "ყაზახური",
"om": "ორომო",
"so": "სომალური",
"ti": "ტიგრინია",
"uz": "უზბეკური",
"wo": "ვოლოფური",
"pl": "პოლონური",
"pt": "პორტუგალიური",
"ro": "რუმინული",
"ru": "რუსული",
"sr": "სერბული",
"tr": "თურქული",
"uk": "უკრაინული"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "გონების ახალი ფორმა",
"title": "გონების ახალი ფორმა",
"lead": "Helexa არის თვითორგანიზებული AI-ბადე, რომელსაც დამოუკიდებელი ოპერატორები მართავენ. ღია. განაწილებული. განვითარებადი.",
"ctaJoinMesh": "შეერთდი ბადეს",
"ctaFollowProject": "გამოიწერე პროექტი",
"subcopy": "შექმნილია ოპერატორებისთვის, შემქმნელებისა და თემებისთვის, რომლებიც თვლიან, რომ AI უნდა იყოს ღია, მდგრადი და გაზიარებული.",
"imageAlt": "Helexa-ს ჰელიქსის ვიზუალი"
},
"intent": {
"title": "რატომ არსებობს Helexa",
"p1": "AI გადაიქცა დედამიწის ყველაზე ძლიერ ინფრასტრუქტურად. მაგრამ დღეს ეს ძალა მუჭა-ჩალის კომპანიებშია კონცენტრირებული კერძო ინტერესებით, გეოგრაფიული შეზღუდვებითა და მყიფე ეკონომიკით ნაკარნახევი.",
"p2Intro": "Helexa სხვაგვარ მომავალს წარმოიდგენს:",
"bullet1": "გონიერება, რომელიც ყველაფერრგან იზრდება და არა ერთი წერტილიდან.",
"bullet2": "ბადე, სადაც ყველას შეუძლია შეიტანოს წვლილი და მიიღოს სარგებელი.",
"bullet3": "სისტემა, რომელიც ემორჩილება არა მითითებებს, არამედ მოთხოვნას.",
"bullet4": "ტექნოლოგია, რომელიც აძლიერებს თემებს და არ ჩანაცვლებს მათ.",
"closing": "Helexa არ არის პლატფორმა. ის არ არის ღრუბელი.\nეს არის ბადე — ცოცხალი, განვითარებადი ქსელი დამოუკიდებელი ოპერატორებისა, რომლებიც გონიერების ახალ ფორმას ქმნიან."
},
"whyNow": {
"title": "მოტრიალების წერტილი AI-ისთვის",
"problemTitle": "პრობლემა",
"problemBullet1": "AI უფრო სწრაფად კონცენტრირდება, ვიდრე ნებისმიერი წინა ტექნოლოგია.",
"problemBullet2": "გამოთვლით რესურსებზე წვდომა განსაზღვრავს შესაძლებლობებს, და ეს წვდომა სულ უფრო ვიწროვდება.",
"problemBullet3": "ღირებულების ბარიერები Researchers-ს, სტარტაპებს და თემებს თამაშგარეში ტოვებს.",
"problemBullet4": "გეოპოლიტიკური და მარეგულირებელი ზეწოლა გლობალურ ხელმისაწვდომობას ემუქრება.",
"problemBullet5": "მოდელების შემქმნელები და მოწყობილობების ოპერატორები იშვიათად იღებენ იმ ღირებულების სამართლიან წილს, რომელსაც ქმნიან.",
"opportunityTitle": "შესაძლებლობა",
"opportunityIntro": "მაგრამ განაწილებული სამყარო შესაძლებელია.",
"opportunityBullet1": "ათასობით GPU უკვე ნაკლებად გამოიყენება მთელ მსოფლიოში.",
"opportunityBullet2": "ოპერატორებს სურთ სამართლიანი ანაზღაურება გამოთვლითი რესურსებისთვის.",
"opportunityBullet3": "დეველოპერებს სჭირდებათ ღია, ცენზურაგამძლე ინფრასტრუქტურა.",
"opportunityBullet4": "თემებს სურთ სუვერენიტეტი და გამძლეობა ციფრულ სისტემებში.",
"opportunityBullet5": "AI-ის ზრდამ გადაასწრო ტრადიციულ ღრუბლებს — საჭიროა ახალი ფორმები.",
"opportunityClosing": "Helexa არის მომენტი, როდესაც ეს ძალები ერთმანეთს ხვდება."
},
"howItWorks": {
"title": "როგორ ფორმირდება ბადე",
"operators": {
"eyebrow": "ოპერატორები მართავენ ნოდებს",
"title": "ყველას შეუძლია შეიტანოს გამოთვლითი რესურსი.",
"body": "ოპერატორები უშვებენ Helexa-ს ნოდებს. ისინი წყვეტენ, რომელ მოდელებს უმასპინძლონ. ისინი ინარჩუნებენ კონტროლს თავიანთ მოწყობილობაზე და ეკონომიკაზე. არ არსებობს დამტკიცებები, არ არსებობს მეკარენი."
},
"routing": {
"eyebrow": "ბადე მიმართულებას აძლევს გონიერებას",
"title": "მოთხოვნა გადის ქსელის გავლით.",
"body": "Helexa სწავლობს, სად არის ხელმისაწვდომი რესურსი, სად იზრდება მოთხოვნა და რომელი ნოდებია საუკეთესო განსაზღვრული მოთხოვნების დასაკმაყოფილებლად. ბადე ორგანულად ერგება სიტუაციას — როგორც მზარდი ჰელიქსი."
},
"value": {
"eyebrow": "ღირებულება ბრუნდება უკან",
"title": "შრომა დამტკიცებულია. ანაზღაურება სამართლიანია.",
"body": "ყოველი მოთხოვნა ატარებს კრიპტოგრაფიულ ქვითარს. ოპერატორები იღებენ შემოსავალს იმ გონიერებისთვის, რომლის მიწოდებაშიც მონაწილეობენ. არც პლატფორმის გადასახადი. არც გაუმჭვირვალე ბილინგი."
}
},
"principles": {
"title": "აგებულია პრინციპებზე, არა პლატფორმებზე",
"distributed": {
"title": "განაწილებული დიზაინით",
"body": "კ żadარი ერთიანი მარცხის წერტილი. არც ცენტრალური ხელისუფლება. ბადე, რომელიც უფრო ძლიერია ყოველი ახალი ოპერატორის დამატებით."
},
"participation": {
"title": "ღია მონაწილეობა",
"body": "თუ გაქვს გამოთვლითი რესურსი, შეგიძლია შეიტანო წვლილი. ბადე ყველას იღებს — edge-დან, სახლის სერვერიდან, მონაცემთა ცენტრიდან."
},
"fairness": {
"title": "სამართლიანობა და გამჭვირვალობა",
"body": "შემოსავალი ეფუძნება რეალურ სამუშაოს, კრიპტოგრაფიულად დამოწმებულს. არც შავი ყუთები. არც დამალული საკომისიოები."
},
"evolving": {
"title": "მუდმივად განვითარებადი გონიერება",
"body": "ბადე სწავლობს მოთხოვნას. მოდელები იტვირთება იქ, სადაც საჭიროა. გონიერება ვრცელდება თანამშრომლობის გზით."
}
},
"roadAhead": {
"title": "რა მიმართულებით უნდა განვითარდეს Helexa",
"p1": "გლობალური გონიერების ფენა, რომელიც ყველას ეკუთვნის და რომელსაც ნოდებისა და თემების ჰელიქსი ამოძრავებს.",
"p2": "ქსელი, რომელიც გამძლეა გათიშვების, პოლიტიკის, მონოპოლიებისა და სხვადასხვა ხარვეზის მიმართ.",
"p3": "ახალი ეკონომიკური მოდელი, სადაც ოპერატორები, შემქმნელები და მომხმარებლები ერთდროულად სარგებელს იღებენ.",
"p4": "ეკოსისტემა, სადაც ინოვაცია იზრდება პერიფერიიდან — და არა ცენტრიდან.",
"card": {
"eyebrow": "ხედვის სnapshot-ი",
"title": "გზა გაზიარებული გონიერების ბადემდე",
"body": "Helexa ჯერ ადრეულ ეტაპზეა. იდეები უფრო დიდია, ვიდრე არსებული იმპლემენტაცია — და ეს შეგნებული არჩევანია. ბადე ეტაპობრივად გაიზრდება, ოპერატორებისა და შემქმნელების ერთობლივი ჩართულობით."
}
},
"joinMesh": {
"badge": "ადრეული ფაზა",
"title": "ბადე ყალიბდება.",
"titleHighlight": "შეგიძლია მისი ნაწილი გახდე.",
"lead": "თუ მართავ მოწყობილობას, აგებ მოდელებს ან უბრალოდ გაწუხებს, როგორ იმართება AI, შენთვის ადგილი აუცილებლად მოიძებნება ბადეში.",
"ctaRunNode": "გაუშვი ნოდი (მალე)",
"ctaJoinAnnouncements": "შეერთდი ადრეული ცნობების სიას",
"ctaExploreCode": "გააცანი კოდს",
"footer": "არც შემოღობილი ბაღები. არც ერთი მეპატრონე. მხოლოდ ბადე ადამიანების, მოწყობილობებისა და იდეებისგან — რომლებიც გონიერების სხვა მომავალს აყალიბებენ."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "Сөйлесу кеңістігі",
"badge": "Чат",
"lead": "Бұл — чат көрінісі. Осы бетке өзіңіздің диалог логикаңызды және UI компоненттеріңізді қосыңыз.",
"transcriptPlaceholder": "Мұнда чат журналын көрсетіңіз. Модель мен пайдаланушы хабарламаларын жылжымалы контейнерде, қажет болса кезек бойынша топтастырып шығарыңыз.",
"inputPlaceholder": "Чатты бастау үшін хабарлама жазыңыз…",
"send": "Жіберу",
"clear": "Тазалау",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

View File

@@ -1,63 +0,0 @@
{
"app": {
"name": "helexa.ai"
},
"nav": {
"home": "Басты бет",
"docs": "Құжаттама",
"chat": "Чат",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
"account": "Account",
"logout": "Sign out"
},
"theme": {
"toggle": {
"toLight": "Жарық режиміне ауыстыру",
"toDark": "Қараңғы режимге ауыстыру"
}
},
"lang": {
"bg": "Болгар тілі",
"de": "Неміс тілі",
"el": "Грек тілі",
"en": "Ағылшын тілі",
"es": "Испан тілі",
"et": "Эстон тілі",
"fr": "Француз тілі",
"he": "Иврит",
"it": "Итальян тілі",
"nl": "Голланд тілі",
"da": "Дат тілі",
"fi": "Фин тілі",
"no": "Норвег тілі",
"sv": "Швед тілі",
"ar": "Араб тілі",
"fa": "Парсы тілі",
"sw": "Суаһили тілі",
"ha": "Хауса тілі",
"am": "Амхар тілі",
"yo": "Йоруба тілі",
"zu": "Зулу тілі",
"ma": "Дария",
"ig": "Игбо тілі",
"ka": "Грузин тілі",
"kk": "Қазақ тілі",
"om": "Оромо тілі",
"so": "Сомали тілі",
"ti": "Тигринья тілі",
"uz": "Өзбек тілі",
"wo": "Волоф тілі",
"pl": "Поляк тілі",
"pt": "Португал тілі",
"ro": "Румын тілі",
"ru": "Орыс тілі",
"sr": "Серб тілі",
"tr": "Түрік тілі",
"uk": "Украин тілі"
},
"footer": {
"copyright": "© {{year}} helexa.ai"
}
}

View File

@@ -1,103 +0,0 @@
{
"hero": {
"badge": "Жаңа интеллект пішіні",
"title": "Интеллекттің жаңа пішіні",
"lead": "Helexa — тәуелсіз операторлар басқаратын, өздігінен ұйымдасатын AIтор. Ашық. Бөлінген. Үнемі дамуда.",
"ctaJoinMesh": "Торға қосылу",
"ctaFollowProject": "Жобаны қадағалау",
"subcopy": "AI ашық, орнықты және ортақ болуы керек деп санайтын операторлар, жасаушылар және қауымдастықтар үшін жасалған.",
"imageAlt": "Helexa геликсінің визуализациясы"
},
"intent": {
"title": "Helexa не үшін бар",
"p1": "AI Жер бетіндегі ең қуатты инфрақұрылымға айналып келеді. Бірақ бүгінде бұл қуат жеке мүдделерге, географиялық шектеулерге және әлсіз экономикаға тәуелді бірнеше корпорацияның қолында шоғырланған.",
"p2Intro": "Helexa басқаша болашақты елестетеді:",
"bullet1": "Бір ғана жерден емес, бүкіл әлемнен өсіп шығатын интеллект.",
"bullet2": "Кез келген адам үлес қосып, пайда көре алатын желі.",
"bullet3": "Бұйрыққа емес, сұранысқа қарай бейімделетін жүйе.",
"bullet4": "Қауымдарды алмастырмай, оларды күшейтетін технология.",
"closing": "Helexa — платформа да емес, бұлт та емес.\nБұл — тәуелсіз операторлардың тірі, үнемі дамып отыратын торы, ол жаңа типтегі интеллект қалыптастырады."
},
"whyNow": {
"title": "AI үшін бетбұрыс сәт",
"problemTitle": "Мәселе",
"problemBullet1": "AI тарихтағы кез келген технологияға қарағанда тезірек орталықтандырылуда.",
"problemBullet2": "Қуатты есептеу ресурстарына қол жеткізу мүмкіндігі қабілеттілікті анықтайды, ал бұл қолжетімділік барған сайын тарылуда.",
"problemBullet3": "Құн кедергілері зерттеушілерді, стартаптарды және жергілікті қауымдастықтарды тыс қалдырады.",
"problemBullet4": "Геосаяси және реттеуші қысымдар жаһандық қолжетімділікке қауіп төндіреді.",
"problemBullet5": "Модель жасаушылар мен темір‑жабдық операторлары көбіне өз еңбегімен құрылған құндылықтан үлес алмайды.",
"opportunityTitle": "Мүмкіндік",
"opportunityIntro": "Бірақ бөлінген әлем құруға болады.",
"opportunityBullet1": "Мыңдаған GPU әлем бойынша толық пайдаланылмай тұр.",
"opportunityBullet2": "Операторлар есептеу қуаты үшін әділ өтемақы алғысы келеді.",
"opportunityBullet3": "Жасаушыларға ашық, цензураға төзімді инфрақұрылым қажет.",
"opportunityBullet4": "Қауымдастықтар цифрлық жүйелерде егемендік пен орнықтылықты қалайды.",
"opportunityBullet5": "AIдың өсу қарқыны дәстүрлі бұлттарды басып озды — жаңа пішіндер қажет.",
"opportunityClosing": "Helexa — осы күштер түйісетін сәт."
},
"howItWorks": {
"title": "Тор қалай құрылады",
"operators": {
"eyebrow": "Операторлар түйіндер жүргізеді",
"title": "Кез келген адам есептеу қуатын қоса алады.",
"body": "Операторлар Helexa түйіндерін іске қосады. Қандай модельдерді орналастыру керегін өздері шешеді. Олар өз темір‑жабдығы мен экономикасын өз қолында сақтайды. Рұқсат күту де жоқ, бақылаушы қақпашылар да жоқ."
},
"routing": {
"eyebrow": "Тор интеллектті бағыттайды",
"title": "Сұраныс желі арқылы ағады.",
"body": "Helexa қай жерде қуат барын, қай жерде сұраныс өсіп жатқанын және қандай түйіндер сұрауларды ең жақсы орындай алатынын үйренеді. Тор өсіп келе жатқан геликс сияқты табиғи түрде бейімделеді."
},
"value": {
"eyebrow": "Құндылық кері ағады",
"title": "Жұмыс дәлелденеді. Төлем әділ бөлінеді.",
"body": "Әр жұмыс криптографиялық түбіртекпен расталады. Операторлар өздері көмектескен интеллект үшін табыс табады. Платформа салығы жоқ. Түсініксіз шоттар жоқ."
}
},
"principles": {
"title": "Платформаларға емес, қағидаттарға негізделген",
"distributed": {
"title": "Бастапқыдан‑ақ бөлінген",
"body": "Жалғыз істен шығу нүктесі жоқ. Орталық билік жоқ. Әрбір жаңа оператор қосылған сайын күшейе түсетін желі."
},
"participation": {
"title": "Ашық қатысу",
"body": "Егер сізде есептеу қуаты болса, үлес қоса аласыз. Тор шет құрылғыларын, үй серверлерін де, деректер орталықтарын да қабылдайды."
},
"fairness": {
"title": "Әділдік пен ашықтық",
"body": "Табыс нақты жұмысқа, криптографиялық растауға сүйенеді. Қара жәшіктер жоқ. Жасырын төлемдер жоқ."
},
"evolving": {
"title": "Дамып отыратын интеллект",
"body": "Тор сұраныстан сабақ алады. Модельдер қажет жеріне жүктеледі. Интеллект ынтымақтастық арқылы таралады."
}
},
"roadAhead": {
"title": "Helexa нені мақсат етеді",
"p1": "Баршаға тиесілі, түйіндер мен қауымдастықтардың геликсі қуаттандыратын жаһандық интеллект қабаты.",
"p2": "Ақауларға, үзілістерге, саясатқа және монополияларға төтеп бере алатын желі.",
"p3": "Операторлар, жасаушылар және пайдаланушылар бәрі де пайда көретін жаңа экономикалық модель.",
"p4": "Инновация орталықтан емес, шеттерден өсіп шығатын экожүйе.",
"card": {
"eyebrow": "Көрініс панорамасы",
"title": "Ортақ интеллект торына қарай",
"body": "Helexa әлі ерте сатыда. Идеялар іске асырылғанынан үлкен — бұл әдейі таңдау. Желі кезең‑кезеңімен өседі, оның эволюциясын операторлар мен жасаушылар бірігіп қалыптастырады."
}
},
"joinMesh": {
"badge": "Ерте кезең",
"title": "Тор қалыптасып келеді.",
"titleHighlight": "Сіз соның бір бөлігі бола аласыз.",
"lead": "Сіз темір‑жабдықты жүргізсеңіз де, модельдер жасасаңыз да немесе жай ғана AI қалай басқарылады деп алаңдасаңыз да, торда сізге орын табылады.",
"ctaRunNode": "Түйін іске қосу (жақында)",
"ctaJoinAnnouncements": "Ерте жаңалықтарға жазылу",
"ctaExploreCode": "Кодпен танысу",
"footer": "Қоршалған бақтар жоқ. Жалғыз иесі жоқ. Интеллекттің басқа болашағын бірге құрайтын адамдар, темір‑жабдық және идеялар тоғыстырылған тор ғана."
}
}

View File

@@ -1,17 +0,0 @@
{
"title": "مساحة محادثة",
"badge": "محادثة",
"lead": "هذه هي واجهة المحادثة. قم بتوصيل منطق الحوار الخاص بك ومكوّنات واجهة المستخدم بهذه الصفحة.",
"transcriptPlaceholder": "سجل المحادثة سيظهر هنا. اعرض رسائل النموذج والمستخدم في حاوية قابلة للتمرير، ويمكنك تجميعها حسب أدوار الحوار إذا رغبت.",
"inputPlaceholder": "اكتب رسالة لبدء المحادثة…",
"send": "إرسال",
"clear": "مسح",
"newChat": "New chat",
"newProject": "New project",
"newProjectName": "New project",
"unsorted": "Unsorted",
"emptyState": "Start a conversation. Your history stays in this browser.",
"anonBanner": "You have reached the anonymous limit. Sign up for a free allocation.",
"signUp": "Sign up",
"stop": "Stop"
}

Some files were not shown because too many files have changed in this diff Show More