Commit Graph
6531 Commits
Author SHA1 Message Date
Alain BrenzikoferandClaude Opus 5 87a0232d8e core: deleting the seed reports ok, not the empty wallet
Delete re-ran /_wallet, so a successful delete printed "no wallet key",
which is the same words the error uses and reads as a failure. There is
nothing to show after a delete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 15:24:18 +00:00
Alain BrenzikoferandClaude Opus 5 a5730b6210 docs: 24 words is a choice, not a default
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 15:19:34 +00:00
Alain BrenzikoferandClaude Opus 5 899d5c3a41 core: the column holds entropy, so name it entropy
The mnemonic comes back from the entropy exactly, which is what export
returns, and the seed PBKDF2 derives from the mnemonic is computed when a
key is needed and never stored. The column was the one thing called seed
that is not the seed, which is why it needed a comment saying so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 14:19:31 +00:00
Alain BrenzikoferandClaude Opus 5 da20a039ae core: the argument is a mnemonic, so name it one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 14:06:26 +00:00
Alain BrenzikoferandClaude Opus 5 443fd43545 core: one command for the seed, whichever way it arrives
create and import differed only in where the entropy came from and already
shared one store function, so they are one command with the source named:
/_wallet create new, or /_wallet create seed=<phrase>. Naming it means no
seed is generated by typing a prefix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 12:28:46 +00:00
Alain BrenzikoferandClaude Opus 5 bfa510cdb1 docs: rfc for names counted per device
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 12:13:20 +00:00
Alain BrenzikoferandClaude Opus 5 309358a162 core: names are the device's, so drop the per-profile account
A name's profile is the record it resolves to, not the key that owns it, so
the account level was carrying a mapping nothing needs. It was also the only
source of the profile to account ambiguity after a restore, of /_wallet
bind, and of the index gap and the refusal that told a visible profile an
account was held by one it cannot see. All of it goes.

Names sit in account 0 from index 1. Index 0 is left unused so neither the
names nor the profile accounts, which start at 1, claim the origin of both
dimensions. users gains no columns at all now, so the migration is one
CREATE TABLE.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 12:12:41 +00:00
Alain BrenzikoferandClaude Opus 5 05508c0edd docs: give the reason for the counter before what it costs
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 07:35:39 +00:00
Alain BrenzikoferandClaude Opus 5 af1349160e docs: the reason for a key per name is that owners are public
One address owning several names links them: whoever knows one name can read
its owner and find the others. That is the reason, not the narrower export.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 07:33:29 +00:00
Alain BrenzikoferandClaude Opus 5 bf5b9d5af4 docs: the wallet is not only for names
The seed, the accounts and the derivation are a wallet. Owning a name is the
first thing it is used for, not what it is, and the title and the problem it
solves now say so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 07:16:20 +00:00
Alain BrenzikoferandClaude Opus 5 bcfa74ed9c docs: what the stealth meta address is for
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 07:09:56 +00:00
Alain BrenzikoferandClaude Opus 5 044e0fc0d6 docs: cut the rfc to what stands on its own
Drops the references to other documents and to what is or is not in a pull
request, so the design reads without them, and the list of tests, which
restated their names and would drift. 164 lines to 134, against a median of
117 for the rfcs here that define schema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-11 06:51:10 +00:00
Alain BrenzikoferandClaude Opus 5 f45647e113 core: pin simplexmq with the wordlist embedded from the upstream file
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 17:15:38 +00:00
Alain BrenzikoferandClaude Opus 5 472ee5ef10 core: refuse an account index BIP-32 cannot harden, whichever way it arrives
The counter path checked it, the explicit one did not, and the parser is not
the only caller: processChatCommand takes APIWalletBind from library callers
too. hardened leaves an index at or above 2^31 alone, so account i and
i + 2^31 are the same key while the duplicate check compares the stored
integers, and two profiles could hold one account's names.

The rfc now says what hiding a profile from /_wallet does and does not
protect: the seed is one per device, so whoever unlocks any profile can
derive every account.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 16:44:15 +00:00
Alain BrenzikoferandClaude Opus 5 3ad17ca477 core: pin simplexmq for the BIP-44 path, and use it
ethereumPath now takes the address index, so the path a name key sits at is
built once in the library rather than again here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 16:29:06 +00:00
Alain BrenzikoferandClaude Opus 5 81fd2415a5 docs: rfc for the keys that own names
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 14:59:55 +00:00
Alain BrenzikoferandClaude Opus 5 619434626c core: store the seed as bytes, and bind one account per profile
On Postgres a bare ByteString binds as a text literal, so entropy with a
backslash or a high bit was rejected and entropy with a zero byte was
truncated: the BIP-39 test vector stored as an empty seed. Blob columns go
through DB.Binary here, as every other one does.

/_wallet bind with no account moved a profile that already had one to a
fresh account, abandoning the old one without saying so. It refuses now;
moving is asked for by number.

The counter could walk past 2^31, where BIP-32 hardening folds every index
back onto a low one, so a profile handed 2147483648 derived account 0's
keys and printed account 0's path. Binding refuses once the counter is
there, and a very long index is now rejected on its digit count rather than
after reading it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 14:28:06 +00:00
Alain BrenzikoferandClaude Opus 5 03b6ed9a53 core: drop the types nothing reads, and say account where it means account
AccountRef and WalletAccount were built and never read: deriveNameKey now
returns the key, and the address comes from addressFromPrivateKey, which
already exists. SeedId is a plain Int64 like every other row id here.

The master key is derived once per command rather than once per name key,
which is where PBKDF2 runs.

A device has a key and a profile has an account, so /_wallet says "no
account for this profile" rather than repeating "no wallet key", which is
what it says when the device has none.

Also: no "wallet: " in front of "bad chat command: ", strEncode for the
address, the repeated seed lookup in one place, comments cut to what the
code does not say, and the export test pins its addresses and secrets,
including one whose first byte is zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 13:45:48 +00:00
Alain BrenzikoferandClaude Opus 5 2d9fe10252 core: test the restore cases the key has to survive
A database backed up after the key carries the key and the binding, so the
restore is all of it and an import is refused. A database backed up before
the key comes back with the profiles and no binding, so the phrase is
imported and each profile says which account was its. A key imported before
the database is restored is replaced by what the backup held.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 13:12:27 +00:00
Alain BrenzikoferandClaude Opus 5 1e0c3503e0 core: create binds no profile either
Create and import differed only in whether they bound a profile, which made
the convenient one inconsistent. Neither binds now, so the store keeps one
function for both, and a profile reaches an account only through bind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 13:06:45 +00:00
Alain BrenzikoferandClaude Opus 5 c5b44fe848 core: the known issue is now about bind, not import
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 12:54:40 +00:00
Alain BrenzikoferandClaude Opus 5 9cba89aa17 core: create makes the seed, bind claims a path
The two were one command: on a device that already had a seed, /_wallet
create only bound a profile. Now create makes the seed and refuses when one
exists, and binds every profile that exists, as a new seed has no account
that already owns a name.

/_wallet import binds nothing. Which account a profile had is what an
import is recovering, and the seed does not say, so the profile says it
with /_wallet bind <account>. /_wallet bind with no account takes the next
free one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 12:51:09 +00:00
Alain BrenzikoferandClaude Opus 5 07acdbb85b core: say same seed, not same key, for the other profiles
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 12:31:23 +00:00
Alain BrenzikoferandClaude Opus 5 de895ff43a core: /_wallet bind, and name the other profiles instead of numbering them
Restoring a chat database older than the key loses which account each
profile had, and the keys are still there, so /_wallet bind <account> sets
it by hand. The counter moves past an account bound this way, and an
account another profile holds is refused.

/_wallet now shows the paths of the active profile only, and names the
other profiles on the key. Their account indexes were the gap that showed
a hidden profile exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 12:21:32 +00:00
Alain BrenzikoferandClaude Opus 5 ab50d9f4b1 core: reject a key index BIP-32 cannot harden
A.decimal wraps into Word32 and hardened leaves anything at or above 2^31
alone, so /_wallet export 4294967296 0 returned account 0's secret under the
path m/44'/60'/0'/0/0. Handing out a different account's key for a mistyped
index is the wrong failure for this command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 11:15:02 +00:00
Alain BrenzikoferandClaude Opus 5 db1b6413e0 core: export the secret of a single name key
/_wallet export gives the seed mnemonic, /_wallet export <account> <name>
gives the secret of one derived key, so a single name can be handed over
without the seed. Both are what a wallet takes on import: the mnemonic as a
recovery phrase, the secret as hex.

The commands are named for what they return, APIWalletExportSeedMnemonic
and APIWalletExportDerivedSecret, as the difference is which secret leaves
the device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 10:11:29 +00:00
Alain BrenzikoferandClaude Opus 5 e7f5908d01 core: name the unique index on wallet_seeds
The one key per device rule is meant to be lifted later. SQLite cannot drop
a column with an inline UNIQUE, or its automatic index, so lifting it would
have meant rebuilding the table. As a named index it is a DROP INDEX and a
DROP COLUMN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 09:53:26 +00:00
Alain BrenzikoferandClaude Opus 5 fe9c20370f core: /_wallet delete takes no confirmation word
The wallet is an API, and no other API command takes a typed confirmation:
/_delete user takes the profile password. The word was also no barrier to a
caller, which can read it from /_wallet export. Confirming belongs in the
command that will wrap this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-10 08:43:43 +00:00
Alain BrenzikoferandClaude Opus 5 e5d18bd59e core: wallet export and delete act on the device key
The key is one per device, so both commands read it directly instead of
going through the active profile's account. A profile without an account
of its own can now export, and the delete error says that the key goes for
every profile on the device. The confirmation word is lowercased, as the
phrase is when imported.

Two known limitations are written down: account indexes restart at 0 after
an import, so profiles are bound in the order they ask for a key, and the
gap left by a hidden profile in the printed indexes shows that it exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-09 16:06:34 +00:00
Alain BrenzikoferandClaude Opus 5 70d4c128bd core: make the wallet an API, not a user-facing feature
Names commands will use the wallet; users do not. The commands move to
/_wallet, and the help section, its topic and the changelog entry are
removed. The tests drive the API end to end.

Comments cut to what the code does not say, dead exports removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-09 14:00:25 +00:00
Alain BrenzikoferandClaude Opus 5 ee11b368b6 core: wallet review fixes, and show the derived addresses
Concurrency: the account index is incremented in SQL and read back in the
same transaction, so two profiles cannot be handed the same key. Importing
a phrase is one transaction and single_seed is UNIQUE, so a phrase cannot
be discarded in favour of a key created meanwhile, and a device cannot end
up with two keys.

Wallet commands are no longer forwarded to a remote host: the recovery
phrase must not leave the device, and the raw command is logged there.

/wallet delete removes the key, confirmed by the last word of the phrase,
so creating a key before importing your own is no longer a dead end.

/wallet now shows every profile on the key with the first two name
addresses each, to check derivation against other wallets. Hidden profiles
are left out, as they are by /users. A bad phrase no longer says which word
was wrong. /wallet export uses the profile's own key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-09 09:05:47 +00:00
Alain BrenzikoferandClaude Opus 5 257cdc02c8 core: pin simplexmq with eth crypto merged into master
ab/eth-crypto forked before Crypto.Entitlement, which Chat.Badges imports,
so the branch had to be merged with simplexmq master before it could be
pinned here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-08 15:47:40 +00:00
Alain BrenzikoferandClaude Opus 5 c11d0318c4 core: wallet key for name ownership (CLI only)
A name has to be owned by an address the client can still derive after a
restart or on a new device. This adds that key and nothing else.

/wallet create makes one BIP-39 key per device and one BIP-44 account per
chat profile under it, /wallet shows the address that would own the next
name that profile buys, /wallet import and /wallet export move the key with
its recovery phrase.

A name key sits at m/44'/60'/<profile>'/0/<name>, which is ordinary BIP-44,
so the phrase reaches the same addresses in other wallets. No signing, so
nothing can be bought or edited yet.

Split out of #7390 / #7425.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
2026-09-08 15:30:13 +00:00
EvgenyandEvgeny @ SimpleX Chat e8fd58bc59 website: improve livestream signup form (#7473)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-08 13:46:46 +01:00
EvgenyandEvgeny @ SimpleX Chat 2caacecf6a website: subscription confirmation page (#7472)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-08 12:14:26 +01:00
EvgenyandEvgeny @ SimpleX Chat d2b7b1ec81 website: update thankyou page (#7470)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-08 09:08:09 +01:00
EvgenyandEvgeny @ SimpleX Chat c9a0f62d38 website: fix livestream form (#7469)
* website: fix livestream form

* target

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-07 23:49:48 +01:00
Evgeny dcef344bab blog: post by Nick Rogers (#7467) 2026-09-07 21:30:26 +01:00
EvgenyandEvgeny @ SimpleX Chat 46a1cdbce6 website: subscription thank you page (#7466)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-07 21:03:56 +01:00
Evgeny Poberezkin 5847fa7f31 website: update crowdfunding post/page 2026-09-05 21:21:49 +01:00
Narasimha-sc 6e3c668fb9 ios, android, desktop: do not add loaded items of another chat to the opened chat (#7450)
* ios, android, desktop: do not add loaded items of another chat to the opened chat

apiLoadMessages could apply a loaded page to a chat it was not loaded for,
mixing messages of two chats in one list:

- the chat id was not checked at all for .last pagination, and the loading
  coroutine is not cancelled when the chat is closed (PreloadItems wraps it
  into NonCancellable, apiFindMessages runs in a scope of its own), so a .last
  page loaded for the previously opened chat was appended to the items of the
  chat opened while it was loading;
- for the other paginations the chat id was checked before the items were
  loaded, but they are applied on the main thread after a dispatch, so the chat
  could be switched in between.

Check the chat id (and the remote host id in kotlin) right before the items are
applied, in every pagination except .initial and opening around item that set
chat id themselves. Empty chatItems remain allowed for .last that is used for
searching.

* android, desktop: test that loaded items of another chat are not added to the opened chat

Reproduces the bug deterministically, without depending on the timing of the
race: processLoadedChat is called with a page loaded for another chat while the
opened chat has its own items.

Without the fix the items of the two chats end up in one list:

    lastPageLoadedForAnotherChatIsNotAddedToOpenedChat
      expected:<[101, 102]> but was:<[101, 102, 201, 202]>
    beforePageLoadedForAnotherChatIsNotAddedToOpenedChat
      expected:<[101, 102]> but was:<[201, 202, 101, 102]>
    aroundPageLoadedForAnotherChatIsNotAddedToOpenedChat
      expected:<[101, 102]> but was:<[101, 102, 201, 202]>
2026-09-05 15:28:00 +01:00
EvgenyandEvgeny @ SimpleX Chat 76a577afb3 core: use file expiration time when sending history (#7454)
* core: use file expiration time when sending history

* simplify

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-05 11:03:57 +01:00
Evgeny Poberezkin a0b6643932 Merge branch 'stable' 2026-09-02 19:20:55 +01:00
SimpleX Chat 3d5e85b180 7.1-beta.2: android 377, desktop 160, ios 352 v7.1.0-beta.2 2026-09-02 15:18:18 +00:00
SimpleX Chat 4df04bdb3f 7.0.2: android 375, desktop 159, ios 351 v7.0.2 2026-09-02 12:22:31 +00:00
Evgeny Poberezkin 4bb7e0f1bf core: 7.1.0.4 (simplexmq 7.1.0.5) 2026-09-02 11:08:06 +01:00
a09a38ce1f website: translations (#7447)
* Translated using Weblate (French)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (Italian)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/

* Translated using Weblate (French)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (Italian)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/

* Added translation using Weblate (Azerbaijani)

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (379 of 379 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/hu/

* website: fix translation review findings (fr, es, it, hu)

Adversarial review of the translations on this branch.

Blocking corrections:

- fr tap-the-connect-button-in-the-app named a button that does not exist.
  The French app label is "Se connecter" (MR/fr/strings.xml connect_button);
  the branch had dropped "se".
- it index-token-p2-cf rendered "equity crowdfunding" as "raccolta fondi",
  the Italian term for a charitable fundraiser, on a string describing a
  securities offering. It also contradicted its own neighbour
  index-token-cta-cf ("Investi in SimpleX Chat").
- es index-hero-invest used an infinitive where the hero is otherwise
  tu-imperative, and index-hero-invest-cta turned "learn more on Wefunder"
  into "learn more about Wefunder".
- hu more-info moved to "tudnivalok" while its show/hide partner hide-info
  still said "informacio"; both labels belong to one button, so hide-info
  is realigned.

French quality:

- "ratchet steps" had become "chaine de cles" (keychain). Every other locale
  keeps "ratchet", as does the sibling string in the same table.
- Five Wikipedia links had been de-localised to en.wikipedia.org while
  simplex-network-overlay-card-1-p-1 still pointed at fr.wikipedia.org.
  Restored the French targets (es and it localise these too) along with the
  French release title of The Mauritanian.
- "each node is discoverable" had become "identifiable", contradicting the
  same sentence's "ne permet pas de decouvrir les serveurs".
- "relais de messagerie" reads as an SMTP mail relay; docs-dropdown-16 now
  says "relais de chat" and matches the nominal pattern of docs-dropdown-4/5.
- Realigned terminology against untouched strings: base de donnees des chats,
  chatbot, files d'attente, code QR, annuaire, and the infinitive used in the
  comparison table.
- Removed a dangling feminine pronoun in the-instructions--source-code.
- Unified apostrophes, em dashes and non-breaking spaces across the 92 strings
  this branch touches.

No keys added or removed; all four files stay complete against en.json.

Not addressed here, pre-existing and not introduced by this branch: fr
hero-overlay-card-3-p-3 has lost its "Read more" anchor, hu
hero-overlay-card-3-p-3 describes the July 2024 Trail of Bits review as a
repeat audit of cryptographic and networking components rather than a review
of cryptographic design, and es docs-dropdown-4/5 read as noun phrases rather
than "Host an SMP/XFTP server".

---------

Co-authored-by: Ranakan <ranakan.dev@protonmail.ch>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: No name <usir.alerts@onionmail.org>
Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com>
Co-authored-by: summoner001 <summoner@disroot.org>
Co-authored-by: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com>
2026-09-02 10:27:17 +01:00
+6 dacbcb00e5 ui: translations (#7446)
* Translated using Weblate (Italian)

Currently translated at 100.0% (2904 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.9% (2903 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2904 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pt_BR/

* Translated using Weblate (Slovak)

Currently translated at 29.6% (862 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Translated using Weblate (German)

Currently translated at 100.0% (2904 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (French)

Currently translated at 93.5% (2717 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2904 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2904 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Slovak)

Currently translated at 31.6% (918 of 2904 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Translated using Weblate (German)

Currently translated at 100.0% (2905 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2905 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.9% (2904 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Slovak)

Currently translated at 31.7% (921 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (2905 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Slovak)

Currently translated at 33.0% (960 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2905 of 2905 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (French)

Currently translated at 97.0% (2827 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2912 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Slovak)

Currently translated at 34.4% (1003 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Translated using Weblate (French)

Currently translated at 100.0% (2912 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Czech)

Currently translated at 93.4% (2722 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2912 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Slovak)

Currently translated at 36.1% (1053 of 2912 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2917 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.9% (2916 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Slovak)

Currently translated at 36.6% (1068 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Added translation using Weblate (Azerbaijani)

* Added translation using Weblate (Azerbaijani)

* Translated using Weblate (German)

Currently translated at 100.0% (2917 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (French)

Currently translated at 100.0% (2917 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (2917 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2917 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2501 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Slovak)

Currently translated at 37.4% (1091 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* Translated using Weblate (Azerbaijani)

Currently translated at 31.5% (789 of 2501 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/az/

* Translated using Weblate (Slovak)

Currently translated at 37.5% (1094 of 2917 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/sk/

* ui: process translations

* ui: fix translation errors found in review (de, es, fr, hu)

de: "you removed %1$s" was translated as an agentless passive
("%1$s wurde aus der Gruppe entfernt"), so your own removal of a member
rendered identically to a removal by someone else. The UI prepends the
member name only for rcv group events, so snd events must name the actor
themselves, as every other snd_group_event_* string does
("Sie haben %s blockiert", "Sie haben dieses Mitglied angenommen").

fr: "Unblock members for all?" was translated as "Débloquer tous les
membres ?" ("unblock ALL members"), dropping the for-all moderation scope
and mis-stating the target set. Aligned with its sibling
block_members_for_all_question ("Bloquer ces membres pour tous ?").

fr: member_info_status was left untranslated as "Status"; the file uses
"Statut" for the other InfoRow status labels.

fr: restore_passphrase_can_not_be_read_desc called the Keystore
"coffre-fort" while its near-identical sibling
restore_passphrase_can_not_be_read_enter_manually_desc called it
"Keystore".

es: "registar" -> "registrar".

hu: "[kapcsolattal] (https://...)" had a space between ] and (, so the
transport-isolation link did not render as a markdown link on iOS.

---------

Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Hosted Weblate user 54392 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: SanctumSaturn <oraculogithub@gmail.com>
Co-authored-by: Martin Kozempel <rado2012mir@gmail.com>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Ranakan <ranakan.dev@protonmail.ch>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@disroot.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: No name <usir.alerts@onionmail.org>
Co-authored-by: Pablo <pyg@duck.com>
Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Mireli5656 <miraliseyidli5656@gmail.com>
Co-authored-by: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com>
2026-09-02 10:26:52 +01:00
Evgeny Poberezkin 5349cb24d5 Merge branch 'stable' 2026-09-02 08:30:56 +01:00
EvgenyandEvgeny @ SimpleX Chat 735662902f core, ui: make badge expiration non-optional (#7445)
* core, ui: make badge expiration non-optional

* update

* update bot types

* fix to use non-optional badge expiry

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-09-02 08:14:09 +01:00