beta_59 prep: language files v8, the SDK docs page, and device-status corrections

The audit found three keys the Store work added or renamed since v7 -- Store,
the out-of-memory message and the Languages hint -- untranslated in all 13
languages. Filled, using each language's own word for the Use button so the
hint matches the control it points at, and bumped the files to v8. Every
language is back to full coverage at 856 keys.

New deploy/site/sdk.html: the wada.* reference. The beta ships a public API and
had no user-facing documentation for it, so nobody outside the repo could write
an app. Covers the lifecycle, all seven tables, the app format, side-loading and
how to publish, plus the two constraints that surprise people -- the mesh is
read-only in v1 and http_get is plain HTTP because TLS does not fit in the heap
left after Wi-Fi associates.

Site device list: the Attaky Core and the T-Display P4 are fully supported now
(Kaj's call), so their not-hardware-verified caveats are gone. Dropped the "one
of the two primary development boards" wording, which stopped being true once
the P4 joined the test loop. The terms section named only the T-Deck and Heltec
V4 as flashable when the flasher offers ten boards; it now points at the list on
the page instead of going stale again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kaj Schittecat
2026-08-05 00:37:22 +02:00
co-authored by Claude Opus 4.8
parent 9b8daea0df
commit dbff43dbd7
18 changed files with 8562 additions and 8054 deletions
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+621 -618
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -1,15 +1,15 @@
{"langs":[
{"code":"bg","name":"Български","ver":"7"},
{"code":"de","name":"Deutsch","ver":"7"},
{"code":"el","name":"Ελληνικά","ver":"7"},
{"code":"es","name":"Español","ver":"7"},
{"code":"fr","name":"Français","ver":"7"},
{"code":"hu","name":"Magyar","ver":"7"},
{"code":"it","name":"Italiano","ver":"7"},
{"code":"nl","name":"Nederlands","ver":"7"},
{"code":"pt-br","name":"Português (BR)","ver":"7"},
{"code":"ro","name":"Română","ver":"7"},
{"code":"ru","name":"Русский","ver":"7"},
{"code":"sr","name":"Српски","ver":"7"},
{"code":"uk","name":"Українська","ver":"7"}
{"code":"bg","name":"Български","ver": "8"},
{"code":"de","name":"Deutsch","ver": "8"},
{"code":"el","name":"Ελληνικά","ver": "8"},
{"code":"es","name":"Español","ver": "8"},
{"code":"fr","name":"Français","ver": "8"},
{"code":"hu","name":"Magyar","ver": "8"},
{"code":"it","name":"Italiano","ver": "8"},
{"code":"nl","name":"Nederlands","ver": "8"},
{"code":"pt-br","name":"Português (BR)","ver": "8"},
{"code":"ro","name":"Română","ver": "8"},
{"code":"ru","name":"Русский","ver": "8"},
{"code":"sr","name":"Српски","ver": "8"},
{"code":"uk","name":"Українська","ver": "8"}
]}
+10 -7
View File
@@ -285,6 +285,9 @@
<a class="fab" id="docsBtn" href="docs.html" aria-label="Documentation" title="Documentation">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v5h5"/><path d="M16 13H8"/><path d="M16 17H8"/><path d="M10 9H8"/></svg>
</a>
<a class="fab" id="sdkBtn" href="sdk.html" aria-label="Lua app SDK" title="Lua app SDK">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</a>
<a class="fab" href="https://t413.com/p/projects/MapTileDl/" target="_blank" rel="noopener" aria-label="Map tile downloader" title="Map tile downloader">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18l-6 3V6l6-3 6 3 6-3v15l-6 3-6-3z"/><path d="M9 3v15"/><path d="M15 6v15"/></svg>
</a>
@@ -401,11 +404,11 @@
<summary><span class="mfr-name">LilyGo</span><span class="mfr-count"></span><svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg></summary>
<div class="boards mfr-boards">
<div class="board" data-search="lilygo t-deck tdeck plus esp32-s3 keyboard handheld"><h4>LilyGo T-Deck / Plus</h4><p>ESP32-S3 keyboard handheld.</p>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — this is one of the two primary development boards, tested on every release.</span></span>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — tested on every release.</span></span>
<esp-web-install-button id="inst-tdeck" manifest="https://firmware.wadamesh.com/latest/manifest-tdeck.json"></esp-web-install-button>
<a class="dl2" data-dl-board="tdeck" download href="https://firmware.wadamesh.com/latest/wadamesh-tdeck-merged.bin"><span class="dl2-row"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 11l5 5 5-5M5 21h14"/></svg>Download .bin</span><span class="dlver" data-dl-ver></span></a></div>
<div class="board" data-search="lilygo t-display p4 tdisplay amoled rm69a10 esp32-p4 touch handheld"><h4>LilyGo T-Display P4 <span style="font-weight:600;color:var(--mut)">(AMOLED)</span></h4><p>ESP32-P4 &middot; AMOLED touch handheld.</p>
<span class="sup part" tabindex="0"><span class="dot"></span>Partially supported<span class="pop"><b>Partially supported</b>Early port — it's known that not everything works yet, but the core does: LoRa mesh communication, chat, map, GPS and the phone-app link over Wi-Fi or USB. Bluetooth pairing isn't available on this board (a hardware limitation of its stock radio firmware).</span></span>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — tested on every release. One exception: Bluetooth pairing isn't available on this board, a hardware limitation of its stock radio firmware. Use Wi-Fi or USB for the phone-app link.</span></span>
<esp-web-install-button id="inst-tdp4" manifest="https://firmware.wadamesh.com/latest-beta/manifest-tdisplay-p4.json"></esp-web-install-button>
<a class="dl2" data-dl-board="tdisplay-p4" data-dl-betaonly download href="https://firmware.wadamesh.com/latest-beta/wadamesh-tdisplay-p4-merged.bin"><span class="dl2-row"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 11l5 5 5-5M5 21h14"/></svg>Download .bin</span><span class="dlver" data-dl-ver></span></a>
<p data-wm-betaonly style="color:var(--mut);font-size:.78rem;margin:6px 0 0">New board: beta channel only until the next stable.</p></div>
@@ -430,7 +433,7 @@
<summary><span class="mfr-name">Heltec</span><span class="mfr-count"></span><svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg></summary>
<div class="boards mfr-boards">
<div class="board" data-search="heltec v4 tft esp32-s3 chsc6x touch"><h4>Heltec V4 + TFT</h4><p>ESP32-S3 with CHSC6x touch.</p>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — this is one of the two primary development boards, tested on every release.</span></span>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — tested on every release.</span></span>
<esp-web-install-button id="inst-heltec" manifest="https://firmware.wadamesh.com/latest/manifest-heltec-v4-tft.json"></esp-web-install-button>
<a class="dl2" data-dl-board="heltec-v4-tft" download href="https://firmware.wadamesh.com/latest/wadamesh-heltec-v4-tft-merged.bin"><span class="dl2-row"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 11l5 5 5-5M5 21h14"/></svg>Download .bin</span><span class="dlver" data-dl-ver></span></a></div>
<div class="board" data-search="heltec v4-r8 v4 r8 esp32-s3 psram sd card experimental"><h4>Heltec V4-R8 <span style="font-weight:600;color:var(--mut)">(experimental)</span></h4><p>ESP32-S3 &middot; 8&nbsp;MB PSRAM &middot; SD card.</p>
@@ -476,11 +479,11 @@
<details class="mfr" data-mfr="Attaky">
<summary><span class="mfr-name">Attaky</span><span class="mfr-count"></span><svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg></summary>
<div class="boards mfr-boards">
<div class="board" data-search="attaky core esp32-s3 psram experimental"><h4>Attaky Core <span style="font-weight:600;color:var(--mut)">(experimental)</span></h4><p>ESP32-S3 &middot; 16&nbsp;MB &middot; 8&nbsp;MB PSRAM.</p>
<span class="sup part" tabindex="0"><span class="dot"></span>Partially supported<span class="pop"><b>Partially supported</b>The core works: LoRa mesh communication, chat, contacts, channels and the phone-app link. This community-contributed board (PR #158) isn't hardware-verified by us yet — flash with care and report back.</span></span>
<div class="board" data-search="attaky core esp32-s3 psram experimental"><h4>Attaky Core</h4><p>ESP32-S3 &middot; 16&nbsp;MB &middot; 8&nbsp;MB PSRAM.</p>
<span class="sup full" tabindex="0"><span class="dot"></span>Fully supported<span class="pop"><b>Fully supported</b>Everything wadamesh offers works here — tested on every release. Contributed by the community (PR #158).</span></span>
<esp-web-install-button id="inst-attaky" manifest="https://firmware.wadamesh.com/latest-beta/manifest-attaky.json"></esp-web-install-button>
<a class="dl2" data-dl-board="attaky" data-dl-betaonly download href="https://firmware.wadamesh.com/latest-beta/wadamesh-attaky-merged.bin"><span class="dl2-row"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 11l5 5 5-5M5 21h14"/></svg>Download .bin</span><span class="dlver" data-dl-ver></span></a>
<p data-wm-betaonly style="color:var(--mut);font-size:.78rem;margin:6px 0 0">New board, not yet hardware-tested &mdash; beta only. Please flash with care and report back.</p></div>
<p data-wm-betaonly style="color:var(--mut);font-size:.78rem;margin:6px 0 0">Beta channel only until the next stable.</p></div>
</div>
</details>
</div>
@@ -609,7 +612,7 @@ We use no tracking, analytics, or advertising cookies. Our CDN and security prov
1. Company — ALLFATHER BV — KBO/Company No. BE 0791.469.718, Bijvoordestraat 3, 3370 Boutersem, Belgium. Contact: hey@allfather.io.
2. Use — The Web Flasher flashes wadamesh firmware onto compatible LilyGo T-Deck and Heltec V4 devices via Web Serial. You must have the right to operate your device. wadamesh is open-source firmware licensed under GPL-3.0; the firmware itself is provided without warranty (see its LICENSE).
2. Use — The Web Flasher flashes wadamesh firmware onto the supported devices listed on this page via Web Serial. You must have the right to operate your device. wadamesh is open-source firmware licensed under GPL-3.0; the firmware itself is provided without warranty (see its LICENSE).
3. Disclaimer & Liability — The Tools are provided "as is" and "as available" without warranties of any kind. Flashing firmware may overwrite device data and settings. Use at your own risk. To the fullest extent permitted by law, we exclude implied warranties and limit liability to direct losses only. Nothing limits liability for intentional misconduct, gross negligence, death or personal injury caused by negligence, or other liability that cannot be excluded under applicable law.
+403
View File
@@ -0,0 +1,403 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WADAMESH — Lua app SDK</title>
<meta name="description" content="Write your own apps for WADAMESH in Lua. The wada.* API reference: ui, input, mesh, net, store, sys and timer, plus the app format and how to publish to the Store.">
<link rel="icon" href="wadamesh-badge.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@600;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#ffffff; --bg2:#f4f6f2; --ink:#15181e; --mut:#5d6770;
--line:#e5e8e2; --teal:#15b6a6; --teal-ink:#0e8d80;
color-scheme:light;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{font:16px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Inter,sans-serif; background:var(--bg); color:var(--ink); -webkit-font-smoothing:antialiased}
a{color:var(--teal-ink); text-decoration:none}
.top{position:sticky; top:0; z-index:30; display:flex; align-items:center; gap:14px; padding:12px 22px;
background:rgba(255,255,255,.88); backdrop-filter:saturate(1.2) blur(8px); border-bottom:1px solid var(--line)}
.top .word{font-weight:700; letter-spacing:2px; font-size:1.05rem; color:var(--ink)}
.top .word .t{color:var(--teal)}
.top .tag{font-family:'JetBrains Mono',monospace; font-size:.66rem; letter-spacing:.18em; text-transform:uppercase; color:var(--mut); border:1px solid var(--line); border-radius:100px; padding:4px 9px}
.top .sp{flex:1}
.top .home{display:inline-flex; align-items:center; gap:7px; color:var(--mut); font-size:.86rem; border:1px solid var(--line); border-radius:100px; padding:6px 13px; background:#fff; transition:background .15s,color .15s,border-color .15s}
.top .home:hover{color:var(--teal-ink); background:var(--bg2); border-color:#bfe6e0}
.top .home svg{width:16px;height:16px}
.shell{max-width:1200px; margin:0 auto; display:grid; grid-template-columns:220px 1fr; gap:34px; padding:0 22px}
nav.toc{position:sticky; top:64px; align-self:start; height:calc(100vh - 64px); overflow:auto; padding:26px 0 40px}
nav.toc .grp{font-family:'JetBrains Mono',monospace; font-size:.68rem; letter-spacing:.13em; text-transform:uppercase; color:var(--mut); margin:20px 0 7px}
nav.toc .grp:first-child{margin-top:0}
nav.toc a{display:block; padding:6px 12px; border-radius:9px; color:#46525c; font-size:.92rem; border-left:2px solid transparent}
nav.toc a:hover{background:var(--bg2); color:var(--ink)}
nav.toc a.on{color:var(--teal-ink); background:rgba(21,182,166,.09); border-left-color:var(--teal); font-weight:600}
main{padding:30px 0 90px; min-width:0}
.hero h1{font-size:clamp(1.9rem,4.4vw,2.6rem); letter-spacing:-.5px; font-weight:750}
.hero p{color:var(--mut); max-width:64ch; margin-top:10px; font-size:1.05rem}
.hero .meta{display:flex; gap:8px; flex-wrap:wrap; margin-top:16px}
.chip{display:inline-flex; align-items:center; gap:6px; font-size:.8rem; color:#3a444d; border:1px solid var(--line); border-radius:100px; padding:5px 11px; background:#fff}
.chip svg{width:14px;height:14px;color:var(--teal)}
section.doc{padding:36px 0; border-top:1px solid var(--line); scroll-margin-top:74px}
.doc h2{font-size:1.5rem; font-weight:730; letter-spacing:-.3px; display:flex; align-items:center; gap:10px}
.doc h2 .ico{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none; background:rgba(21,182,166,.12);color:var(--teal)}
.doc h2 .ico svg{width:18px;height:18px}
.row{display:grid; grid-template-columns:minmax(0,1fr) 372px; gap:34px; align-items:start; margin-top:16px}
@media(max-width:980px){.row{grid-template-columns:1fr}}
.prose{color:#3c454d; min-width:0}
.prose p{margin:0 0 12px}
.prose h3{font-size:1.02rem; margin:18px 0 6px; color:var(--ink)}
.prose ul{margin:4px 0 12px; padding-left:18px}
.prose li{margin:4px 0}
.prose b{color:var(--ink)}
.prose code{font-family:'JetBrains Mono',monospace; font-size:.85em; background:var(--bg2); border:1px solid var(--line); border-radius:6px; padding:1px 6px}
.shot{position:sticky; top:74px}
.cap{text-align:center; font-family:'JetBrains Mono',monospace; font-size:.68rem; color:var(--mut); margin-top:10px; letter-spacing:.04em}
/* T-Deck frame: the real screenshot + a generated QWERTY keyboard (added in JS) */
.device{width:344px; margin:0 auto; background:#16191d; border-radius:16px; padding:12px 12px 9px; border:1px solid #e9ece7; box-shadow:0 24px 60px -30px rgba(21,24,30,.5)}
.device img.screen{display:block; width:320px; height:240px; border-radius:8px}
.kbd{margin:9px auto 2px; display:flex; flex-direction:column; gap:4px; width:312px}
.kr{display:flex; gap:4px; justify-content:center}
.key{flex:1; max-width:28px; height:15px; background:#23282e; border-radius:4px; display:grid; place-items:center; font:8px/1 system-ui; color:#7d8893}
.key.wide{max-width:140px; flex:4}
.gallery{display:grid; grid-template-columns:repeat(auto-fill,minmax(232px,1fr)); gap:20px; margin-top:18px}
.mini{background:#16191d; border-radius:12px; padding:7px; border:1px solid #e9ece7}
.mini img{display:block; width:100%; height:auto; border-radius:6px}
.mini .cap{margin-top:6px}
.webgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(178px,1fr)); gap:18px; margin-top:22px}
figure.phone{margin:0; background:#16191d; border-radius:20px; padding:8px; border:1px solid #e9ece7; box-shadow:0 18px 44px -26px rgba(21,24,30,.5)}
figure.phone img{display:block; width:100%; height:auto; border-radius:13px}
figure.phone figcaption{text-align:center; font-family:'JetBrains Mono',monospace; font-size:.66rem; color:var(--mut); margin-top:8px; letter-spacing:.04em}
.subh{font-size:1.06rem; font-weight:700; color:var(--ink); margin:30px 0 2px}
/* Settings-page detail cards: real screenshot + what the page does + its controls */
.sgrp{font-family:'JetBrains Mono',monospace; font-size:.7rem; letter-spacing:.14em; text-transform:uppercase; color:var(--mut); margin:30px 0 0; padding-top:4px}
.scards{display:grid; grid-template-columns:repeat(auto-fill,minmax(290px,1fr)); gap:20px; margin-top:13px}
.scard{border:1px solid var(--line); border-radius:14px; background:#fff; padding:13px; display:flex; flex-direction:column}
.scard .thumb{background:#16191d; border-radius:9px; padding:6px}
.scard .thumb img{display:block; width:100%; height:auto; border-radius:5px}
.scard .thumb.ph{aspect-ratio:4/3; display:grid; place-items:center; border:1px dashed #39424b; padding:0}
.scard .thumb.ph span{font-family:'JetBrains Mono',monospace; font-size:.6rem; letter-spacing:.12em; color:#7d8893; text-transform:uppercase}
.scard h3{font-size:1.04rem; margin:12px 0 2px; color:var(--ink); display:flex; align-items:center; gap:8px; flex-wrap:wrap}
.scard h3 .tag{font-family:'JetBrains Mono',monospace; font-size:.58rem; letter-spacing:.05em; text-transform:uppercase; color:var(--teal-ink); background:rgba(21,182,166,.1); border-radius:5px; padding:2px 6px; font-weight:600}
.scard .lede{color:var(--mut); font-size:.9rem; margin:2px 0 9px}
.scard ul{margin:0; padding-left:17px}
.scard li{margin:4px 0; font-size:.89rem; color:#3c454d; line-height:1.5}
.scard li b{color:var(--ink); font-weight:600}
/* Caution callout (amber) */
.callout{display:flex; gap:11px; margin-top:18px; padding:13px 15px; border-radius:11px; background:#fff7ed; border:1px solid #f6dcb8}
.callout .cico{flex:none; width:20px; height:20px; color:#c2740b; margin-top:1px}
.callout .cico svg{width:20px; height:20px}
.callout b{display:block; color:#7c4a03; margin-bottom:3px; font-size:.94rem}
.callout p{margin:0; color:#7a5320; font-size:.9rem; line-height:1.55}
footer{border-top:1px solid var(--line); padding:26px 22px; text-align:center; color:var(--mut); font-size:.85rem}
@media(max-width:980px){
.shell{grid-template-columns:1fr; gap:0}
nav.toc{position:static; height:auto; border-bottom:1px solid var(--line); padding:14px 0; display:flex; gap:6px; overflow:auto; white-space:nowrap}
nav.toc .grp{display:none} nav.toc a{border-left:0; flex:none}
.shot{position:static; margin:0 auto 6px}
}
.prose pre{background:var(--bg2);border:1px solid var(--line);border-radius:10px;padding:14px 16px;overflow-x:auto;margin:14px 0;font:13px/1.6 "JetBrains Mono",ui-monospace,Menlo,Consolas,monospace}
.prose pre code{background:none;padding:0;font-size:inherit}
.api{width:100%;border-collapse:collapse;margin:12px 0 18px;font-size:.92rem;display:block;overflow-x:auto}
.api th,.api td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);vertical-align:top}
.api th{font-weight:700;white-space:nowrap}
.api td:first-child{white-space:nowrap;font-family:"JetBrains Mono",ui-monospace,Menlo,Consolas,monospace;font-size:.86rem;color:var(--teal-ink)}
.note{background:var(--bg2);border-left:3px solid var(--teal);border-radius:0 8px 8px 0;padding:12px 16px;margin:16px 0}
.note p{margin:0}
</style>
</head>
<body>
<div class="top">
<a class="word" href="index.html">WADA<span class="t">MESH</span></a>
<span class="tag">Lua SDK</span>
<span class="sp"></span>
<a class="home" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg> Back to site</a>
</div>
<div class="shell">
<nav class="toc" id="toc">
<div class="grp">Start here</div>
<a href="#intro">What this is</a>
<a href="#hello">Your first app</a>
<a href="#lifecycle">App lifecycle</a>
<div class="grp">Reference</div>
<a href="#ui">wada.ui</a>
<a href="#input">wada.input</a>
<a href="#mesh">wada.mesh</a>
<a href="#net">wada.net</a>
<a href="#store">wada.store</a>
<a href="#sys">wada.sys</a>
<a href="#timer">wada.timer</a>
<div class="grp">Shipping</div>
<a href="#format">App format</a>
<a href="#install">Install yours</a>
<a href="#publish">Publish to the Store</a>
<a href="#sandbox">Sandbox limits</a>
</nav>
<main>
<div class="hero">
<h1>Lua app SDK</h1>
<p>WADAMESH runs small Lua apps. They live on the device as a single <code>.lua</code> file, appear in the app drawer next to the built-ins, and can be installed and updated from the Store over the air.</p>
<div class="meta"><span class="chip">API v1</span> <span class="chip">Lua 5.4</span></div>
</div>
<section class="doc" id="intro">
<h2>What this is</h2>
<div class="row"><div class="prose">
<p>An app is one Lua chunk. The firmware gives it a screen, an input stream, read access to the mesh, a little key/value storage and an HTTP fetch. That is deliberately the whole surface — roughly thirty functions, all under the <code>wada.</code> table.</p>
<p>LVGL is <b>not</b> exposed. Binding a UI toolkit wholesale would freeze us out of ever changing it, and would hand every app enough rope to wedge the device. Instead there is a small set of widgets we own and can keep stable across firmware versions.</p>
<p>Two of the apps that ship in the Store — <b>RF Monitor</b> and <b>Airtime</b> — are written against exactly this API and nothing else. They are the reference implementations; read them if a doc paragraph is ambiguous.</p>
<div class="note"><p><b>API v1 is read-only where the mesh is concerned.</b> An app can see contacts, the packet log and radio statistics. It cannot transmit. That line is deliberate: a buggy or hostile app must not be able to put packets on the air in your name.</p></div>
</div></div>
</section>
<section class="doc" id="hello">
<h2>Your first app</h2>
<div class="row"><div class="prose">
<p>Save this as <code>hello.lua</code>:</p>
<pre><code>local n = 0
function on_start()
wada.ui.label("Hello from Lua", 16)
status = wada.ui.label("tick 0", 12)
status:color(wada.ui.colors.sub)
wada.timer.every(1000)
end
function on_tick()
n = n + 1
status:set("tick " .. n)
end
function on_input(ev)
if ev.type == "key" then
wada.sys.toast("you pressed " .. tostring(ev.name))
end
end</code></pre>
<p>Copy it to <code>/apps/hello.lua</code> on the device's SD card (or internal storage on boards without a card), open the app drawer, and it is there. No manifest is required for a file you side-load yourself — see <a href="#install">Install yours</a>.</p>
</div></div>
</section>
<section class="doc" id="lifecycle">
<h2>App lifecycle</h2>
<div class="row"><div class="prose">
<p>Define the callbacks you need as globals. All four are optional.</p>
<table class="api">
<tr><th>Callback</th><th>When it runs</th></tr>
<tr><td>on_start()</td><td>Once, when the app opens. Build your UI here.</td></tr>
<tr><td>on_tick()</td><td>On the cadence set by <code>wada.timer.every(ms)</code>. Never faster than 33&nbsp;ms.</td></tr>
<tr><td>on_input(ev)</td><td>For every touch, key or trackball event. See <a href="#input">wada.input</a>.</td></tr>
<tr><td>on_stop()</td><td>Once, when the app closes. Persist anything you care about here.</td></tr>
</table>
<p>Every callback runs inside a guarded call on the UI thread. If your code raises an error the app is closed with a toast rather than taking the firmware down with it — but it does mean a slow callback stalls the whole UI, so keep them short. There is an instruction budget: an app that spins forever is stopped.</p>
<p>Widgets you create in <code>on_start</code> are destroyed for you when the app closes. You do not free anything.</p>
</div></div>
</section>
<section class="doc" id="ui">
<h2>wada.ui</h2>
<div class="row"><div class="prose">
<p>Widgets are created in order, top to bottom, on the app's page. Every constructor returns a handle with methods you can call later.</p>
<table class="api">
<tr><th>Call</th><th>Returns / does</th></tr>
<tr><td>wada.ui.label(text, size)</td><td>A text line. <code>size</code> is a class, not a pixel height: 12, 14 or 16.</td></tr>
<tr><td>wada.ui.button(text, fn)</td><td>A tappable button; <code>fn</code> is called with no arguments.</td></tr>
<tr><td>wada.ui.canvas(w, h)</td><td>A drawing surface. See the canvas methods below.</td></tr>
<tr><td>wada.ui.chart(points)</td><td>A line chart sized to the page. The Airtime and RF Monitor primitive.</td></tr>
<tr><td>wada.ui.scroll(on)</td><td>Allow the page to scroll when content is taller than the screen.</td></tr>
<tr><td>wada.ui.text_h(size)</td><td>Line height in pixels for a size class — use it to lay out a canvas.</td></tr>
<tr><td>wada.ui.colors</td><td>Theme table: <code>text</code>, <code>sub</code>, <code>bg</code>, <code>accent</code>, <code>good</code>, <code>bad</code>.</td></tr>
</table>
<h3>Label handle</h3>
<table class="api">
<tr><td>lbl:set(text)</td><td>Replace the text.</td></tr>
<tr><td>lbl:color(c)</td><td>Set the colour, e.g. <code>wada.ui.colors.accent</code>.</td></tr>
<tr><td>lbl:pos(x, y)</td><td>Place it explicitly instead of in flow.</td></tr>
<tr><td>lbl:width(px)</td><td>Fix the width; longer text wraps instead of running off the panel.</td></tr>
</table>
<h3>Canvas handle</h3>
<table class="api">
<tr><td>cv:fill(c)</td><td>Flood the whole canvas.</td></tr>
<tr><td>cv:rect(x, y, w, h, c)</td><td>Filled rectangle.</td></tr>
<tr><td>cv:line(x1, y1, x2, y2, c)</td><td>Line.</td></tr>
<tr><td>cv:circle(x, y, r, c)</td><td>Filled circle.</td></tr>
<tr><td>cv:text(x, y, s, c, size)</td><td>Draw a string.</td></tr>
<tr><td>cv:pos(x, y)</td><td>Move the canvas itself.</td></tr>
</table>
<h3>Chart handle</h3>
<table class="api">
<tr><td>ch:push(v)</td><td>Append a point, scrolling the series.</td></tr>
<tr><td>ch:fill(t)</td><td>Replace every point from a table.</td></tr>
<tr><td>ch:range(min, max)</td><td>Fix the Y range instead of autoscaling.</td></tr>
<tr><td>ch:axis(ticks, gutter)</td><td>Draw Y-axis labels; <code>gutter</code> reserves space for them.</td></tr>
<tr><td>ch:pos(x, y)</td><td>Move the chart.</td></tr>
</table>
<div class="note"><p>Screens differ a lot — 240&nbsp;px portrait on a Heltec V4, 320&nbsp;px landscape on a T-Deck, and a tall high-DPI panel on a T-Display P4. Ask <code>wada.sys.board()</code> for the real width and height rather than hardcoding a layout, and prefer <code>wada.ui.text_h()</code> over assuming a font is so many pixels tall.</p></div>
</div></div>
</section>
<section class="doc" id="input">
<h2>wada.input</h2>
<div class="row"><div class="prose">
<p>Input arrives through <code>on_input(ev)</code>. The event is a table; <code>ev.type</code> tells you which kind it is.</p>
<table class="api">
<tr><th>ev.type</th><th>Fields</th></tr>
<tr><td>"touch"</td><td><code>ev.x</code>, <code>ev.y</code> in page coordinates.</td></tr>
<tr><td>"key"</td><td><code>ev.name</code> — the key label on boards with a keyboard.</td></tr>
<tr><td>"dir"</td><td><code>ev.dir</code> — one of <code>up</code>, <code>down</code>, <code>left</code>, <code>right</code>, <code>select</code>. Trackball, D-pad and swipes all arrive here.</td></tr>
</table>
<p>Handle <code>"dir"</code> if you want your app to work on every board. A touch-only board sends swipes as directions, and a keyboard board sends its navigation keys the same way, so one branch covers both.</p>
</div></div>
</section>
<section class="doc" id="mesh">
<h2>wada.mesh</h2>
<div class="row"><div class="prose">
<p>Read-only in API v1.</p>
<table class="api">
<tr><td>wada.mesh.contacts()</td><td>Array of <code>{name, type, ago_s, lat, lon}</code>.</td></tr>
<tr><td>wada.mesh.rx_log()</td><td>Recent packets: <code>{ago_ms, type, rssi, snr, hops}</code>. The RF Monitor feed.</td></tr>
<tr><td>wada.mesh.stats()</td><td><code>{rssi, noise, rx_air_s, tx_air_s, rx_pkts, rx_err, tx_budget_ms, rx_events, rx_dropped, tx_pkts, freq, bw, sf, duty_pct}</code>.</td></tr>
<tr><td>wada.mesh.self()</td><td>This node: <code>{name, lat, lon}</code>.</td></tr>
</table>
<p>These are snapshots taken when you call them, not live views — call again on each tick to refresh.</p>
</div></div>
</section>
<section class="doc" id="net">
<h2>wada.net</h2>
<div class="row"><div class="prose">
<table class="api">
<tr><td>wada.net.http_get(url, cb)</td><td>Fetch a URL. <code>cb(body)</code> runs when it lands, or with <code>nil</code> on failure.</td></tr>
</table>
<p>The fetch is asynchronous and runs on the firmware's existing network worker, so it does not block the UI. Your callback runs on the UI thread once the body is in memory.</p>
<div class="note"><p><b>Plain HTTP only, and that is not an oversight.</b> After Wi-Fi associates there is not enough free internal memory on the smaller boards for a TLS handshake — mbedTLS wants around 30&nbsp;KB and roughly 5&nbsp;KB is free. If you need an HTTPS source, put a small proxy in front of it, the way the map tiles do. Responses are capped (64&nbsp;KB by default).</p></div>
</div></div>
</section>
<section class="doc" id="store">
<h2>wada.store</h2>
<div class="row"><div class="prose">
<table class="api">
<tr><td>wada.store.get(key)</td><td>Read a string, or <code>nil</code>.</td></tr>
<tr><td>wada.store.set(key, value)</td><td>Write a string.</td></tr>
</table>
<p>Keys are namespaced per app, so two apps cannot collide. Keep it small — the budget is about 2&nbsp;KB per app, and the underlying store silently drops oversized values. High scores and settings, not logs.</p>
<p>Writes hit flash. Do them in <code>on_stop()</code> or on a real user action, never on every tick.</p>
</div></div>
</section>
<section class="doc" id="sys">
<h2>wada.sys</h2>
<div class="row"><div class="prose">
<table class="api">
<tr><td>wada.sys.millis()</td><td>Milliseconds since boot.</td></tr>
<tr><td>wada.sys.board()</td><td><code>{w, h, touch, keyboard, trackball, gps}</code> — size and capabilities.</td></tr>
<tr><td>wada.sys.toast(msg)</td><td>Brief on-screen message.</td></tr>
<tr><td>wada.sys.random(n)</td><td>Integer in 1..n.</td></tr>
</table>
</div></div>
</section>
<section class="doc" id="timer">
<h2>wada.timer</h2>
<div class="row"><div class="prose">
<table class="api">
<tr><td>wada.timer.every(ms)</td><td>Call <code>on_tick</code> every <code>ms</code>. Clamped to 33&nbsp;ms minimum.</td></tr>
<tr><td>wada.timer.stop()</td><td>Stop ticking.</td></tr>
</table>
<p>One timer per app. Calling <code>every</code> again changes the interval rather than adding a second timer.</p>
</div></div>
</section>
<section class="doc" id="format">
<h2>App format</h2>
<div class="row"><div class="prose">
<p>On the device an app is one or two files on the active storage root:</p>
<pre><code>/apps/&lt;id&gt;.lua the code
/apps/&lt;id&gt;.json the manifest (optional for side-loaded apps)</code></pre>
<p>The manifest is what the Store and the drawer read:</p>
<pre><code>{
"id": "airtime",
"name": "Airtime",
"version": "1.3",
"min_api": 1,
"icon": "A",
"description": "Duty cycle and airtime budget.",
"boards": ["*"]
}</code></pre>
<table class="api">
<tr><td>id</td><td>Lowercase, no spaces. Must match the filename.</td></tr>
<tr><td>min_api</td><td>Refuse to run on firmware older than this API version.</td></tr>
<tr><td>icon</td><td>A single character shown in the drawer tile and Store card.</td></tr>
<tr><td>boards</td><td><code>["*"]</code> for everything, or a list of board ids to restrict to.</td></tr>
</table>
</div></div>
</section>
<section class="doc" id="install">
<h2>Install your own</h2>
<div class="row"><div class="prose">
<p>Drop a bare <code>.lua</code> file into <code>/apps/</code> on the SD card and it shows up in the drawer — no manifest needed. The filename becomes the name. That is the fast loop while you are writing something.</p>
<p>Apps you side-load appear in the Store under <b>Your own apps</b>, where you can remove them again. A long press on the drawer tile also offers to remove.</p>
<p>On boards without an SD card the same path lives on internal storage.</p>
</div></div>
</section>
<section class="doc" id="publish">
<h2>Publish to the Store</h2>
<div class="row"><div class="prose">
<p>The Store is served as static files, so publishing is a pull request against the firmware repository:</p>
<ul>
<li>Add <code>deploy/apps/&lt;id&gt;/&lt;version&gt;/&lt;id&gt;.lua</code> and the matching <code>.json</code>.</li>
<li>Add an entry to <code>deploy/apps/apps.json</code>.</li>
</ul>
<p>Version paths are immutable — publishing 1.1 never rewrites 1.0. Devices compare the catalog version against what they have installed and offer <b>Update</b> when they differ, so bumping the version in both places is the whole release process.</p>
<p>Apps in the catalog are reviewed before they are merged. Keep them small and keep them readable.</p>
</div></div>
</section>
<section class="doc" id="sandbox">
<h2>Sandbox limits</h2>
<div class="row"><div class="prose">
<p>Apps run in a restricted environment. These are removed: <code>io</code>, <code>os</code>, <code>require</code>, <code>dofile</code>, and loading new chunks at runtime. Available: <code>math</code>, <code>string</code>, <code>table</code>, and the usual <code>pairs</code>, <code>ipairs</code>, <code>select</code>, <code>pcall</code>, <code>tostring</code>, <code>tonumber</code>.</p>
<p>Memory comes from a capped pool in PSRAM, so an app that allocates without bound fails its own allocation rather than starving the radio or the UI. There is an instruction budget on every callback for the same reason.</p>
<p>None of this makes a hostile app safe, which is why the catalog is curated. It makes an <i>honest</i> app that has a bug survivable: it gets closed, and the device keeps carrying traffic.</p>
</div></div>
</section>
</main>
</div>
<script>
// Highlight the section currently in view, same behaviour as the docs page.
(function () {
var links = [].slice.call(document.querySelectorAll('#toc a'));
var secs = links.map(function (a) { return document.querySelector(a.getAttribute('href')); });
function sync() {
var y = window.scrollY + 120, cur = 0;
for (var i = 0; i < secs.length; i++) if (secs[i] && secs[i].offsetTop <= y) cur = i;
links.forEach(function (a, i) { a.classList.toggle('on', i === cur); });
}
window.addEventListener('scroll', sync, { passive: true });
sync();
})();
</script>
</body>
</html>
+24
View File
@@ -0,0 +1,24 @@
# beta_59 - Lua apps and the Store: apps and languages are now downloadable
# One user-facing note per non-blank, non-# line; # lines are section comments.
# --- The headline ---------------------------------------------------------------------
WADAMESH now runs apps written in Lua. They install from a Store on the device, appear in the app drawer next to the built-ins, and update over the air without reflashing the firmware. The whole system ships in this one beta: the runtime, the API, the Store, and the first apps converted to it.
Store lives in the app drawer and has three tabs. Apps lists what you can install, update or remove. Built-in lets you hide the stock apps you never use, so the drawer holds what you actually reach for. Languages downloads translations.
RF Monitor and Airtime are now Lua apps rather than firmware pages, and they gained things the native versions never had along the way: RF Monitor scrolls through the full packet list, Airtime has proper Y-axis labels and a Reset button. Snake is a Lua app too. Removing one now really removes it, and reinstalling brings it back with your scores intact.
You can write your own. Drop a bare .lua file into /apps/ on the card and it shows up in the drawer, no manifest needed. The API reference is at wadamesh.com/sdk.html - about thirty functions covering the screen, input, read-only mesh data, an HTTP fetch and a little storage.
# --- Languages are files now -----------------------------------------------------------
Translations are no longer baked into the firmware. They download from the Store's Languages tab as versioned files, which means a translation can be fixed and shipped without waiting for a firmware release - the device offers Update when a newer one exists.
Every language got a full sweep. Around 150 strings that had quietly stayed English for months - the channel settings sheets, the message menus, most buttons - are translated now, across all 13 languages.
Settings > Language opens the Languages tab directly, and switching one tells you it is restarting before it does, instead of rebooting under your finger.
# --- Heltec V4 -------------------------------------------------------------------------
The V4 has 2 MB of PSRAM and very little free internal memory, which made the Store unreliable there. It now ships every language and every catalog app inside the firmware image, so the apps are in the drawer on first boot and all 13 languages are already installed. Its Store is a picker rather than a shop.
Around 17 KB of internal memory reclaimed on the V4, measured step by step: the store and Discover tables moved out of static memory, and the allocator now puts ordinary allocations in PSRAM instead of forcing everything under 4 KB into internal RAM.
# --- Fixes -----------------------------------------------------------------------------
"Save update bin to SD" works again. It failed with "SD write failed" at 1-7% on every T-Deck, and had been broken since the feature shipped in beta_36. The download buffer sat in PSRAM, which the SD card cannot reliably read from directly, so the write died partway through at a point that moved around with the network. Nothing to do with card size or formatting. Reported by Marshal W7TER, df2080 and Dennis.
The same defect is fixed in two other places it had not been noticed yet: copying any file to the SD card from the Files app, and exporting a crash dump.
The Store fits narrow and high-DPI screens properly. Cards measure their own text instead of assuming two lines, so nothing is cut off and a long title no longer lands on top of the description - this also fixes every board for languages whose text runs longer than the English.
Installing an app or a language shows a spinner on the button for the whole download instead of a brief message, so a slow connection no longer looks like the tap did nothing.
Opening the app drawer and the Store is quicker - the card was being re-scanned on the UI thread every single time the drawer opened.
+39
View File
@@ -522,6 +522,7 @@ static const I18nPair kBuiltin_hu[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normál (100%)\nLarge (150%)\nHuge (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Nincs elég szabad memória a Bluetooth-hoz. Először kapcsold ki a Wi-Fi-t." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Nincs elég szabad memória a Wi-Fi-hez. Először kapcsold ki a Bluetooth-t." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Nincs elég szabad memória a letöltéshez. Indítsd újra és próbáld meg újra." },
{ "Nothing selected", "Semmi sincs kiválasztva" },
{ "Notification sound set", "Értesítési hang beállítása" },
{ "Notify on new contact", "Értesítés új kapcsolatfelvételről" },
@@ -763,6 +764,7 @@ static const I18nPair kBuiltin_hu[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Még mindig elemzem az előző\nútvonalat… próbáld újra egy pillanat múlva." },
{ "Stop", "Leállít" },
{ "Storage", "Storage" },
{ "Store", "Áruház" },
{ "Store data on SD (reboot)", "Adatok tárolása SD-n (újraindítás)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Élő képként közvetíti az eszköz képernyőjét, amelyre koppintva vezérelhetsz. Nehezebb; a panelen helykitöltő látszik. A váltás újraindítja az eszközt." },
{ "Switch", "Kapcsoló" },
@@ -776,6 +778,7 @@ static const I18nPair kBuiltin_hu[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Tab gyorsbillentyűk – koppintson egy sorra, majd nyomjon meg egy billentyűt" },
{ "Tap Use to switch - the device reboots to apply.", "Koppints a Használ gombra a váltáshoz – az eszköz újraindul." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Váltáshoz koppints a Használ gombra - az eszköz újraindul. A nyelvi fájlok a tároló /lang mappájában vannak; szerkesztheted őket vagy hozzáadhatod a sajátodat." },
{ "Tap a language; the device reboots to apply.", "Érintsen meg egy nyelvet; az eszköz újraindul." },
{ "Taste the rainbow!", "Ízleld meg a szivárványt!" },
@@ -1439,6 +1442,7 @@ static const I18nPair kBuiltin_nl[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normaal (100%)\nGroot (150%)\nEnorm (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Niet genoeg vrij geheugen voor Bluetooth. Schakel eerst Wi-Fi uit." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Niet genoeg vrij geheugen voor Wi-Fi. Schakel eerst Bluetooth uit." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Onvoldoende vrij geheugen om het op te halen. Herstart en probeer opnieuw." },
{ "Nothing selected", "Niets geselecteerd" },
{ "Notification sound set", "Meldingsgeluid ingesteld" },
{ "Notify on new contact", "Melden bij nieuw contact" },
@@ -1680,6 +1684,7 @@ static const I18nPair kBuiltin_nl[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Vorige pad wordt nog\ngeanalyseerd… probeer zo opnieuw." },
{ "Stop", "Stop" },
{ "Storage", "Opslag" },
{ "Store", "Store" },
{ "Store data on SD (reboot)", "Gegevens op SD opslaan (herstart)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Streamt het scherm als live beeld dat je aantikt om te bedienen. Zwaarder; het paneel toont een plaatshouder. Omschakelen herstart het apparaat." },
{ "Switch", "Wisselen" },
@@ -1693,6 +1698,7 @@ static const I18nPair kBuiltin_nl[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Tab-sneltoetsen — tik een rij, druk dan een toets" },
{ "Tap Use to switch - the device reboots to apply.", "Tik op Gebruik om te wisselen – het apparaat herstart om dit toe te passen." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Tik Gebruik om te wisselen - het apparaat herstart. Taalbestanden staan in /lang op de opslag; bewerk ze of voeg je eigen toe." },
{ "Tap a language; the device reboots to apply.", "Tik op een taal; het apparaat herstart." },
{ "Taste the rainbow!", "Proef de regenboog!" },
@@ -2356,6 +2362,7 @@ static const I18nPair kBuiltin_de[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normal (100%)\nGroß (150%)\nRiesig (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Nicht genug freier Speicher für Bluetooth. Schalte zuerst Wi-Fi aus." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Nicht genug freier Speicher für Wi-Fi. Schalte zuerst Bluetooth aus." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Nicht genug freier Speicher zum Abrufen. Neu starten und erneut versuchen." },
{ "Nothing selected", "Nichts ausgewählt" },
{ "Notification sound set", "Benachrichtigungston festgelegt" },
{ "Notify on new contact", "Bei neuem Kontakt benachrichtigen" },
@@ -2597,6 +2604,7 @@ static const I18nPair kBuiltin_de[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Vorheriger Pfad wird noch\nanalysiert… gleich erneut versuchen." },
{ "Stop", "Stopp" },
{ "Storage", "Speicher" },
{ "Store", "Store" },
{ "Store data on SD (reboot)", "Daten auf SD speichern (Neustart)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Überträgt den Bildschirm als Livebild, das per Tippen gesteuert wird. Schwerer; das Panel zeigt einen Platzhalter. Umschalten startet das Gerät neu." },
{ "Switch", "Wechseln" },
@@ -2610,6 +2618,7 @@ static const I18nPair kBuiltin_de[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Tab-Tasten — Zeile tippen, dann Taste drücken" },
{ "Tap Use to switch - the device reboots to apply.", "Zum Wechseln auf Nutzen tippen – das Gerät startet neu." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Zum Wechseln auf Nutzen tippen - das Gerät startet neu. Sprachdateien liegen in /lang auf dem Speicher; bearbeiten oder eigene hinzufügen." },
{ "Tap a language; the device reboots to apply.", "Sprache antippen; Gerät startet neu." },
{ "Taste the rainbow!", "Schmecke den Regenbogen!" },
@@ -3273,6 +3282,7 @@ static const I18nPair kBuiltin_fr[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normal (100%)\nGrand (150%)\nÉnorme (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Mémoire insuffisante pour le Bluetooth. Désactivez d'abord le Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Mémoire insuffisante pour le Wi-Fi. Désactivez d'abord le Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Mémoire libre insuffisante pour le récupérer. Redémarrez et réessayez." },
{ "Nothing selected", "Rien de sélectionné" },
{ "Notification sound set", "Son de notification défini" },
{ "Notify on new contact", "Notifier pour un nouveau contact" },
@@ -3514,6 +3524,7 @@ static const I18nPair kBuiltin_fr[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Analyse du chemin précédent\nen cours… réessayez bientôt." },
{ "Stop", "Arrêter" },
{ "Storage", "Stockage" },
{ "Store", "Boutique" },
{ "Store data on SD (reboot)", "Stocker les données sur SD (redémarrage)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Diffuse l'écran en image en direct que vous touchez pour contrôler. Plus lourd ; le panneau affiche un substitut. Basculer redémarre l'appareil." },
{ "Switch", "Basculer" },
@@ -3527,6 +3538,7 @@ static const I18nPair kBuiltin_fr[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Raccourcis onglet — touchez une ligne, puis une touche" },
{ "Tap Use to switch - the device reboots to apply.", "Touchez Utiliser pour changer – l'appareil redémarre pour appliquer." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Touchez Utiliser pour changer - l'appareil redémarre. Les fichiers de langue sont dans /lang sur le stockage ; modifiez-les ou ajoutez les vôtres." },
{ "Tap a language; the device reboots to apply.", "Touchez une langue ; l'appareil redémarre." },
{ "Taste the rainbow!", "Goûtez l'arc-en-ciel !" },
@@ -4190,6 +4202,7 @@ static const I18nPair kBuiltin_es[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normal (100%)\nGrande (150%)\nEnorme (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "No hay memoria suficiente para Bluetooth. Desactiva primero el Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "No hay memoria suficiente para Wi-Fi. Desactiva primero el Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "No hay suficiente memoria libre para descargarlo. Reinicia e inténtalo de nuevo." },
{ "Nothing selected", "Nada seleccionado" },
{ "Notification sound set", "Sonido de notificación establecido" },
{ "Notify on new contact", "Avisar con nuevo contacto" },
@@ -4431,6 +4444,7 @@ static const I18nPair kBuiltin_es[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Aún analizando la ruta\nanterior… reinténtalo en un momento." },
{ "Stop", "Detener" },
{ "Storage", "Almacenamiento" },
{ "Store", "Tienda" },
{ "Store data on SD (reboot)", "Guardar datos en SD (reinicio)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Transmite la pantalla como imagen en vivo que tocas para controlar. Más pesado; el panel muestra un marcador. Cambiarlo reinicia el dispositivo." },
{ "Switch", "Cambiar" },
@@ -4444,6 +4458,7 @@ static const I18nPair kBuiltin_es[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Atajos de pestaña — toca una fila, luego una tecla" },
{ "Tap Use to switch - the device reboots to apply.", "Toca Usar para cambiar: el dispositivo se reinicia para aplicarlo." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Toca Usar para cambiar - el dispositivo se reinicia. Los archivos de idioma están en /lang del almacenamiento; edítalos o añade el tuyo." },
{ "Tap a language; the device reboots to apply.", "Toca un idioma; el dispositivo se reinicia." },
{ "Taste the rainbow!", "¡Saborea el arcoíris!" },
@@ -5107,6 +5122,7 @@ static const I18nPair kBuiltin_it[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normale (100%)\nGrande (150%)\nEnorme (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Memoria insufficiente per il Bluetooth. Disattiva prima il Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Memoria insufficiente per il Wi-Fi. Disattiva prima il Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Memoria libera insufficiente per scaricarlo. Riavvia e riprova." },
{ "Nothing selected", "Niente selezionato" },
{ "Notification sound set", "Suono di notifica impostato" },
{ "Notify on new contact", "Avvisa a nuovo contatto" },
@@ -5348,6 +5364,7 @@ static const I18nPair kBuiltin_it[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Analisi del percorso precedente\nin corso… riprova tra poco." },
{ "Stop", "Ferma" },
{ "Storage", "Archiviazione" },
{ "Store", "Store" },
{ "Store data on SD (reboot)", "Salva dati su SD (riavvio)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Trasmette lo schermo come immagine dal vivo da toccare per controllare. Più pesante; il pannello mostra un segnaposto. Il cambio riavvia il dispositivo." },
{ "Switch", "Cambia" },
@@ -5361,6 +5378,7 @@ static const I18nPair kBuiltin_it[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Tasti scheda — tocca una riga, poi premi un tasto" },
{ "Tap Use to switch - the device reboots to apply.", "Tocca Usa per cambiare – il dispositivo si riavvia per applicare." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Tocca Usa per cambiare - il dispositivo si riavvia. I file lingua sono in /lang sulla memoria; modificali o aggiungi i tuoi." },
{ "Tap a language; the device reboots to apply.", "Tocca una lingua; il dispositivo si riavvia." },
{ "Taste the rainbow!", "Assapora l'arcobaleno!" },
@@ -6024,6 +6042,7 @@ static const I18nPair kBuiltin_ru[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Обычный (100%)\nБольшой (150%)\nОгромный (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Недостаточно памяти для Bluetooth. Сначала выключите Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Недостаточно памяти для Wi-Fi. Сначала выключите Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Недостаточно свободной памяти для загрузки. Перезагрузите и повторите попытку." },
{ "Nothing selected", "Ничего не выбрано" },
{ "Notification sound set", "Звук уведомления задан" },
{ "Notify on new contact", "Уведомлять о новом контакте" },
@@ -6265,6 +6284,7 @@ static const I18nPair kBuiltin_ru[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Ещё анализируется предыдущий\nпуть… повторите чуть позже." },
{ "Stop", "Стоп" },
{ "Storage", "Хранилище" },
{ "Store", "Магазин" },
{ "Store data on SD (reboot)", "Хранить данные на SD (перезагрузка)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Транслирует экран как живую картинку, по которой вы нажимаете. Тяжелее; на панели заглушка. Переключение перезагружает устройство." },
{ "Switch", "Переключить" },
@@ -6278,6 +6298,7 @@ static const I18nPair kBuiltin_ru[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Горячие клавиши вкладки — коснитесь строки, затем нажмите клавишу" },
{ "Tap Use to switch - the device reboots to apply.", "Нажмите «Выбрать», чтобы переключиться — устройство перезагрузится." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Нажмите «Выбрать» для смены - устройство перезагрузится. Файлы языков лежат в /lang в хранилище; правьте их или добавляйте свои." },
{ "Tap a language; the device reboots to apply.", "Выберите язык; устройство перезагрузится." },
{ "Taste the rainbow!", "Попробуй радугу!" },
@@ -6941,6 +6962,7 @@ static const I18nPair kBuiltin_uk[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Звичайний (100%)\nВеликий (150%)\nВеличезний (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Недостатньо пам'яті для Bluetooth. Спершу вимкніть Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Недостатньо пам'яті для Wi-Fi. Спершу вимкніть Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Недостатньо вільної пам'яті для завантаження. Перезавантажте та спробуйте ще раз." },
{ "Nothing selected", "Нічого не вибрано" },
{ "Notification sound set", "Звук сповіщення встановлено" },
{ "Notify on new contact", "Сповіщати про новий контакт" },
@@ -7182,6 +7204,7 @@ static const I18nPair kBuiltin_uk[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Ще аналізується попередній\nшлях… спробуйте за мить." },
{ "Stop", "Стоп" },
{ "Storage", "Сховище" },
{ "Store", "Магазин" },
{ "Store data on SD (reboot)", "Зберігати дані на SD (перезавантаження)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Транслює екран як живу картинку, якою керуєте дотиком. Важче; на панелі заглушка. Перемикання перезавантажує пристрій." },
{ "Switch", "Перемкнути" },
@@ -7195,6 +7218,7 @@ static const I18nPair kBuiltin_uk[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Гарячі клавіші вкладки — торкніться рядка, потім натисніть клавішу" },
{ "Tap Use to switch - the device reboots to apply.", "Натисніть «Обрати», щоб перемкнути — пристрій перезавантажиться." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Натисніть «Обрати» для зміни - пристрій перезавантажиться. Файли мов лежать у /lang у сховищі; редагуйте їх або додавайте власні." },
{ "Tap a language; the device reboots to apply.", "Виберіть мову; пристрій перезавантажиться." },
{ "Taste the rainbow!", "Скуштуй веселку!" },
@@ -7858,6 +7882,7 @@ static const I18nPair kBuiltin_bg[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Нормален (100%)\nГолям (150%)\nОгромен (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Недостатъчно памет за Bluetooth. Първо изключете Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Недостатъчно памет за Wi-Fi. Първо изключете Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Недостатъчно свободна памет за изтегляне. Рестартирайте и опитайте отново." },
{ "Nothing selected", "Нищо не е избрано" },
{ "Notification sound set", "Звукът за известие е зададен" },
{ "Notify on new contact", "Известяване при нов контакт" },
@@ -8099,6 +8124,7 @@ static const I18nPair kBuiltin_bg[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Все още се анализира предишният\nпът… опитайте пак след малко." },
{ "Stop", "Спри" },
{ "Storage", "Хранилище" },
{ "Store", "Магазин" },
{ "Store data on SD (reboot)", "Съхранявай данните на SD (рестарт)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Излъчва екрана като живо изображение, което докосваш за управление. По-тежко; панелът показва заместител. Превключването рестартира устройството." },
{ "Switch", "Превключи" },
@@ -8112,6 +8138,7 @@ static const I18nPair kBuiltin_bg[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Клавиши за раздела — докосни ред, после натисни клавиш" },
{ "Tap Use to switch - the device reboots to apply.", "Натиснете „Избери“, за да превключите — устройството ще се рестартира." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Докосни «Избери» за смяна - устройството се рестартира. Езиковите файлове са в /lang в паметта; редактирай ги или добави свои." },
{ "Tap a language; the device reboots to apply.", "Изберете език; устройството ще се рестартира." },
{ "Taste the rainbow!", "Вкуси дъгата!" },
@@ -8775,6 +8802,7 @@ static const I18nPair kBuiltin_sr[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Нормалан (100%)\nВелик (150%)\nОгроман (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Нема довољно меморије за Bluetooth. Прво искључите Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Нема довољно меморије за Wi-Fi. Прво искључите Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Нема довољно слободне меморије за преузимање. Поново покрените и покушајте опет." },
{ "Nothing selected", "Ништа није изабрано" },
{ "Notification sound set", "Звук обавештења постављен" },
{ "Notify on new contact", "Обавести о новом контакту" },
@@ -9016,6 +9044,7 @@ static const I18nPair kBuiltin_sr[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Још се анализира претходна\nпутања… покушајте ускоро." },
{ "Stop", "Заустави" },
{ "Storage", "Складиште" },
{ "Store", "Продавница" },
{ "Store data on SD (reboot)", "Чувај податке на SD (рестарт)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Преноси екран као живу слику коју додиром контролишеш. Теже; панел приказује чувар места. Пребацивање рестартује уређај." },
{ "Switch", "Пребаци" },
@@ -9029,6 +9058,7 @@ static const I18nPair kBuiltin_sr[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Пречице картице — додирни ред, па притисни тастер" },
{ "Tap Use to switch - the device reboots to apply.", "Додирните „Користи“ да промените — уређај ће се поново покренути." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Додирни «Користи» за промену - уређај се поново покреће. Датотеке језика су у /lang на меморији; уреди их или додај своје." },
{ "Tap a language; the device reboots to apply.", "Изаберите језик; уређај ће се рестартовати." },
{ "Taste the rainbow!", "Окуси дугу!" },
@@ -9692,6 +9722,7 @@ static const I18nPair kBuiltin_el[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Κανονικό (100%)\nΜεγάλο (150%)\nΤεράστιο (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Ανεπαρκής μνήμη για Bluetooth. Απενεργοποιήστε πρώτα το Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Ανεπαρκής μνήμη για Wi-Fi. Απενεργοποιήστε πρώτα το Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Δεν υπάρχει αρκετή ελεύθερη μνήμη για τη λήψη. Επανεκκινήστε και δοκιμάστε ξανά." },
{ "Nothing selected", "Τίποτα δεν επιλέχθηκε" },
{ "Notification sound set", "Ήχος ειδοποίησης ορίστηκε" },
{ "Notify on new contact", "Ειδοποίηση για νέα επαφή" },
@@ -9933,6 +9964,7 @@ static const I18nPair kBuiltin_el[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Ακόμη αναλύεται η προηγούμενη\nδιαδρομή… δοκιμάστε σε λίγο." },
{ "Stop", "Διακοπή" },
{ "Storage", "Αποθηκευτικός χώρος" },
{ "Store", "Κατάστημα" },
{ "Store data on SD (reboot)", "Αποθήκευση δεδομένων σε SD (επανεκκίνηση)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Μεταδίδει την οθόνη ως ζωντανή εικόνα που πατάτε για έλεγχο. Βαρύτερο· το πάνελ δείχνει placeholder. Η εναλλαγή επανεκκινεί τη συσκευή." },
{ "Switch", "Εναλλαγή" },
@@ -9946,6 +9978,7 @@ static const I18nPair kBuiltin_el[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Πλήκτρα καρτέλας — πατήστε μια σειρά, μετά ένα πλήκτρο" },
{ "Tap Use to switch - the device reboots to apply.", "Πατήστε Χρήση για αλλαγή — η συσκευή επανεκκινεί για να εφαρμοστεί." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Πατήστε Χρήση για αλλαγή - η συσκευή επανεκκινεί. Τα αρχεία γλώσσας βρίσκονται στο /lang· επεξεργαστείτε τα ή προσθέστε δικά σας." },
{ "Tap a language; the device reboots to apply.", "Πατήστε μια γλώσσα· η συσκευή επανεκκινεί." },
{ "Taste the rainbow!", "Γευτείτε το ουράνιο τόξο!" },
@@ -10609,6 +10642,7 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normal (100%)\nGrande (150%)\nEnorme (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Memória insuficiente para Bluetooth. Desligue o Wi-Fi primeiro." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Memória insuficiente para Wi-Fi. Desligue o Bluetooth primeiro." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Memória livre insuficiente para baixar. Reinicie e tente novamente." },
{ "Nothing selected", "Nada selecionado" },
{ "Notification sound set", "Som de notificação definido" },
{ "Notify on new contact", "Notificar novo contato" },
@@ -10850,6 +10884,7 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Ainda analisando a rota\nanterior… tente de novo em instantes." },
{ "Stop", "Parar" },
{ "Storage", "Armazenamento" },
{ "Store", "Loja" },
{ "Store data on SD (reboot)", "Salvar dados no SD (reinicia)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Transmite a tela como imagem ao vivo que você toca para controlar. Mais pesado; o painel mostra um espaço reservado. Alternar reinicia o aparelho." },
{ "Switch", "Trocar" },
@@ -10863,6 +10898,7 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Atalhos da aba — toque numa linha, depois numa tecla" },
{ "Tap Use to switch - the device reboots to apply.", "Toque em Usar para trocar — o aparelho reinicia para aplicar." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Toque em Usar para trocar - o aparelho reinicia. Os arquivos de idioma ficam em /lang no armazenamento; edite-os ou adicione o seu." },
{ "Tap a language; the device reboots to apply.", "Toque num idioma; o aparelho reinicia para aplicar." },
{ "Taste the rainbow!", "Prove o arco-íris!" },
@@ -11526,6 +11562,7 @@ static const I18nPair kBuiltin_ro[] = {
{ "Normal (100%)\nLarge (150%)\nHuge (200%)", "Normal (100%)\nMare (150%)\nFoarte mare (200%)" },
{ "Not enough free memory for Bluetooth. Turn Wi-Fi off first.", "Memorie liberă insuficientă pentru Bluetooth. Opreşte mai întâi Wi-Fi." },
{ "Not enough free memory for Wi-Fi. Turn Bluetooth off first.", "Memorie liberă insuficientă pentru Wi-Fi. Opreşte mai întâi Bluetooth." },
{ "Not enough free memory to fetch it. Reboot and try again.", "Memorie liberă insuficientă pentru descărcare. Reporniți și încercați din nou." },
{ "Nothing selected", "Nimic selectat" },
{ "Notification sound set", "Sunet de notificare setat" },
{ "Notify on new contact", "Notifică la contact nou" },
@@ -11767,6 +11804,7 @@ static const I18nPair kBuiltin_ro[] = {
{ "Still analyzing the previous\npath… try again in a moment.", "Încă se analizează calea\nanterioară… reîncearcă imediat." },
{ "Stop", "Opreşte" },
{ "Storage", "Stocare" },
{ "Store", "Magazin" },
{ "Store data on SD (reboot)", "Stochează datele pe SD (repornire)" },
{ "Streams the device screen as a live picture you tap to control. Heavier; the panel shows a placeholder. Toggling reboots the device.", "Transmite ecranul ca imagine live pe care o atingi pentru control. Mai greu; panoul arată un substituent. Comutarea reporneşte dispozitivul." },
{ "Switch", "Comută" },
@@ -11780,6 +11818,7 @@ static const I18nPair kBuiltin_ro[] = {
{ "TOUCH BETA", "TOUCH BETA" },
{ "TX", "TX" },
{ "Tab hotkeys — tap a row, then press a key", "Taste rapide pentru file — atinge un rând, apoi apasă o tastă" },
{ "Tap Use to switch - the device reboots to apply.", "Atinge Foloseşte pentru a schimba – dispozitivul repornește pentru aplicare." },
{ "Tap Use to switch - the device reboots to apply. Language files live in /lang on the storage; edit them or add your own.", "Atinge Foloseşte pentru a schimba - dispozitivul reporneşte. Fişierele de limbă stau în /lang pe stocare; editează-le sau adaugă-le pe ale tale." },
{ "Tap a language; the device reboots to apply.", "Atinge o limbă; dispozitivul reporneşte pentru aplicare." },
{ "Taste the rainbow!", "Gustă curcubeul!" },