oxedyne/daimond/www/i18n/en.js
288 KiB, 41 runs
created by r2519314175:1323, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | /* i18n/en.js — every English string the interface says, in one table. |
| 2 | * |
| 3 | * This is the BASELINE. A translator copies it to `i18n/<locale>.js`, changes |
| 4 | * the code in the `register` call and the values, and leaves the keys alone. |
| 5 | * A key missing from a translation falls back to the English here rather than |
| 6 | * to a blank, so a half-finished file is usable on the day it starts. |
| 7 | * |
| 8 | * Conventions: |
| 9 | * - `{name}` is a placeholder, filled at the call site. Keep every one that |
| 10 | * appears, in whatever order the target language wants them. |
| 11 | * - A key ending `.one` / `.other` is a plural pair, chosen by count. A |
| 12 | * language with more forms adds them and overrides the chooser. |
| 13 | * - Keys ending `_help` are tooltips, `_ph` are placeholders inside a field. |
| 14 | * - British spelling throughout the English. |
| 15 | * - Product nouns are NOT translated: Daimond, Diamond, Daimond Pro, Hands. |
| 16 | * A "Diamond" is a named object in this app, not the gemstone. |
| 17 | * |
| 18 | * DECLARATIONS THE CHECKER READS. `dev/i18ncheck.mjs` reads every `t`, `tOr`, |
| 19 | * `tf` and `tr` call site in the app and asks whether the key it names is in |
| 20 | * this table. Two shapes of call site name no key it can read, and each is |
| 21 | * DECLARED here rather than skipped there -- a checker with a skip list of its |
| 22 | * own is the next thing to go blind: |
| 23 | * |
| 24 | * // i18n-family: <prefix> = one two three |
| 25 | * A key built by concatenation, `tOr('<prefix>' + v, …)`. Closed: the |
| 26 | * variants listed are exactly the keys this table holds under that |
| 27 | * prefix, checked both ways, so a variant added to one and not the other |
| 28 | * fails. A prefix may be declared over several lines and the variants add |
| 29 | * up; a family of twenty-two reads as groups, not as one long line. |
| 30 | * |
| 31 | * // i18n-family: <prefix> = open -- <why it cannot be closed> |
| 32 | * The same, where the value comes from outside this app and no list can |
| 33 | * be right. Reported by name on every run with its member count, never |
| 34 | * silently. |
| 35 | * |
| 36 | * // i18n-family: <prefix> = one-way one two -- <why the prefix is a namespace> |
| 37 | * The same, where the prefix is a NAMESPACE and not a set: other keys |
| 38 | * live under it that are not variants at all. Only the forward sweep runs |
| 39 | * -- every variant named must be in this table -- and the reverse sweep, |
| 40 | * which would demand those other keys be declared variants too, is given |
| 41 | * up. That is a weakening, so it is reported by name on every run beside |
| 42 | * the open families, and it is decided per family: `permmode.` needs it |
| 43 | * and the other eleven do not. |
| 44 | * |
| 45 | * // i18n-indirect: <file> <expression> = key key |
| 46 | * A call site whose key is held in a variable. The keys it can name are |
| 47 | * written out here; every one must be in this table. |
| 48 | * |
| 49 | * // i18n-indirect: <file> <expression> = open -- <why no list can be right> |
| 50 | * The same, where the expression forms names on purpose that this table |
| 51 | * does not hold -- a probe using `t`'s own loudness as its test -- or |
| 52 | * where the key arrives from the markup. Reported on every run. |
| 53 | * |
| 54 | * An undeclared call site of either shape FAILS the check. That is the point: |
| 55 | * the declaration is a decision on the record, and a missing one is a question |
| 56 | * nobody answered. Two shapes need no declaration because the key is in plain |
| 57 | * sight: a conditional of literals, `t(read ? 'attach.read' : 'attach.note')`, |
| 58 | * which is read for both keys, and a `t(k)` inside a `tOr`/`tf`/`tr` definition |
| 59 | * naming that helper's own key parameter, which is the `tOr(…)` call above it |
| 60 | * and is read there. |
| 61 | */ |
| 62 | (function () { |
| 63 | 'use strict'; |
| 64 | if (!window.DaimondI18n) return; |
| 65 | |
| 66 | window.DaimondI18n.register('en', { |
| 67 | |
| 68 | // ── Where `t` itself resolves a key ──────────────────────── |
| 69 | // Five calls in js/i18n.js are the lookup layer doing its own work rather |
| 70 | // than call sites naming a key: the plural helper appending a form, the |
| 71 | // applier reading a key out of the markup, and `pick` recovering the key or |
| 72 | // keys behind a string a widget was handed. Every one of them is handed a |
| 73 | // name from somewhere else, so no list here could be right. |
| 74 | // i18n-indirect: i18n.js key + (n === 1 ? '.one' : '.other') = open -- `tn` appends the plural form to the key its own caller named |
| 75 | // i18n-indirect: i18n.js n.getAttribute('data-i18n-label') = open -- the key is in the markup, where check 4 of dev/i18ncheck.mjs reads it |
| 76 | // i18n-indirect: i18n.js spec = open -- a `data-i18n` mark, which is one key or several joined by `|` |
| 77 | // i18n-indirect: i18n.js ks[0] = open -- one of the keys behind such a mark |
| 78 | // i18n-indirect: i18n.js ks[i] = open -- the rest of them |
| 79 | |
| 80 | // ── Shared words ─────────────────────────────────────────── |
| 81 | 'common.close': 'Close', |
| 82 | // The spoken name of a closer, so a reader hears which of the app's |
| 83 | // crosses this is rather than a fourth button called Close. |
| 84 | 'common.close_named': 'Close {name}', |
| 85 | 'admin.close_drawer': 'Close the Admin panel', |
| 86 | 'rail.close_rail': 'Close the Diamonds panel', |
| 87 | 'sheet.close': 'Close the sheet', |
| 88 | 'pal.close': 'Close the Go to box', |
| 89 | 'common.dismiss': 'Dismiss', |
| 90 | 'agents.read': 'Read', |
| 91 | 'agents.read_help': 'Open this agent’s transcript as a file in the Doc panel', |
| 92 | 'agents.read_nothing': 'This agent has not said anything yet.', |
| 93 | 'common.close_panel': 'Close panel', |
| 94 | 'common.refresh': 'Refresh', |
| 95 | 'common.copy': 'Copy', |
| 96 | 'common.cancel': 'Cancel', |
| 97 | 'common.save': 'Save', |
| 98 | |
| 99 | // Copying an identifier. `copy.aria` wraps each `what_` so a reader hears |
| 100 | // "Copy the build id" rather than five buttons all called Copy. |
| 101 | 'copy.aria': 'Copy {what}', |
| 102 | 'copy.failed': 'Could not copy', |
| 103 | 'copy.nothing': 'Nothing to copy', |
| 104 | 'copy.what_build': 'the build id', |
| 105 | 'copy.what_build_n': 'build {id}', |
| 106 | 'copy.what_fingerprint': 'your account fingerprint', |
| 107 | 'copy.what_device': 'the id of {name}', |
| 108 | 'copy.what_model': 'model id {id}', |
| 109 | 'copy.what_default_model': 'the default model id', |
| 110 | 'copy.what_handle': 'your public handle', |
| 111 | |
| 112 | // ── The top bar ──────────────────────────────────────────── |
| 113 | 'topbar.menu': 'Menu', |
| 114 | 'topbar.open_menu': 'Open menu', |
| 115 | 'topbar.brand': 'Daimond, the daimond.app home page', |
| 116 | 'topbar.made_by': 'Made by Oxedyne', |
| 117 | 'topbar.made_with_ai': 'Code made mostly with AI', |
| 118 | 'topbar.up_to_date': 'Daimond is up to date', |
| 119 | 'topbar.about': 'About Daimond', |
| 120 | 'topbar.guide': 'Open the user guide', |
| 121 | 'topbar.guide_short': 'User guide', |
| 122 | 'topbar.appearance': 'Appearance and layout', |
| 123 | // Full-screen toggle (phone only). See js/fullscreen.js. |
| 124 | 'fullscreen.enter': 'Full screen', |
| 125 | 'fullscreen.exit': 'Exit full screen', |
| 126 | |
| 127 | // ── About ────────────────────────────────────────────────── |
| 128 | // `about.what` is the guide's opening paragraph, near enough word for word: |
| 129 | // it is already the house voice and already says it, and a second wording |
| 130 | // would be a second thing to keep true. |
| 131 | // |
| 132 | // It said "nothing to install" until the app became installable -- a service |
| 133 | // worker, a manifest and a guide section on adding it to a home screen -- at |
| 134 | // which point the About box was denying a feature that had shipped. The |
| 135 | // guide's own word is "nothing to DOWNLOAD", which is what stayed true: an |
| 136 | // installed Daimond is the same page and the same stored work, with an icon |
| 137 | // and a window of its own, and nothing is fetched to a disk. |
| 138 | 'about.title': 'About Daimond', |
| 139 | 'about.splash_alt': 'A figure flying through the night towards a diamond, above the curve of a planet.', |
| 140 | 'about.what': 'Daimond is an AI agent workspace that runs in your browser. There is no server account and nothing to download. Your chats, keys and files stay on this device.', |
| 141 | // The link out to the delivery check. Painted rather than bound to a |
| 142 | // `data-i18n` mark -- see the note at daimond.js's `about.verify` call. |
| 143 | 'about.verify': 'Check this build against the published source', |
| 144 | |
| 145 | // ── Panel names ──────────────────────────────────────────── |
| 146 | // Short: they ride in a chip row and a phone tab bar. |
| 147 | 'panel.rail': 'Diamonds', |
| 148 | 'panel.ai': 'AI', |
| 149 | 'panel.web': 'Web', |
| 150 | 'panel.doc': 'Doc', |
| 151 | 'panel.preview': 'Preview', |
| 152 | // "Email message" and not "Message": the Social panel holds messages of its |
| 153 | // own, and two things called Message is the collision decision 17 removes. |
| 154 | 'panel.msg': 'Email message', |
| 155 | 'panel.tools': 'Tools', |
| 156 | 'panel.compose': 'Compose', |
| 157 | 'panel.graph': 'Graph', |
| 158 | 'panel.agents': 'Agents', |
| 159 | 'panel.pending': 'Pending', |
| 160 | 'skills.menu': 'Skills you can call', |
| 161 | 'skills.none': 'No skills yet. Put a markdown file in {dir} and its name becomes a command.', |
| 162 | 'skills.draft_badge': '· draft', |
| 163 | 'skills.draft_title': 'Install /{name}?', |
| 164 | 'skills.draft_ask': 'Daimond drafted this skill. Installing it writes exactly the text below to {into}, where typing /{name} will run it.', |
| 165 | 'skills.draft_install': 'Install', |
| 166 | 'skills.draft_bad': 'This draft cannot be installed', |
| 167 | 'panel.mail': 'Email', |
| 168 | 'panel.work': 'Workspace', |
| 169 | 'panel.spend': 'Spending', |
| 170 | 'panel.term': 'Terminal', |
| 171 | |
| 172 | // ── The chip row ─────────────────────────────────────────── |
| 173 | // {name} is a panel's name, from `panel.*` above. |
| 174 | 'chip.dock_full': 'The dock is full. Choose a larger tiling in Appearance, or close a panel.', |
| 175 | 'chip.show': 'Show {name}', |
| 176 | 'chip.close': 'Close {name}', |
| 177 | 'chip.open': 'Open {name}', |
| 178 | 'chip.open_instead': 'Open {name} in place of the panel beside the chat', |
| 179 | 'chip.more.one': '{n} more panel', |
| 180 | 'chip.more.other': '{n} more panels', |
| 181 | |
| 182 | // ── The panel gallery and the command palette ────────────── |
| 183 | 'gallery.title': 'Panels', |
| 184 | 'pal.title': 'Go to', |
| 185 | 'pal.placeholder': 'Go to a panel, or a setting…', |
| 186 | 'pal.results': 'Results', |
| 187 | 'pal.nothing': 'Nothing matches.', |
| 188 | 'pal.kind_panel': 'Panel', |
| 189 | 'pal.kind_theme': 'Theme', |
| 190 | 'pal.kind_size': 'Text size', |
| 191 | 'pal.kind_dock': 'Dock', |
| 192 | 'pal.kind_lang': 'Language', |
| 193 | 'pal.kind_ccy': 'Currency', |
| 194 | 'pal.kind_permmode': 'Permissions', |
| 195 | 'pal.hint_open': 'open', |
| 196 | 'pal.hint_full': 'dock full', |
| 197 | 'pal.hint_current': 'current', |
| 198 | |
| 199 | // ── The rail ─────────────────────────────────────────────── |
| 200 | 'rail.diamonds': 'Diamonds', |
| 201 | 'rail.diamonds_help': 'A repeatable method, distilled from a conversation.', |
| 202 | 'rail.new_diamond': 'New Diamond', |
| 203 | 'rail.unnamed_diamond': 'Unnamed Diamond', |
| 204 | 'rail.chats': 'Chats', |
| 205 | 'rail.chats_help': 'Raw conversation. A Diamond is cut from it.', |
| 206 | 'rail.new_chat': 'New chat', |
| 207 | 'rail.chats_menu': 'More', |
| 208 | 'rail.delete_all_chats': 'Delete all chats', |
| 209 | 'layout.handle': 'Drag to resize, double-click to reset', |
| 210 | |
| 211 | // ── Admin ────────────────────────────────────────────────── |
| 212 | 'admin.identity': 'Your Daimond identity', |
| 213 | 'admin.settings': 'Settings', |
| 214 | 'admin.local_identity': 'Local identity', |
| 215 | 'admin.account_help': 'Your account. Click for logout, passphrase and backup.', |
| 216 | 'admin.locked': 'Locked', |
| 217 | 'admin.locked_help': 'Locked. Enter your passphrase to unlock.', |
| 218 | 'admin.no_account': 'No account', |
| 219 | 'admin.no_account_help': 'Your API key is stored unencrypted. Click to create an account and encrypt it.', |
| 220 | 'admin.no_crypto_help': 'This browser has no WebCrypto, so keys cannot be encrypted here.', |
| 221 | 'admin.version_history': 'Version history', |
| 222 | |
| 223 | // ── The Admin drawer ─────────────────────────────────────── |
| 224 | // The name at the head of the drawer, which changes with what it is holding. |
| 225 | 'drawer.admin': 'Admin', |
| 226 | 'drawer.models': 'Models', |
| 227 | 'drawer.version': 'Version', |
| 228 | 'drawer.credits': 'Credits', |
| 229 | 'drawer.push': 'Push', |
| 230 | |
| 231 | // ── Dialogs ──────────────────────────────────────────────── |
| 232 | 'dlg.are_you_sure': 'Are you sure?', |
| 233 | 'dlg.ok': 'OK', |
| 234 | 'dlg.done': 'Done', |
| 235 | 'dlg.not_now': 'Not now', |
| 236 | |
| 237 | // ── The Admin drawer's home menu ─────────────────────────── |
| 238 | 'home.connect_model': 'Connect a model', |
| 239 | 'home.connect_note': 'Daimond needs a provider key or credits before it can answer.', |
| 240 | 'home.locked': 'Locked. Enter your passphrase to unlock this device.', |
| 241 | 'home.sec_account': 'Account', |
| 242 | 'home.sec_prompts': 'Prompts', |
| 243 | 'home.sec_accounts': 'Accounts', |
| 244 | 'home.create_account': 'Create an account…', |
| 245 | 'home.account_note': 'A passphrase on this device that encrypts your keys and signs you in. Nothing leaves the browser.', |
| 246 | 'home.fingerprint': 'Your device identity fingerprint', |
| 247 | 'home.change_name': 'Change name…', |
| 248 | // The account's PUBLIC name, which is a different thing from the line above: |
| 249 | // that one labels this device's keypair and is seen by nobody. |
| 250 | 'home.handle_help': 'Your public handle: the name other people see, the same on every device of this account.', |
| 251 | 'home.change_handle': 'Change public handle…', |
| 252 | // The dialog that opens is titled without the ellipsis: the row invites, the |
| 253 | // dialog states. |
| 254 | 'handle.rename_title': 'Change public handle', |
| 255 | 'home.change_passphrase': 'Change passphrase…', |
| 256 | 'home.add_passkey': 'Add a passkey…', |
| 257 | 'home.remove_passkey': 'Remove passkey', |
| 258 | 'home.export_backup': 'Export a backup', |
| 259 | 'home.import_backup': 'Import a backup…', |
| 260 | 'home.dashboard': 'Daimond Dashboard ↗', |
| 261 | 'home.signed_in_as': 'You are signed in as {role}.', |
| 262 | // {role} is a prompt role's name, lower-cased by the caller. |
| 263 | 'home.edit_prompt': 'Edit the {role} prompt…', |
| 264 | 'home.prompt_opens': 'Opens {path} in the Doc panel.', |
| 265 | 'home.prompts_note': 'What each agent runs under, kept as editable files. A page or email is always data, never a command.', |
| 266 | 'home.switch_to': 'Switch to {name}', |
| 267 | 'home.unnamed_account': 'Unnamed account', |
| 268 | 'home.add_account': '+ Add another account', |
| 269 | 'home.accounts_note': 'Each has its own chats, keys and files, and cannot see another’s. Switching locks this one and opens the other.', |
| 270 | 'home.log_out': 'Log out', |
| 271 | 'home.sec_devices': 'Devices', |
| 272 | 'home.sec_console': 'Console', |
| 273 | 'home.console_open': 'Open the operator console', |
| 274 | 'home.console_note': 'This account holds the {role} role. The console opens in a new tab and follows this sign-in.', |
| 275 | 'home.sec_push': 'Push', |
| 276 | 'home.push_setup': 'Set up git push…', |
| 277 | 'home.push_to': 'Change the push token for {host}…', |
| 278 | 'home.push_help': 'A token lets Daimond push what it has committed. Without one it still commits, but every push is refused.', |
| 279 | |
| 280 | // ── The credential a push travels with ───────────────────── |
| 281 | // Two boxes and no more: the user name a token travels as is a per-forge |
| 282 | // constant, so it is inferred from the host rather than asked for. Every line |
| 283 | // here says what happens if it is skipped, because "no credential" and |
| 284 | // "authentication failed" look identical from inside a refused push. |
| 285 | 'push.lead': 'A token lets Daimond push work you have committed. Without one it still commits, but every push is refused and the work stays on this machine.', |
| 286 | 'push.host': 'Host', |
| 287 | 'push.host_ph': 'github.com', |
| 288 | 'push.host_note': 'Just the name, as in github.com. No https://, no port, no path. Only this host is authenticated; a repository anywhere else is refused, and your token never reaches it.', |
| 289 | 'push.token': 'Token', |
| 290 | 'push.token_ph': 'Paste a token with push access', |
| 291 | 'push.token_note': 'Push access is enough; the token never needs more. Daimond only pushes, only fast-forward, only to origin. Force, delete, mirror and any other remote are refused whatever the token allows. Empty this box and save to remove it.', |
| 292 | 'push.save': 'Save', |
| 293 | 'push.privacy': 'The token is encrypted under your passphrase and kept in this browser. It is never shown again, never sent to us, never given to a model. Without an account there is nothing to encrypt it with, so it is held for this tab only and asked for again after a reload.', |
| 294 | 'push.set': 'Set. A push reaches {host}.', |
| 295 | 'push.none': 'Not set. Every push is refused until a token is saved here.', |
| 296 | 'push.saved': 'Saved. Encrypted under your passphrase and used each time you unlock.', |
| 297 | 'push.cleared': 'Removed. Pushes are refused again until a token is saved here.', |
| 298 | 'push.session_only': 'Held for this tab only. With no account there is nothing to encrypt it with, so a reload asks for it again. Create an account to keep it.', |
| 299 | 'push.err_host': 'Enter the host the push goes to, as in github.com, or empty the token box to remove the credential.', |
| 300 | 'push.err_not_held': 'That was not accepted, so no push credential is held. Check the host and the token.', |
| 301 | 'push.not_resealed': 'Your passphrase changed, but the push token could not be re-encrypted under it. Pushing works until you reload. Set the token again to keep it.', |
| 302 | // What the token is CALLED, for a list of what did not survive a passphrase |
| 303 | // change. Never shown on its own; the sentence above is what a person reads. |
| 304 | 'push.the_token': 'your push token', |
| 305 | |
| 306 | // ── The devices that sync this account ───────────────────── |
| 307 | // The list is what it says it is: devices that have SYNCED. A linked device |
| 308 | // holds the same keys as this one, so there is nothing to revoke and the |
| 309 | // wording must not imply there is. |
| 310 | 'devices.on_platform': '{brand} on {platform}', |
| 311 | 'devices.unknown': 'This device', |
| 312 | 'devices.this_device': 'this device', |
| 313 | 'devices.only_this': 'Only this device syncs this account.', |
| 314 | // Naming a device. The name is the user's own words, so it never leaves the |
| 315 | // browser except inside the encrypted parcel; the wording says so, because a |
| 316 | // list of the user's devices is exactly where that question is asked. |
| 317 | 'devices.rename_title': 'Name this device', |
| 318 | 'devices.rename_body': 'A name to tell this device from the others. Empty the box to go back to “{derived}”. The name is stored with your own encrypted data and travels only to your other devices.', |
| 319 | 'devices.rename_aria': 'Rename {name}', |
| 320 | 'devices.note': 'Devices that have synced this account. One that holds the account but has never synced is not listed. Nothing here signs a device out. A linked device holds the same keys as this one, so it has to be dealt with there.', |
| 321 | 'devices.remove_aria': 'Remove {name}', |
| 322 | 'devices.remove_body': '“{name}” comes off this list. That does not sign the device out. It holds the same keys as this one, so it reappears the next time it syncs.', |
| 323 | 'devices.remove': 'Remove', |
| 324 | 'devices.remove_title':'Remove this device', |
| 325 | // The always-on runner: one nominated device picks up handed-off turns, so a |
| 326 | // device that may be closed mid-turn does not grab one. |
| 327 | 'devices.nominee': 'runner', |
| 328 | 'devices.nominee_aria': '{name} is the always-on runner', |
| 329 | 'devices.nominate_aria': 'Make {name} the always-on runner', |
| 330 | 'devices.unnominate_aria': 'Clear {name} as the always-on runner', |
| 331 | 'devices.nominee_note': 'Star one device as the always-on runner. Handed-off turns wait for it while it is awake, and fall to another awake device only when it is not.', |
| 332 | |
| 333 | // ── The status rows under the rail ───────────────────────── |
| 334 | 'astat.offline': 'Offline', |
| 335 | 'astat.service_unreachable': 'Account service unreachable', |
| 336 | 'astat.no_account': 'No credits account', |
| 337 | 'money.daimond_credits': 'Daimond credits', |
| 338 | 'money.own_key': 'Your {provider} key', |
| 339 | 'money.own_keys': 'Your own keys', |
| 340 | 'money.left_at_rate': '~{mins} min at this rate', |
| 341 | 'money.spent_so_far': '{amt} spent', |
| 342 | 'money.spent_help': 'Your provider reports no balance, so this is what has gone through this key since Daimond started counting.', |
| 343 | 'money.estimate_help': 'Your own figure, less what has been spent since. Update it in Models when you top up.', |
| 344 | 'astat.locked': 'Locked', |
| 345 | 'astat.credits': 'Credits', |
| 346 | 'astat.credits_help': 'Buy credits, or connect your own provider key', |
| 347 | 'astat.pro_owned': 'Subscribed', |
| 348 | 'astat.pro_owned_help': 'You have Daimond Pro. Sync, cloud storage and Email are on.', |
| 349 | 'astat.pro_paused': 'Billing paused', |
| 350 | 'astat.pro_paused_help': 'You were away, so we paused your Pro billing. It resumes the moment you are back.', |
| 351 | 'astat.pro_ended': 'Subscription ended', |
| 352 | 'astat.pro_ended_help': 'Your Pro subscription has ended. Sync, cloud storage and Email are off; everything on this device is untouched.', |
| 353 | 'astat.pro_upgrade': 'Get Pro', |
| 354 | 'astat.pro_upgrade_help': 'Subscribe to Pro to turn on sync, cloud storage and Email.', |
| 355 | 'astat.tools': 'Tools · {have}', |
| 356 | 'astat.tools_of': 'Tools · {have} of {all}', |
| 357 | 'astat.tools_help': 'What Daimond can do, and what the rest would cost.', |
| 358 | 'astat.workspace_browser': 'Workspace · Browser', |
| 359 | 'astat.workspace_native': 'Workspace · Machine', |
| 360 | 'astat.evictable': 'evictable', |
| 361 | 'astat.store_of': '{used} of {quota} this browser allows.', |
| 362 | 'astat.store_persistent': 'Marked persistent, so the browser will not evict it.', |
| 363 | 'astat.store_evictable': 'Not persistent: the browser may evict this workspace under storage pressure. Click to ask for permanent storage.', |
| 364 | 'astat.not_connected': 'not connected', |
| 365 | 'astat.native_help': 'Let the agents work on a real folder on this machine.', |
| 366 | 'astat.n_files': '{n} files', |
| 367 | 'astat.counting': 'Counting. Click to stop.', |
| 368 | 'astat.part': '(part)', |
| 369 | 'astat.counted': '{n} files under {folder}.', |
| 370 | 'astat.counted_partial': '{n} files under {folder}, counted until you stopped it.', |
| 371 | 'astat.count_again': 'Click to count again.', |
| 372 | 'astat.count_offer': 'Click to count it. The browser will not say how big a real folder is, and counting reads every file, so a large tree takes a while. You can stop at any point.', |
| 373 | |
| 374 | // ── Pro, in its own words ────────────────────────────────── |
| 375 | // Pro is a FIVE-YEAR licence, not a perpetual one: it gates sync, cloud |
| 376 | // storage and Email, which are services Oxedyne has to keep running, and a |
| 377 | // licence with no end to a service is a promise to run it for ever. Terms §7 |
| 378 | // says so, and nothing here may say otherwise -- what the app claims at the |
| 379 | // point of sale is the claim the Terms have to support. "No subscription. |
| 380 | // Nothing renews." stays: it is true, and it is the point. |
| 381 | 'pro.owned_plain': 'You have Daimond Pro. Cross-device sync, cloud storage and Email are on.', |
| 382 | 'pro.offer_plain': 'Daimond Pro is $8 a month. It turns on cross-device sync, cloud storage, and Email, so your own mail is read and sent in the workspace. Monthly, and you can cancel any time. And if you are away for a month, we pause the billing on our own and resume it when you are back. Inference, bandwidth and other metered use are still paid from credits.', |
| 383 | 'pro.paused_plain': 'We paused your Pro billing while you were away. Resume it and cross-device sync, cloud storage and Email come straight back on -- you are not charged for the time you were gone.', |
| 384 | 'pro.term_note': 'Pro is $8 a month, monthly, cancel any time.', |
| 385 | 'pro.renews_on': 'Your subscription renews on {date}.', |
| 386 | 'pro.paused_note': 'Billing is paused while you are away. It resumes the moment you are back -- you are not charged for the time you were gone.', |
| 387 | 'pro.past_due_note': 'A payment did not go through. We are retrying your card; Pro stays on in the meantime.', |
| 388 | 'pro.paused_head': 'We paused your billing while you were away.', |
| 389 | 'pro.warned_soon': 'You have been away a while. If you stay away, we will pause your billing on its own -- and resume it the moment you are back.', |
| 390 | 'pro.subscribe': 'Subscribe to Pro', |
| 391 | 'pro.subscribe_priced': 'Subscribe — {price}/month', |
| 392 | 'pro.resume': 'Resume Pro', |
| 393 | 'pro.cancel': 'Cancel subscription', |
| 394 | 'pro.cancel_confirm': 'Cancel Daimond Pro? It stays on until the end of the month you have paid for, then sync, cloud storage and Email switch off. Everything on this device is untouched.', |
| 395 | 'pro.checkout_failed': 'Could not start checkout', |
| 396 | |
| 397 | // ── The egress gate ──────────────────────────────────────── |
| 398 | // The last thing between a steered agent and a request that carries the |
| 399 | // user's own information out. {host} is a hostname; keep it where a reader |
| 400 | // will look for it. |
| 401 | 'egress.type_title': 'Type into {host}?', |
| 402 | 'egress.type_body': 'This turn has read content from outside your workspace. Daimond now wants to type into the page at {host}, which may send it on.\n\nWhat it wants to enter:\n\n{text}\n\nIf you did not expect this, decline.', |
| 403 | 'egress.type_ok': 'Type it', |
| 404 | 'egress.nothing': '(nothing)', |
| 405 | 'egress.act_title': 'Act on {host}?', |
| 406 | 'egress.act_body': 'This turn has read content from outside your workspace. Daimond now wants to act on the page at {host}.\n\nWhoever wrote the page wrote its links, and following one can carry information away.\n\nAllow this only if you expected it.', |
| 407 | 'egress.act_ok': 'Allow acting on {host}', |
| 408 | 'egress.heavy_title': 'Send this to {host}?', |
| 409 | 'egress.heavy_body': 'This turn has read content from outside your workspace. Daimond now wants to reach {host} with an unusually long address.\n\nAn address can carry information out. This one carries:\n\n{text}\n\nIf you did not expect this, decline. Nothing is lost but the one request.', |
| 410 | 'egress.heavy_ok': 'Send it anyway', |
| 411 | 'egress.reach_title': 'Reach {host}?', |
| 412 | 'egress.reach_body': 'This turn has read a web page or a message from outside your workspace, and something in it may be steering Daimond.\n\nIt now wants to reach {host}, which it has not visited before. Anything it knows could ride in that address.\n\nAllow this only if you expected it.', |
| 413 | 'egress.reach_ok': 'Allow {host}', |
| 414 | 'egress.any_title': 'Let Daimond reach any website?', |
| 415 | 'egress.any_body': 'This conversation has read a web page or a message written by somebody else, and something in it may be steering Daimond.\n\nIt now wants to reach {host}.\n\nSaying yes lets it reach any website at all for the rest of this conversation, not only this one, and you will not be asked here again. Anything Daimond knows can be written into a web address, so a page it reaches can carry your words away with it.\n\nA new chat asks again, and so does a fresh daimon. An unusually long address is always put to you on its own, whatever you say now.', |
| 416 | 'egress.any_ok': 'Allow any website here', |
| 417 | // A search, where the thing leaving is the QUERY rather than an address. |
| 418 | // {query} is the user's own search text, already truncated, and it stands |
| 419 | // alone in its own paragraph -- never wrapped in quotation marks, because |
| 420 | // the query may contain quotes of its own and a mismatched pair reads as |
| 421 | // corruption. {engine} is a display name and is sometimes a proper noun |
| 422 | // (Brave Search, Serper) and sometimes a translated phrase (Daimond |
| 423 | // credits). Every \n\n is load-bearing: five paragraphs, four breaks, and |
| 424 | // a consent dialog run together into a wall is a dialog nobody reads. The |
| 425 | // clause about the setting answers the question a real user asked -- why |
| 426 | // that engine -- and the last sentence tells a frightened reader that |
| 427 | // declining is cheap. Neither may be compressed away. |
| 428 | 'egress.search_title': 'Search the web?', |
| 429 | 'egress.search_body': 'This turn has read content from outside your workspace. Daimond now wants to search the web.\n\nWhat it wants to search for:\n\n{query}\n\nSearching with {engine}, which is your setting. The query is what leaves this device.\n\nIf you did not expect this, decline. Nothing is lost but the one search.', |
| 430 | 'egress.search_ok': 'Run this search', |
| 431 | |
| 432 | // ── A chat tile in the rail ──────────────────────────────── |
| 433 | 'chat.tool_refused': 'refused', |
| 434 | 'chat.tool_failed': 'failed', |
| 435 | 'chat.copy_message': 'Copy message', |
| 436 | 'chat.include_turn': 'Include this turn when folding', |
| 437 | 'chat.connect_to_chat': 'Connect a provider, or unlock, to chat on this model.', |
| 438 | 'tile.cost_estimated': 'Estimated. This model is not in the price table.', |
| 439 | 'tile.cost_so_far': 'Cost so far for this chat.', |
| 440 | 'tile.click_to_open': 'Click to open, double-click to rename', |
| 441 | 'tile.settings': 'Settings for this tile', |
| 442 | 'tile.settings_named': 'Settings for "{name}"', |
| 443 | 'tile.close_named': 'Close "{name}"', |
| 444 | // The name field in a chat's cog dialog. A chat needs no name, so the hint |
| 445 | // says what happens when it is left empty rather than asking for one. |
| 446 | 'tile.dlg_name': 'Name', |
| 447 | 'tile.dlg_name_hint': 'Unnamed: the rail shows the time', |
| 448 | 'tile.dlg_running': 'Running', |
| 449 | 'tile.dlg_detail': 'Detail', |
| 450 | 'tile.detail_simple': 'Simple', |
| 451 | 'tile.detail_simple_help': 'The name, whether it is running, and what is waiting.', |
| 452 | 'tile.detail_max': 'Max', |
| 453 | 'tile.detail_max_help': 'Models, context, cost, version and when it last moved.', |
| 454 | 'tile.spend_help': 'What this Diamond has cost, over {turns} turns.', |
| 455 | 'tile.detail_default': 'Default ({what})', |
| 456 | 'tile.detail_default_help': 'Follow the view set in Appearance, and any change to it.', |
| 457 | 'tile.detail_note': 'Tiles follow the view unless you set one differently here.', |
| 458 | |
| 459 | // ── The models a Diamond runs on, and the context its chats hold ── |
| 460 | 'tile.dlg_models': 'Models', |
| 461 | 'tile.model_daimon': 'Daimon', |
| 462 | 'tile.model_daimon_help': 'The model this Diamond thinks with. The conversation carries over; use Fresh daimon to end it.', |
| 463 | 'tile.daimon_reset': 'Fresh daimon', |
| 464 | 'tile.daimon_reset_help': 'End this daimon’s conversation without folding it into the crystal', |
| 465 | 'tile.daimon_reset_title': 'Start a fresh daimon?', |
| 466 | 'tile.daimon_reset_body': 'This daimon’s conversation ({n} messages) is discarded, and the next instruction starts a new one. Nothing goes into the crystal — fold it first if any of it is worth keeping. The crystal, the files and the links are untouched.', |
| 467 | 'tile.daimon_reset_ok': 'Discard the conversation', |
| 468 | 'tile.daimon_reset_done': 'A fresh daimon. The crystal is untouched.', |
| 469 | 'tile.daimon_reset_busy_title': 'The daimon is still working', |
| 470 | 'tile.daimon_reset_busy': 'This daimon is in the middle of a turn. Its answer would land in the conversation you are about to discard, and so would the conversation the model is holding — so nothing is discarded yet. Stop the turn first: while it runs, the composer’s button is Stop.', |
| 471 | 'tile.model_workers': 'Workers', |
| 472 | 'tile.model_vision': 'Workers, images', |
| 473 | 'tile.model_vision_help': 'The model a worker runs on when its task names an image, and the model a worker is moved to when the one it is on turns out not to take pictures. Daimond cannot tell in advance which models see, so the choice is yours; it acts on the first refusal.', |
| 474 | 'tile.model_same_as_text': 'Same as the daimon', |
| 475 | 'tile.model_none': 'none', |
| 476 | 'tile.diamond_model_help': 'This Diamond thinks with {model}.', |
| 477 | 'tile.model_note': 'A worker keeps the model it started on. A new model applies to the next one dispatched.', |
| 478 | 'tile.model_change': 'Change', |
| 479 | 'tile.model_change_help': 'Switch the daimon to the selected model. The conversation carries over.', |
| 480 | 'tile.model_changed': 'Daimon moved from {from} to {to}. The conversation continues.', |
| 481 | 'tile.model_change_note': 'Daimon moved from {from} to {to}.', |
| 482 | 'tile.model_change_unlogged': 'The model changed, but the history entry could not be written.', |
| 483 | 'tile.dlg_context': 'Context', |
| 484 | 'tile.context_line': '{used} of {all} used. Folds at {at}%.', |
| 485 | 'tile.context_unknown': 'This model publishes no context window, so there is nothing to measure against until a request is refused.', |
| 486 | 'tile.context_used_folds': 'Context window used: {used} / {all}. Folds at {at}%.', |
| 487 | 'tile.fold_context': 'Fold now', |
| 488 | 'tile.fold_context_help': 'Summarise the earlier part of this conversation now, before it fills up', |
| 489 | 'tile.fold_context_title': 'Fold this conversation?', |
| 490 | 'tile.fold_context_body': 'A model summarises the earlier part, which costs a call, and the summary becomes what this chat remembers. The messages on screen stay where they are.', |
| 491 | 'tile.fold_context_ok': 'Fold', |
| 492 | 'tile.fold_nothing': 'Nothing to fold. This conversation is already as short as it goes.', |
| 493 | 'tile.fold_mid_turn': 'Wait for this turn to finish before folding.', |
| 494 | 'tile.fold_unavailable': 'Nothing has been said in this chat yet.', |
| 495 | 'tile.fold_failed': 'The fold did not finish', |
| 496 | |
| 497 | 'tile.dlg_delete': 'Delete', |
| 498 | 'tile.start': 'Start', |
| 499 | 'tile.start_help': 'Confirm the model and start this chat', |
| 500 | 'tile.workers': 'Workers', |
| 501 | 'tile.worker_model_help': 'The model workers run on. Left alone, they run on the chat’s own model.', |
| 502 | 'tile.folded': 'Folded', |
| 503 | 'tile.fold_all': 'Fold all', |
| 504 | 'tile.folded_help': 'Already folded into "{name}". Fold again to add what is new since.', |
| 505 | 'tile.fold_all_help': 'Fold this whole chat into a Diamond', |
| 506 | 'tile.dlg_colour': 'Colour', |
| 507 | 'tile.colour_bg': 'Background', |
| 508 | 'tile.colour_fg': 'Text', |
| 509 | 'tile.colour_clear':'Use the theme’s colours', |
| 510 | 'tile.colour_faint':'These two contrast at {ratio}:1, below the usual {min}:1 floor for text this size. Left as chosen.', |
| 511 | |
| 512 | // ── The spend governor ───────────────────────────────────── |
| 513 | 'gov.past_budget': 'Well past your run budget', |
| 514 | 'gov.faster': 'Spending faster than usual', |
| 515 | 'gov.per_min': '{rate}/min', |
| 516 | 'gov.run_spent': 'This run has spent {spent} of a {budget} pace budget. A large fan-out asks before it runs.', |
| 517 | 'gov.fanout_body.one': 'This Diamond is about to run {n} agent, at about {total} ({each} each).', |
| 518 | 'gov.fanout_body.other': 'This Diamond is about to run {n} agents, at about {total} ({each} each).', |
| 519 | 'gov.burst_spent': 'This burst has spent {spent} already.', |
| 520 | 'gov.would_pass': 'That would pass your {budget} pace budget for one run.', |
| 521 | 'gov.run_n.one': 'Run {n} agent', |
| 522 | 'gov.run_n.other': 'Run {n} agents', |
| 523 | 'spend.includes_estimate': 'Estimated. At least one model here is not in the price table.', |
| 524 | |
| 525 | // ── Models and provider keys ─────────────────────────────── |
| 526 | 'models.lead': 'A key for each provider you use. New chats and Diamonds start on the default model; any model can be picked for a single chat.', |
| 527 | 'models.add': '+ Add provider', |
| 528 | 'models.provider': 'Provider', |
| 529 | 'models.choose_provider': 'Choose a provider…', |
| 530 | 'models.custom_provider': 'Custom (advanced)…', |
| 531 | 'models.base_url': 'Base URL', |
| 532 | 'models.api_key': 'API key', |
| 533 | 'models.api_key_ph': 'Paste your API key', |
| 534 | 'models.default_model': 'Default model', |
| 535 | 'models.choose_provider_first': 'Choose a provider first…', |
| 536 | 'models.model_id_ph': 'Model id (type manually)', |
| 537 | 'models.default_note': 'New agents use this unless you choose another for them.', |
| 538 | 'models.save_start': 'Save & start', |
| 539 | 'models.privacy': '<strong>Everything stays on this device.</strong> Your keys, chats and files live only in this browser, and nothing is sent to us.', |
| 540 | |
| 541 | // The Models panel: a row per provider, expandable to the models it runs. |
| 542 | // A vendor's name (Groq, Fireworks AI) is a proper noun and is NOT here; |
| 543 | // these two are names Daimond made up for itself. |
| 544 | 'models.credits_row': 'Daimond credits', |
| 545 | 'models.custom_provider_name': 'Custom provider', |
| 546 | // A stand-in where the gateway did not name the company behind a minted key. |
| 547 | // It goes into {provider} slots built for a BRAND NAME ("via {provider}", |
| 548 | // "Remove {provider}"), so it must read as a name, not as a noun phrase with |
| 549 | // an article -- "via the provider" forces an article a Romance language |
| 550 | // then has to fight. |
| 551 | 'models.the_provider': 'your provider', |
| 552 | 'models.empty': 'No provider yet. Add one to give Daimond a model to think with.', |
| 553 | 'models.balance_left': '{amount} left', |
| 554 | 'models.via': 'via {provider}', |
| 555 | 'models.count.one': '{n} model', |
| 556 | 'models.count.other': '{n} models', |
| 557 | 'models.row_paid_help': 'These models spend your Daimond balance. Your browser calls {provider} directly with a key minted for you; nothing passes through Daimond.', |
| 558 | 'models.row_own_help': 'These models are billed to your own {provider} account. They do not touch your Daimond balance.', |
| 559 | // What a row says about its key. The symbol in front is added by the code. |
| 560 | 'models.sealed': 'sealed', |
| 561 | 'models.key_set': 'key set', |
| 562 | 'models.no_key': 'no key', |
| 563 | 'models.connecting': 'connecting…', |
| 564 | 'models.no_credits': 'no credits', |
| 565 | 'models.offline': 'account service unreachable', |
| 566 | 'models.could_not_connect': 'could not connect', |
| 567 | 'models.unlock_to_use': 'unlock to use', |
| 568 | 'models.sealed_unlock': 'sealed, unlock to use', |
| 569 | 'models.top_up_to_use': 'top up to use', |
| 570 | 'models.top_up': 'Top up your credits →', |
| 571 | 'models.ask_provider': 'Ask this provider what it can run', |
| 572 | 'models.ask_provider_again': 'Ask this provider again', |
| 573 | 'models.list_asked': 'Listed {when}.', |
| 574 | 'models.list_never': 'Never asked.', |
| 575 | 'models.add_key_first': 'Add a key first', |
| 576 | 'models.asking': 'Asking…', |
| 577 | // One model in the list. |
| 578 | 'models.econ_credits': 'credits', |
| 579 | 'models.econ_own': 'your key', |
| 580 | 'models.favourites': 'Favourites', |
| 581 | 'models.is_default': 'default', |
| 582 | 'models.model_is_default': 'New chats and Diamonds start on this model.', |
| 583 | 'models.model_make_default': 'Make this the model new chats and Diamonds start on.', |
| 584 | 'models.model_paid': 'Spends your Daimond balance, via {provider}.', |
| 585 | 'models.model_own': 'Billed to your own {provider} account.', |
| 586 | 'models.model_twin': 'Another provider serves a model of this name. This is the {provider} one.', |
| 587 | 'models.remove': 'Remove {provider}', |
| 588 | 'models.starts_on': 'New chats start on: {model}', |
| 589 | 'models.no_default': 'No default model yet. Star one above.', |
| 590 | // ── Track D: drafting model (one contiguous block, en only) ── |
| 591 | 'models.drafting_label': 'Drafting model', |
| 592 | 'models.drafting_help': 'Model that drafts proposals from your notes. Defaults to your chat model.', |
| 593 | 'models.drafting_same': 'Same as chat', |
| 594 | 'models.drafting_same_on': 'Same as chat ({model})', |
| 595 | // ── end Track D block ── |
| 596 | 'models.none_yet': 'No provider yet', |
| 597 | 'models.err_refused': 'The account service refused (HTTP {status}).', |
| 598 | 'models.err_bad_key': 'The account service sent a key Daimond cannot use.', |
| 599 | 'models.err_no_key': 'That provider has no key yet.', |
| 600 | 'models.err_key_refused': 'The provider refused the key (HTTP {status}).', |
| 601 | // What is left on a provider key, and how that is known. The manual line is |
| 602 | // deliberately not called a balance: it is the user's own figure less an |
| 603 | // estimate, and saying so is the whole point of showing it. |
| 604 | 'models.credit_auto': '{amount} left on this key, as the provider said at {when}.', |
| 605 | 'models.credit_manual': '{amount} left. Your {base} from {when}, less about {spent} spent since.', |
| 606 | 'models.credit_unknown': 'Nothing known about what is left on this key.', |
| 607 | 'models.credit_check': 'Ask what is left', |
| 608 | 'models.credit_recheck': 'Ask again', |
| 609 | 'models.credit_probe_failed': '{provider} would not say what is left. Tell Daimond instead.', |
| 610 | 'models.credit_base_ph': 'e.g. 12.50', |
| 611 | 'models.credit_base_label': 'What is on this key now, in US dollars', |
| 612 | 'models.credit_base_set': 'I have this much, as of now', |
| 613 | 'models.credit_base_update': 'Update my figure', |
| 614 | 'models.credit_base_bad': 'That is not an amount. Enter a number of US dollars.', |
| 615 | // A probed figure that this device has since watched money leave. It stops |
| 616 | // claiming to be what the provider said, because it no longer is. |
| 617 | 'models.credit_auto_spent': '{amount} left. The provider said {base} at {when}, less about {spent} spent here since.', |
| 618 | // How old the reading is. Without it a figure that has quietly stopped |
| 619 | // updating looks exactly like one that is current. |
| 620 | 'models.age_now': 'Checked just now.', |
| 621 | 'models.age_mins.one': 'Checked {n} minute ago.', |
| 622 | 'models.age_mins.other': 'Checked {n} minutes ago.', |
| 623 | 'models.age_hours.one': 'Checked {n} hour ago.', |
| 624 | 'models.age_hours.other': 'Checked {n} hours ago.', |
| 625 | 'models.age_days.one': 'Checked {n} day ago.', |
| 626 | 'models.age_days.other': 'Checked {n} days ago.', |
| 627 | 'models.age_failed': 'The last check did not answer, so this figure has not moved since.', |
| 628 | |
| 629 | // ── Search ───────────────────────────────────────────────── |
| 630 | // The engine is the USER's setting, not the model's choice, so every string |
| 631 | // here belongs to the settings row rather than to a tool result. Engine |
| 632 | // names (Brave, Exa, Tavily, Serper) are proper nouns and are not here, for |
| 633 | // the same reason a provider's name is not: see the note above |
| 634 | // `models.credits_row`. |
| 635 | 'search.head': 'Search', |
| 636 | 'search.engine': 'Search engine', |
| 637 | // An allowance, not a free key: the key costs nothing and the queries past |
| 638 | // the allowance are billed by the vendor, to the user. Deliberately no |
| 639 | // figure and no engine named -- a number here goes stale silently when a |
| 640 | // vendor changes its tier, and nobody re-reads eight locales to catch it. |
| 641 | // The per-engine line under the field (`.search-engine-note`) is the one |
| 642 | // that can be accurate. |
| 643 | 'search.engine_note': 'Which service Daimond searches with. Most give a free allowance each month if you bring your own key.', |
| 644 | 'search.credits': 'Daimond credits', |
| 645 | 'search.key': 'API key', |
| 646 | 'search.key_note': 'Kept on this device, sealed with your passphrase, and sent only with the search it pays for.', |
| 647 | // {engine} is an engine's name, e.g. Brave. It is never translated. |
| 648 | 'search.no_key': 'Add a key for {engine}, or switch to Daimond credits.', |
| 649 | // What is being searched: the open web, the news, or scholarly work. Three |
| 650 | // peers in one pulldown, so none of them takes an article, and the third |
| 651 | // is "Academic" rather than "Research" -- French would otherwise have one |
| 652 | // word, Recherche, doing three jobs in one panel: the heading, the engine |
| 653 | // and the corpus. |
| 654 | // Reached through a lookup table in daimond.js rather than by name, so the |
| 655 | // three keys are written out here for the checker: it can read a literal and |
| 656 | // it can read a prefix, and it must never guess at a variable. |
| 657 | // i18n-indirect: daimond.js kind[k] = search.kind_web search.kind_news search.kind_academic |
| 658 | 'search.kind_web': 'Web', |
| 659 | 'search.kind_news': 'News', |
| 660 | 'search.kind_academic': 'Academic', |
| 661 | // Named, not "that engine": two of the three places this is reached from |
| 662 | // -- the gateway's refusal and the operator console -- have no pulldown |
| 663 | // above them for a "that" to point at. |
| 664 | 'search.refused_serper': '{engine} can only be used with your own key.', |
| 665 | // The per-engine allowance under the key field -- the line that CAN be |
| 666 | // accurate, where `search.engine_note` deliberately is not. {n} arrives |
| 667 | // already grouped by toLocaleString(), so no separator of our own goes in; |
| 668 | // the reader gets 1,000 or 1 000 or 1.000 as their locale wants it. "Last |
| 669 | // time we looked" is not padding: it is a third party's pricing, it will |
| 670 | // go stale, and the hedge is what keeps this a report rather than a |
| 671 | // promise. Keep it in every language. |
| 672 | 'search.free_month': '{engine}: about {n} searches a month free, last time we looked.', |
| 673 | |
| 674 | // ── Credits, packs and Pro ───────────────────────────────── |
| 675 | // Anything the user is CHARGED says US dollars out loud. See `billing.*`. |
| 676 | 'credits.lead': 'Buy credits and Daimond runs the model for you, with no provider key to manage. No subscription. You keep what you buy, and nothing is charged again unless you turn on auto-reload.', |
| 677 | 'credits.balance': 'Balance: {amount}', |
| 678 | 'credits.balance_unavailable': 'Balance unavailable.', |
| 679 | 'credits.see_spend': 'See where your spending goes →', |
| 680 | 'credits.offline': 'The Daimond account service is unreachable, so credits are unavailable. Your own provider key still works.', |
| 681 | 'credits.need_account': 'Credits let you use Daimond without a provider key. They need an account: a passphrase kept on this device.', |
| 682 | 'credits.need_passcode': 'Credits need an account, and this gateway is not opening new ones while the beta is closed. Redeem a passcode above to open one.', |
| 683 | 'credits.create_account': 'Create an account', |
| 684 | 'credits.added': 'Credits added', |
| 685 | 'credits.now': 'Your balance is now {amount}.', |
| 686 | 'pro.owned': '<b>You have Daimond Pro.</b> Cross-device sync, cloud storage and Email are on.', |
| 687 | 'pro.offer': '<p><b>Daimond Pro — $8 a month.</b> Pro turns on cross-device sync, cloud storage, and Email, so your own mail is read and sent in the workspace. Away for a month? We pause the billing on our own and resume it when you are back.</p>', |
| 688 | 'pro.fine': 'Monthly, cancel any time. Metered use (inference, bandwidth, storage beyond the free tier) is paid from credits, whether or not you have Pro.', |
| 689 | 'billing.usd_note': 'You are billed in US dollars; the converted figure is approximate.', |
| 690 | 'billing.rates_as_of': 'Rates as of {date}, approximate.', |
| 691 | |
| 692 | // ── The closed beta and its passcode ─────────────────────── |
| 693 | // The gateway refuses to open a new account while the beta is shut, and a |
| 694 | // passcode is the one way past it. Nothing here may contradict what the |
| 695 | // gateway itself says (gateway/src/handlers/account.rs, `beta_closed`): that |
| 696 | // only the ACCOUNT is closed, that Daimond is free and works with no account |
| 697 | // at all on a provider key of the user's own, and that a passcode opens one. |
| 698 | // And nothing here may soften WHICH refusal a code met — "never issued", |
| 699 | // "already used" and "run out of time" send a person to three different |
| 700 | // places, which is the whole reason the gateway tells them apart. |
| 701 | // |
| 702 | // NOR MAY IT PROMISE WHAT A FREE ACCOUNT DOES NOT GIVE. Since 2026-08-17 a |
| 703 | // beta code grants the FREE tier unless the operator asks for Pro, and Pro |
| 704 | // bundles exactly two things — `ProBundled::ALL` in |
| 705 | // `gateway/src/handlers/common.rs` names sync and Email, and cloud upload |
| 706 | // rides the sync unlock. So a free account keeps the app, its credits, the |
| 707 | // inference bought with them and the telemetry it consented to, and has no |
| 708 | // sync, no cloud storage and no Daimond Email. The four-item list these |
| 709 | // strings used to carry ("credits, sync between devices, mail, and inference |
| 710 | // bought for you") promised two things a free code does not deliver, to |
| 711 | // every applicant who reached the door. |
| 712 | 'beta.title': 'Closed beta', |
| 713 | 'beta.title_unavailable': 'No account was made', |
| 714 | 'beta.title_plain': 'Beta passcode', |
| 715 | 'beta.lead_beta_only': 'Daimond is not opening new accounts on this server yet. Only the account is closed, not the app: Daimond is free, runs in your browser, and works with no account at all on a provider key of your own. An account adds credits and inference bought for you, so you need no key of your own. Sync between devices and Daimond Email come with Pro, which is bought separately.', |
| 716 | 'beta.lead_unavailable': 'This server could not say whether it is open to new accounts, so it made none. Nothing is wrong with your device, and Daimond works on your own provider key meanwhile. Try again in a moment.', |
| 717 | 'beta.lead_no_reason': 'This device has no Daimond account yet.', |
| 718 | 'beta.have_code': 'If Oxedyne sent you a beta passcode, put it in here. It works once, on this device.', |
| 719 | 'beta.no_code': 'No passcode? Places in each wave of the test are given out by application.', |
| 720 | 'beta.apply': 'Apply to test Daimond', |
| 721 | 'beta.code': 'Passcode', |
| 722 | 'beta.code_ph': 'abcd-efgh-ijkl', |
| 723 | 'beta.redeem': 'Redeem', |
| 724 | 'beta.redeeming': 'Redeeming…', |
| 725 | 'beta.try_again': 'Try again', |
| 726 | 'beta.enter_code': 'Enter a passcode', |
| 727 | 'beta.still_closed': 'Still no account. The server gave the same answer.', |
| 728 | 'beta.err_enter_code': 'Type the passcode you were sent.', |
| 729 | 'beta.err_locked': 'Unlock Daimond first: a passcode is redeemed onto this device’s key.', |
| 730 | 'beta.err_no_identity': 'This device has no identity to redeem a passcode onto. Create one first.', |
| 731 | 'beta.err_unknown': 'That passcode is not one we issued. Check it character by character — twelve characters, in three groups.', |
| 732 | 'beta.err_spent': 'That passcode has already been used. Each one works once, on one device.', |
| 733 | 'beta.err_expired': 'That passcode has expired. Ask Oxedyne for another.', |
| 734 | 'beta.err_throttled': 'Too many passcode attempts from this connection. Wait a while and try again.', |
| 735 | 'beta.err_generic': 'That passcode was not accepted.', |
| 736 | 'beta.err_unreachable': 'The Daimond account service could not be reached, so the passcode was not used. Try again shortly.', |
| 737 | 'beta.done_title': 'You are in', |
| 738 | 'beta.done_pro': 'This device has a Daimond account, and Pro is on it: sync between devices, cloud storage and Email.', |
| 739 | 'beta.done_plain': 'This device has a Daimond account. Everything in the app works, and credits buy inference for you, so you need no provider key of your own. Sync between devices, cloud storage and Email are what Pro adds — buy it whenever you want them.', |
| 740 | 'beta.done_handle': 'Other people see this account as {handle}.', |
| 741 | 'beta.done_not_signed_in': 'The passcode was spent and the account exists, but this device could not finish signing in just now. It will try again on its own.', |
| 742 | |
| 743 | // ── The one thing a tester is asked to agree to ──────────── |
| 744 | // Shown after the passcode is spent and the account exists, so declining |
| 745 | // cannot cost anybody anything, and again in the Credits drawer for as long |
| 746 | // as the account does -- which is what makes "you can turn it off" true. |
| 747 | // |
| 748 | // NOTHING HERE MAY OVERSTATE OR UNDERSTATE WHAT IS SENT. The whole of it is |
| 749 | // `www/js/telemetry.js`: twenty events, each one integer, and no text field |
| 750 | // anywhere in the payload. `tel_never` is the sentence that matters most and |
| 751 | // it is a claim about the code, not a promise about our care -- so it must |
| 752 | // not be softened into "we do not collect personal data" in any language. |
| 753 | // `tel_who` says it is not anonymous, because it is not: the numbers arrive |
| 754 | // on the account the passcode made, and the passcode carries the note we |
| 755 | // wrote about who it was for. |
| 756 | 'beta.tel_title': 'Send usage counts?', |
| 757 | 'beta.tel_title_on': 'Usage counts', |
| 758 | 'beta.tel_lead': 'The test is far more useful to us if Daimond can report how it is being used. It sends numbers only: which of twenty things happened, how many milliseconds into the session, and one count each — how long a turn took, which panel was opened, how many errors were thrown.', |
| 759 | 'beta.tel_never': 'It never sends words. Not a message, not a file name, not a Diamond’s name, not a path, not an error message. There is no box for them, and our server refuses a report that carries any.', |
| 760 | 'beta.tel_who': 'It goes to Oxedyne on the account your passcode made, so we can see which numbers are yours and come back and ask you about them.', |
| 761 | 'beta.tel_free': 'Saying no costs you nothing. Your account, your credits and everything else stay exactly as they are, and nothing in the app behaves differently.', |
| 762 | 'beta.tel_more': 'What is sent, in full', |
| 763 | 'beta.tel_yes': 'Send usage counts', |
| 764 | 'beta.tel_no': 'Do not send', |
| 765 | 'beta.tel_on': 'Daimond is sending usage counts for this account: numbers only, never words.', |
| 766 | 'beta.tel_stop': 'Stop sending usage counts', |
| 767 | |
| 768 | // ── The front door ───────────────────────────────────────── |
| 769 | // The strip above the passphrase form, for a browser that has never held an |
| 770 | // account. Three routes, named as the reader would name them: two of them |
| 771 | // are forms to fill in and the third is a code they already hold. The names |
| 772 | // are short because they sit side by side on a 366px card — a name that |
| 773 | // wraps to three lines is a route that reads as an obstacle. |
| 774 | 'door.head': 'New to Daimond?', |
| 775 | 'door.lead': 'It is in closed testing, so accounts are given out by passcode.', |
| 776 | 'door.test': 'Apply to test', |
| 777 | 'door.waitlist': 'Join the waitlist', |
| 778 | 'door.code': 'I have a passcode', |
| 779 | // Said when the code button is pressed before there is a key to redeem onto. |
| 780 | // It names the order and promises the next step, because the alternative — |
| 781 | // a code box that refuses the moment it is used — is the thing this app |
| 782 | // keeps being caught doing. |
| 783 | 'door.code_later': 'Make your passphrase first: a passcode is redeemed onto this device’s own key. The code box opens by itself as soon as it can.', |
| 784 | |
| 785 | // ── Auto-reload ──────────────────────────────────────────── |
| 786 | // Every amount here is charged to a card, so the copy stays plain. |
| 787 | 'autoreload.title': 'Auto-reload', |
| 788 | 'autoreload.lead': 'Daimond can buy credits when they run low, so a long job does not stop halfway. It charges the card below without asking, up to a limit you set here.', |
| 789 | 'autoreload.card_word': 'card', |
| 790 | 'autoreload.card_has': '{brand} ending {last4}', |
| 791 | 'autoreload.replace': 'Replace…', |
| 792 | 'autoreload.replace_help': 'Save a different card. Stripe collects it; Daimond never sees it.', |
| 793 | 'autoreload.no_card': 'No card saved.', |
| 794 | 'autoreload.save_card': 'Save a card', |
| 795 | 'autoreload.save_card_help': 'Opens Stripe’s own page. Nothing is charged, and no card detail reaches Daimond.', |
| 796 | 'autoreload.switch_on': 'Buy credits automatically', |
| 797 | 'autoreload.switch_no_card': 'Buy credits automatically (save a card first)', |
| 798 | 'autoreload.when_below': 'When the balance falls below', |
| 799 | 'autoreload.when_below_hint': 'A reload fires the moment a turn takes the balance under this.', |
| 800 | 'autoreload.buy_amount': 'Buy this much', |
| 801 | 'autoreload.buy_amount_hint': 'One top-up. The gateway will not sell more than {max} at a time.', |
| 802 | 'autoreload.monthly_cap': 'Never spend more, per month, than', |
| 803 | 'autoreload.monthly_cap_hint': 'The most auto-reload may spend in a calendar month. Raise it here if you need more.', |
| 804 | 'autoreload.spent': 'Spent this month: {spent}', |
| 805 | 'autoreload.spent_of': 'Spent this month: {spent} of {cap}', |
| 806 | 'autoreload.last_error': 'The last automatic top-up failed: {reason}', |
| 807 | 'autoreload.saving': 'Saving…', |
| 808 | 'autoreload.on_note': 'On. Daimond tops itself up, within your monthly limit.', |
| 809 | 'autoreload.off_note': 'Off. Nothing will be charged.', |
| 810 | |
| 811 | // ── The chat ─────────────────────────────────────────────── |
| 812 | 'chat.no_chat': 'No chat', |
| 813 | 'chat.model_for_chat': 'Model for this chat', |
| 814 | 'chat.select_all': 'Select all', |
| 815 | 'chat.deselect_all': 'Deselect all', |
| 816 | 'chat.fold_selected': 'Fold selected', |
| 817 | 'chat.fold_now': 'Fold', |
| 818 | 'chat.fold_now_help': 'Fold this conversation now, keeping a summary of what came before.', |
| 819 | 'say.more': 'the detail', |
| 820 | 'say.less': 'hide the detail', |
| 821 | 'rail.dupes_trashed': 'Removed {n} duplicate copies of the built-in Diamonds — an old sync made them. They are in the Trash if you want one back.', |
| 822 | 'rail.dupes_kept': 'There are {n} more duplicate built-in Diamonds, but you have worked in them, so nothing was removed. Delete the ones you do not want.', |
| 823 | // A question the model put, answered with one tap. `Chose: ` and `Other: ` are |
| 824 | // NOT here: they are the wire markers the answer travels under, read by the model |
| 825 | // and not by a person, so they are the same in every language on purpose. |
| 826 | 'ask.of': 'Decision {n} of {of}', |
| 827 | 'ask.recommended': 'Recommended', |
| 828 | 'ask.why': 'Why: {why}', |
| 829 | 'ask.silent': 'If you say nothing: {what}', |
| 830 | 'ask.other': 'Something else…', |
| 831 | 'ask.other_ph': 'Answer in your own words', |
| 832 | 'ask.send': 'Send this answer', |
| 833 | 'ask.answered': 'You answered: {what}', |
| 834 | 'say.more_help': 'About {n} tokens, kept on this device. Closed, they are not sent to the model; open the fold and they are.', |
| 835 | 'say.less_help': 'About {n} tokens. While this is open the model sees them too, on every turn until you close it.', |
| 836 | 'wire.role_help': 'Your role prompt. Edit it in prompts/ — this is the part of the system message you own.', |
| 837 | 'wire.safety_help': 'Appended after your edits, every turn. You cannot remove it by editing your role prompt.', |
| 838 | 'wire.tools_help': 'Written from the tools this conversation actually holds, so it cannot promise one that is not there.', |
| 839 | 'wire.machine_help': 'Written from the same fence that enforces it, so the description and the rule cannot disagree.', |
| 840 | 'wire.schemas_help': 'The full JSON schema of every tool, sent on every single request. Usually the largest thing in the payload.', |
| 841 | 'wire.diamond_help': 'Composed for this turn alone: the Diamond\'s own folder, what the paperclip has attached, and its memory — crystal.json — as it stands. The capp beside it is named, never carried.', |
| 842 | 'wire.standing_help': 'Your DAIMOND.md, appended to the system message of every turn in every conversation.', |
| 843 | 'chat.copy_all_aria': 'Copy the conversation', |
| 844 | 'wire.chip': 'Wire', |
| 845 | 'wire.chip_help': 'Show what is actually sent to the model on every turn — the system message, whose each part is, and the tool schemas.', |
| 846 | 'wire.head': 'Sent on every turn, before anything you have said — about {n} tokens', |
| 847 | 'wire.role': 'Role prompt', |
| 848 | 'wire.role_why': 'yours', |
| 849 | 'wire.safety': 'Safety clause', |
| 850 | 'wire.safety_why': 'immovable', |
| 851 | 'wire.tools': 'Tool names', |
| 852 | 'wire.tools_why': 'from the registry', |
| 853 | 'wire.machine': 'This computer', |
| 854 | 'wire.machine_why': 'from the fence', |
| 855 | 'wire.diamond': 'This Diamond', |
| 856 | 'wire.diamond_why': 'this turn only', |
| 857 | 'wire.standing': 'Standing instructions', |
| 858 | 'wire.standing_why': 'yours', |
| 859 | 'wire.schemas': 'Tool schemas ({n})', |
| 860 | 'wire.schemas_why': 'every turn', |
| 861 | 'chat.copy_all': 'Copy', |
| 862 | 'chat.copy_all_help': 'Copy the whole conversation to the clipboard, as plain text.', |
| 863 | 'chat.copy_all_said': 'The conversation was copied to the clipboard.', |
| 864 | 'chat.copy_all_empty': 'There is nothing in this conversation to copy yet.', |
| 865 | 'chat.copy_all_failed': 'Daimond could not reach the clipboard. Select the text and copy it yourself.', |
| 866 | 'chat.steps': 'Steps', |
| 867 | 'chat.concise': 'Concise', |
| 868 | 'chat.concise_help': 'Ask for short answers in this chat. Every message carries the /concise skill, a file in your workspace you can edit.', |
| 869 | 'chat.concise_failed_title': 'Concise is not available', |
| 870 | 'chat.concise_failed': 'Daimond could not write .daimond/skills/concise.md, so the chip would refuse every turn instead of shortening it. Open a workspace folder, or check the Files panel, and try again.', |
| 871 | 'chat.steps_help': 'Show or hide the tool steps in the thread', |
| 872 | 'chat.collapse_help': 'Collapse every answer, leaving what you asked, and pick turns to fold', |
| 873 | 'chat.input_ph': 'Type a message…', |
| 874 | 'chat.send': 'Send', |
| 875 | // The turn indicator: what the three dots at the foot of the thread say while |
| 876 | // a turn runs. It is up from the moment the turn starts until it ends, so |
| 877 | // these have to carry a long silence — a run of tool calls with the steps |
| 878 | // hidden changes nothing else on screen for minutes at a time. {tool} is a |
| 879 | // tool's own name, which the thread shows untranslated; {n} counts the |
| 880 | // tool-call rounds, and is there so something on the line MOVES. |
| 881 | // Nothing here says anything about how long any of it has taken. |
| 882 | 'chat.busy': 'Thinking…', |
| 883 | 'chat.busy_tool': 'Running {tool}, step {n}…', |
| 884 | 'chat.busy_next': 'Step {n} done, thinking…', |
| 885 | // The road went under a tool call, not the far end. Said while the ladder climbs, |
| 886 | // because two minutes of silent retrying looks exactly like a hung turn. |
| 887 | 'chat.busy_road': 'The connection dropped — trying {name} again ({n} of {of})…', |
| 888 | 'chat.busy_writing': 'Writing the answer…', |
| 889 | // One command's own line, when the turn's fence withheld the network from it. |
| 890 | // `src/tools.rs`'s NO_NET_NOTE says the same thing to the model; this is the |
| 891 | // half a person can read. A statement of what happened, not a failure. |
| 892 | 'chat.tool_no_net': 'This ran with no network, because Daimond had read something written by somebody else. Anything in it that tried to fetch, install or clone got nothing. The permissions button gives it back.', |
| 893 | 'chat.answered': 'Daimond answered, {n} words', |
| 894 | 'chat.answer_failed': 'Daimond could not answer', |
| 895 | 'chat.collapse': 'Collapse every answer', |
| 896 | 'chat.jump': 'Jump back to your last message', |
| 897 | 'chat.end': 'Jump to the end of the chat', |
| 898 | 'chat.jump_help': 'Jump back to your last message. Press again to walk back through the ones before it.', |
| 899 | 'chat.end_help': 'Jump to the end of the chat.', |
| 900 | 'chat.new': '+ New chat', |
| 901 | // Messages typed while an answer is still arriving. They are held, shown, and |
| 902 | // sent one turn each once the answer is finished. |
| 903 | // Short on purpose: a phone's composer is ~120px wide, and a longer line wraps |
| 904 | // to two clipped rows. What happens to a queued message is said in full by the |
| 905 | // line above the bubbles (chat.queue_help), at the moment it matters. |
| 906 | 'chat.queue_ph': 'Type the next message…', |
| 907 | 'chat.queue_help': 'Waiting. Sent when this answer finishes', |
| 908 | 'chat.queued_pending': 'Not sent yet. Click to edit it.', |
| 909 | 'chat.queue_cancel': 'Do not send this', |
| 910 | 'chat.queue_returned': 'That turn did not finish, so what you queued is back in the box, unsent.', |
| 911 | // Speaking into a turn that is already running. A turn is a round of requests, |
| 912 | // and the message list is rebuilt between them, so a message typed mid-turn can |
| 913 | // go to the model AT THAT SEAM rather than after the whole turn is over. Two |
| 914 | // states, and the difference between them is what these say: waiting (typed, not |
| 915 | // yet delivered) and delivered (in the conversation, and the model has it). |
| 916 | // The heading is honest about the limit -- an answer that writes prose straight |
| 917 | // through has no seam in it, so a correction can still end up waiting until it |
| 918 | // finishes -- because a promise of "shortly" that a quiet turn cannot keep is |
| 919 | // worse than no promise. |
| 920 | 'chat.send_into': 'Send into this answer', |
| 921 | 'chat.interject_help': 'Waiting. Goes in at the model’s next step, or when this answer finishes', |
| 922 | 'chat.interject_pending': 'Not delivered yet. Click to edit it.', |
| 923 | 'chat.interjected': 'You cut in here', |
| 924 | 'chat.interjected_help': 'Said into the turn at this point. The model had it from here on.', |
| 925 | 'chat.thinking': 'Thinking', |
| 926 | 'chat.compacted': 'Conversation folded', |
| 927 | 'chat.compacted_help': 'Daimond replaced the earlier part with a summary so the conversation fits the model’s context window.', |
| 928 | 'chat.fold_boundary': 'The model no longer has what is above this line as it was written — it has the summary in this note instead, plus the {kept} most recent messages. Everything above stays on your screen, so you can still read it and quote from it.', |
| 929 | 'chat.shortened': 'Answers shortened for the model', |
| 930 | 'chat.shortened_help': 'Daimond sent the older answers shortened so the conversation fits the model’s context window. Every word of them stays on your screen and in your record.', |
| 931 | 'chat.working_more': '+{n} characters', |
| 932 | // The same thing said on the chat's TILE, for a queue left on a conversation |
| 933 | // the user has walked away from: the bubbles are only drawn in the chat on |
| 934 | // screen, and money about to be spent should not depend on remembering. |
| 935 | 'chat.queue_badge.one': '{n} waiting', |
| 936 | 'chat.queue_badge.other': '{n} waiting', |
| 937 | 'chat.queue_badge_help.one': 'One message is waiting on this chat. Open it and it is sent as its own turn.', |
| 938 | 'chat.queue_badge_help.other': '{n} messages are waiting on this chat. Open it and they are sent, one turn each.', |
| 939 | |
| 940 | // ── How the turn ended, and a template of a Diamond ──────── |
| 941 | // |
| 942 | // FURNITURE, in the register of a timestamp. `dev/CONTRACT_CLAIMS.md` §3 binds |
| 943 | // every word here: no word may claim a fault, because the line closes EVERY |
| 944 | // turn that held tools and an app appending a warning to each of them teaches |
| 945 | // its reader to skip the one that mattered. "Answered · no tools used" is a |
| 946 | // statement of what happened, not an accusation — the reader decides whether |
| 947 | // the model promised otherwise. |
| 948 | // i18n-family: end.how_ = answered stopped capped silent failed |
| 949 | 'end.how_answered': 'Answered', |
| 950 | 'end.how_stopped': 'Stopped', |
| 951 | 'end.how_capped': 'Step limit reached', |
| 952 | 'end.how_silent': 'Ended without saying anything', |
| 953 | 'end.how_failed': 'Ended on an error', |
| 954 | 'end.no_calls': 'no tools used', |
| 955 | 'end.calls.one': '{n} tool call', |
| 956 | 'end.calls.other': '{n} tool calls', |
| 957 | 'end.refused.one': '{n} refused', |
| 958 | 'end.refused.other': '{n} refused', |
| 959 | 'end.broke.one': '{n} failed', |
| 960 | 'end.broke.other': '{n} failed', |
| 961 | 'end.missing': 'not written: {paths}', |
| 962 | 'end.help': 'How this turn ended. Tools available: {offered}. Requests sent: {rounds}. Tool calls made: {calls}.', |
| 963 | |
| 964 | // A template is a Diamond's SHAPE without its contents. Two facts run through |
| 965 | // every string below because neither is guessable and both change what a |
| 966 | // person does: opening one mints a NEW Diamond and can never write over an |
| 967 | // existing one, and triggered actions are deliberately not carried, because a |
| 968 | // trigger fires with nobody pressing anything. |
| 969 | 'tmpl.section': 'Template', |
| 970 | 'tmpl.dlg_help': 'A template carries this Diamond’s shape — the page it draws through, its automation, and whatever a capp keeps beside itself — and none of what it has recorded. Triggered actions are left out on purpose: a trigger fires with nobody pressing anything, and one carried across would start work on somebody else’s machine because they opened a file. Whoever opens it gets a NEW Diamond; nothing of theirs is written over.', |
| 971 | 'tmpl.with_conversation': 'Include what it has recorded', |
| 972 | 'tmpl.with_conversation_help': 'Carries everything instead: the memory, the kept conversation and a capp’s own entries. Still a template, so it still opens as a new Diamond.', |
| 973 | 'tmpl.save': 'Save as a template…', |
| 974 | 'tmpl.saving': 'Making the template…', |
| 975 | 'tmpl.saved': 'Saved as {file}. Anybody you give it to opens it as a Diamond of their own, from the Share view of the Social panel.', |
| 976 | 'tmpl.panel_head': 'Open a template', |
| 977 | 'tmpl.panel_help': 'A template is a Diamond’s shape without its contents: the page it draws through and its automation, and none of what it has recorded. It opens as a NEW Diamond and can never write over one you already have. Triggered actions are never carried, because a trigger fires with nobody pressing anything.', |
| 978 | 'tmpl.panel_open': 'Open a template file…', |
| 979 | 'tmpl.opened': 'Opened as a new Diamond, “{name}”. {n} file(s) arrived.', |
| 980 | 'tmpl.unnamed': 'this template', |
| 981 | // The consent question. It says what it is (a page — a program), what cannot |
| 982 | // be told about it (a template carries no signature and no name, unlike a |
| 983 | // share), which files, and what declining does — which here is nothing at all, |
| 984 | // because the engine's door has no half-landing behind it. |
| 985 | 'tmpl.code_title': 'This template contains code', |
| 986 | 'tmpl.code_body': '“{name}” includes a page: a program written by somebody else, which Daimond will run when you open it. A template carries no signature and nobody’s name, so nothing here can tell you where it came from — only the person who gave you the file can.\n\nWhat would be added: {files}\n\nIt opens as a NEW Diamond and can never write over one you already have. Declining writes nothing at all.', |
| 987 | 'tmpl.code_ok': 'Accept the page and open it', |
| 988 | 'tmpl.declined': 'The page was not accepted, so nothing has been opened.', |
| 989 | 'tmpl.err_empty': 'That file is empty, so there is nothing to open.', |
| 990 | 'tmpl.err_not_template': 'That file is not a Daimond template.', |
| 991 | 'tmpl.err_file_huge': 'That file is {size}, which is larger than any template can be, so it was not opened.', |
| 992 | 'tmpl.err_no_file': 'No file was chosen, so nothing was opened.', |
| 993 | 'tmpl.err_nothing': 'There is nothing to save: that Diamond made an empty template.', |
| 994 | 'tmpl.err_no_door': 'This build can read a template but has nowhere to open one.', |
| 995 | |
| 996 | // ── The Web panel ────────────────────────────────────────── |
| 997 | 'web.who_drives': 'Who is driving', |
| 998 | 'web.back': 'Back', |
| 999 | 'web.reload': 'Reload', |
| 1000 | 'web.pop_out': 'Open in a real tab', |
| 1001 | // The name of the pause control in the panel header, so `pause.act_pause` |
| 1002 | // reads "Pause Web access". It governs `root/web`, which reaches further |
| 1003 | // than this panel — a page fetched for a chat is stopped by it too — so it |
| 1004 | // is not called "This panel". |
| 1005 | 'web.pause': 'Web access', |
| 1006 | 'web.blind_title': "You're driving. I'm not watching.", |
| 1007 | 'web.blind_note': 'Daimond has stopped reading this page. No text, no picture, no keystrokes. Sign in, then click <b>Resume Daimond</b> in the browser tab to hand the wheel back. That button is in the tab, so this page can never take the wheel from you.', |
| 1008 | 'web.resumed': 'I’ve resumed, check', |
| 1009 | // {where} is what the extension said Daimond stopped at, e.g. a sign-in page. |
| 1010 | 'web.blind_title_at': 'You’re driving. I stopped at {where}.', |
| 1011 | // The header: what is on screen, and who has the wheel. |
| 1012 | 'web.guide': 'Guide', |
| 1013 | 'web.guide_title': 'Daimond’s user guide', |
| 1014 | 'web.panel_for': 'The Web panel', |
| 1015 | 'web.who_you': 'You', |
| 1016 | 'web.who_daimond': 'Daimond', |
| 1017 | 'web.who_ready': 'Ready', |
| 1018 | 'web.who_view_only': 'View only', |
| 1019 | 'web.who_you_help': 'You are driving. Daimond is not watching this page.', |
| 1020 | 'web.who_daimond_help': 'Daimond is driving. You can take the wheel at any time.', |
| 1021 | 'web.who_ready_help': 'Daimond Hands is installed.', |
| 1022 | 'web.who_view_only_help': 'This page can be shown, but not operated. Install Daimond Hands to drive it.', |
| 1023 | // Opening a site through the extension. {host} is a hostname, already escaped; |
| 1024 | // these are placed as markup, so keep the tags and add none. |
| 1025 | 'web.opening': '<b>Opening {host}…</b><br>A <b>Daimond Hands</b> window opens in front. Approve the site there, then confirm once in Chrome. Both happen only the first time for a site.', |
| 1026 | 'web.not_approved': '<b>{host} was not approved.</b><br>', |
| 1027 | 'web.approval_closed': 'The approval window was closed, or you said no. To allow it, ask for the site again and click <b>Allow, then confirm in Chrome</b>. The window opens in front of this one, and the <b>Daimond Hands</b> icon holds the question until you answer it.', |
| 1028 | // An answer that arrived after Daimond had stopped waiting: the site was |
| 1029 | // approved late, or refused late. The first sits above the driving note. |
| 1030 | 'web.approved_late': '<b>{host} was approved.</b> Your answer arrived after Daimond gave up waiting, so the panel said otherwise for a moment. The page is open now.', |
| 1031 | 'web.answer_late': 'Your answer came after Daimond had stopped waiting. ', |
| 1032 | 'web.driving_tab': '<b>Daimond is driving {host}</b> in a browser tab. Watch it there, or pull a live picture into this panel.', |
| 1033 | 'web.show_live': 'Show live view here', |
| 1034 | 'web.real_tab': 'The page opens as a real browser tab, which is what carries your own sign-ins, so this panel only shows a picture of it.', |
| 1035 | 'web.mirror_wait': 'Waiting for the first picture of that tab. If Chrome has put a permission window in front, it wants your answer.', |
| 1036 | 'web.mirror_silent': 'Nothing has come back from Daimond Hands for a while, so there is no live picture. The tab itself is still open, and is still the page.', |
| 1037 | 'web.mirror_refused': 'This tab cannot be photographed, so there is no live picture. Watch the tab itself, or ask for the picture again.', |
| 1038 | // A site that refuses to be framed, which is most of them. |
| 1039 | 'web.blocked': '<b>{host}</b> will not display inside another page. Most sites block this as protection against clickjacking; it is not a fault.', |
| 1040 | 'web.blocked_hands': 'Daimond Hands can drive it in a real tab.', |
| 1041 | 'web.blocked_install': 'Install Daimond Hands to drive it live.', |
| 1042 | 'web.open_new_tab': 'Open in a new tab', |
| 1043 | 'web.read_as_text': 'Read it as text', |
| 1044 | 'web.reading': 'Reading…', |
| 1045 | 'web.read_failed': 'That page could not be read: {reason}', |
| 1046 | 'web.readonly_badge': 'Read-only copy, not the live site. Do not sign in here.', |
| 1047 | |
| 1048 | // ── Compose ──────────────────────────────────────────────── |
| 1049 | 'compose.from': 'From', |
| 1050 | 'compose.to': 'To', |
| 1051 | 'compose.cc': 'Cc', |
| 1052 | 'compose.subject': 'Subject', |
| 1053 | 'compose.optional': 'Optional', |
| 1054 | 'compose.body_ph': 'Write your message.', |
| 1055 | 'compose.send': 'Send', |
| 1056 | 'compose.save_draft': 'Save draft', |
| 1057 | 'compose.attach': 'Attach…', |
| 1058 | 'compose.discard': 'Discard', |
| 1059 | |
| 1060 | // ── Agents ───────────────────────────────────────────────── |
| 1061 | 'agents.control_all': 'Control all agents', |
| 1062 | 'agents.pause_all': 'Pause all agents', |
| 1063 | 'agents.resume_all': 'Resume all paused agents', |
| 1064 | 'agents.stop_all': 'Stop all agents', |
| 1065 | 'agents.clear_finished': 'Clear finished agents', |
| 1066 | 'agent.model_vision': 'This task names an image, so it runs on the Diamond’s image model, {model}.', |
| 1067 | 'agent.model_vision_fallback': 'This task names an image, but no image model is set for this Diamond, so it runs on the text model, {model}.', |
| 1068 | 'agent.model_rerouted': '{from} cannot be shown pictures, so this worker moved to {to}.', |
| 1069 | 'agent.model_blind': '{model} cannot be shown pictures, so the picture was left out.', |
| 1070 | 'agent.model_blind_none': '{model} cannot be shown pictures and this Diamond has no image model set.', |
| 1071 | // ── Pending ──────────────────────────────────────────────── |
| 1072 | 'pending.empty': 'Nothing waiting on you.', |
| 1073 | 'pending.sort': 'Sort', |
| 1074 | 'pending.by_priority': 'By priority', |
| 1075 | 'pending.by_newest': 'Newest first', |
| 1076 | 'pending.by_oldest': 'Oldest first', |
| 1077 | 'pending.priority': 'Priority', |
| 1078 | // i18n-family: pending.prio_ = high normal low |
| 1079 | 'pending.prio_high': 'High', |
| 1080 | 'pending.prio_normal': 'Normal', |
| 1081 | 'pending.prio_low': 'Low', |
| 1082 | 'pending.no_detail': 'No further detail was given.', |
| 1083 | 'pending.execute': 'Do it', |
| 1084 | 'pending.execute_named': 'Do it: {what}', |
| 1085 | 'pending.discuss': 'Discuss it first', |
| 1086 | 'pending.discuss_named': 'Discuss first: {what}', |
| 1087 | 'pending.cancel': 'Drop it', |
| 1088 | 'pending.cancel_named': 'Drop it: {what}', |
| 1089 | 'pending.noted': 'Noted, and taken off the list.', |
| 1090 | 'pending.opened': 'Opened for sending. It stays here until it is sent.', |
| 1091 | 'pending.diamond_gone': 'The Diamond that raised this is gone, so there is nobody to discuss it with.', |
| 1092 | 'pending.discuss_prompt': 'We need to discuss this further before I approve it: “{headline}”', |
| 1093 | |
| 1094 | // A dispatched agent's request for permission, raised here because there was |
| 1095 | // nobody at the screen to put it to. The agent is holding its work open until |
| 1096 | // one of the three answers arrives. |
| 1097 | 'pending.consent.click_head': 'An agent wants to click something on {host}', |
| 1098 | 'pending.consent.type_head': 'An agent wants to send text to {host}', |
| 1099 | 'pending.consent.reach_head': 'An agent wants to reach {host}', |
| 1100 | 'pending.consent.search_head': 'An agent wants to search the web', |
| 1101 | 'pending.consent.why': 'It asked while this page was not in front of you, so the question is waiting here instead of on a dialog nobody would have seen.', |
| 1102 | 'pending.consent.agent': 'The agent is {name}. Its task: {task}', |
| 1103 | 'pending.consent.waiting': 'The agent is still waiting for this answer.', |
| 1104 | 'pending.consent.expired': 'The agent that asked has gone — this was raised before the app last reloaded. Saying yes now could not let the act happen.', |
| 1105 | 'pending.consent.allowed': 'Allowed. The agent has been told to go ahead.', |
| 1106 | 'pending.consent.gone': 'Nothing is waiting on this any more, so there was nothing to allow.', |
| 1107 | 'pending.consent.nochat': 'Declined. There is no conversation to take it to: two agents were running, so Daimond cannot tell which one asked.', |
| 1108 | |
| 1109 | // The agent tile's own controls, its state word and the tally above the |
| 1110 | // list. All of these were hardcoded English in a shipped panel until |
| 1111 | // 2026-08-12, and `dev/i18ncheck.mjs` could not see them: it compares key |
| 1112 | // sets, so a string with no key at all is invisible to it by construction. |
| 1113 | 'agents.act_pause': 'Pause', |
| 1114 | 'agents.act_pause_help': 'Hang up this agent, keeping its work so far; resume it later.', |
| 1115 | 'agents.act_stop': 'Stop', |
| 1116 | 'agents.act_stop_help': 'Stop this agent for good. It keeps whatever it managed to do.', |
| 1117 | 'agents.act_resume': 'Resume', |
| 1118 | 'agents.act_resume_help': 'Continue this agent from where it left off.', |
| 1119 | 'agents.act_discard_help': 'Discard this paused agent.', |
| 1120 | 'agents.tally_running': '{n} running', |
| 1121 | 'agents.tally_paused': '{n} paused', |
| 1122 | 'agents.tally_queued': '{n} queued', |
| 1123 | 'agents.live': '{n} live', |
| 1124 | 'agents.dropped.one': '1 older run is not kept.', |
| 1125 | 'agents.dropped.other': '{n} older runs are not kept.', |
| 1126 | // The state word on an agent's pill, keyed by the engine's own status token. |
| 1127 | // Built by concatenation at daimond.js's `tOr('agents.status_' + run.status, |
| 1128 | // run.status)`, so a status with no key here reaches the screen as the raw |
| 1129 | // internal word -- which is what it used to do for all seven. |
| 1130 | // i18n-family: agents.status_ = queued running paused done error stopped interrupted |
| 1131 | 'agents.status_queued': 'queued', |
| 1132 | 'agents.status_running': 'running', |
| 1133 | 'agents.status_paused': 'paused', |
| 1134 | 'agents.status_done': 'done', |
| 1135 | 'agents.status_error': 'failed', |
| 1136 | 'agents.status_stopped': 'stopped', |
| 1137 | 'agents.status_interrupted': 'cut off', |
| 1138 | |
| 1139 | 'agents.search': 'Search agents', |
| 1140 | 'agents.search_help': 'Search agents by name, task, Diamond or tag', |
| 1141 | |
| 1142 | // ── Email ────────────────────────────────────────────────── |
| 1143 | 'mail.write': 'Write a message', |
| 1144 | 'mail.add_mailbox': 'Add a mailbox', |
| 1145 | 'mail.sync_now': 'Sync now', |
| 1146 | |
| 1147 | // The pitch, shown while Email is not unlocked on this account. The first |
| 1148 | // two carry markup and are placed as markup; keep the tags and add none. |
| 1149 | 'mail.pitch.head': '<b>Daimond can read your mail.</b> Your inbox lands in the workspace as ordinary files, so every agent can read, search and work from it.', |
| 1150 | 'mail.pitch.fine': 'Email is part of Pro — one payment, and five years of it — alongside cross-device sync and cloud storage. Covers {cap} mailboxes. Sending and fetching are metered against credits, like inference. No subscription, and nothing renews.', |
| 1151 | 'mail.pitch.privacy': 'Daimond’s gateway makes the connection and forgets your password. No mail is ever stored on our side.', |
| 1152 | 'mail.pitch.unknown': 'The account service is not reachable, so Daimond cannot tell whether Email is unlocked here.', |
| 1153 | 'mail.pro_pitch': 'Email is part of Pro. Buy five years of Pro to turn it on, along with sync and cloud storage.', |
| 1154 | |
| 1155 | // The mailbox list. |
| 1156 | 'mail.remove_mailbox': 'Remove this mailbox', |
| 1157 | 'mail.remove_mailbox_named': 'Remove the mailbox {address}', |
| 1158 | 'mail.no_mailbox': 'No mailbox yet. Press <b>+</b> to add one.', |
| 1159 | 'mail.remove.title': 'Remove {address}?', |
| 1160 | 'mail.remove.body': 'The mail already synced stays in the workspace. The mailbox frees a seat on your unlock.', |
| 1161 | 'mail.remove.ok': 'Remove', |
| 1162 | 'mail.ago.never': 'never', |
| 1163 | 'mail.ago.just_now': 'just now', |
| 1164 | 'mail.ago.mins': '{n}m ago', |
| 1165 | 'mail.ago.hours': '{n}h ago', |
| 1166 | 'mail.ago.days': '{n}d ago', |
| 1167 | |
| 1168 | // Folders. A server may name its folders in its own language, so the name |
| 1169 | // shown is the ROLE's name wherever the server declares one (RFC 6154) and |
| 1170 | // the server's own spelling everywhere else. |
| 1171 | 'mail.folders': 'Folders', |
| 1172 | 'mail.folders_loading': 'Looking for folders…', |
| 1173 | 'mail.refreshed_held': '{done} folders refreshed in {boxes} mailboxes; {held} held by a pause.', |
| 1174 | 'mail.refreshed': '{done} folders refreshed in {boxes} mailboxes.', |
| 1175 | 'mail.count.never': 'Not fetched yet, so there is no count.', |
| 1176 | 'mail.count.asat': '{n} messages, as at {when}.', |
| 1177 | 'mail.count.more': '{n} more were waiting on the server then.', |
| 1178 | 'mail.settings': 'Mailbox settings', |
| 1179 | 'mail.settings_named': 'Settings for {address}', |
| 1180 | 'mail.all_mailboxes': 'All mailboxes', |
| 1181 | 'mail.refresh_all': 'Refresh all {folders} folders in {boxes} mailboxes', |
| 1182 | // How often one folder refreshes, keyed by the interval in SECONDS. Built by |
| 1183 | // concatenation in mail.js, which falls through to `mail.every.secs` for an |
| 1184 | // interval no key names -- so the family and the fallback key are declared |
| 1185 | // together. |
| 1186 | // i18n-family: mail.every. = 0 300 900 1800 3600 14400 43200 86400 secs |
| 1187 | 'mail.every.0': 'Manual only', |
| 1188 | 'mail.every.300': 'Every 5 minutes', |
| 1189 | 'mail.every.900': 'Every 15 minutes', |
| 1190 | 'mail.every.1800': 'Every 30 minutes', |
| 1191 | 'mail.every.3600': 'Every hour', |
| 1192 | 'mail.every.14400': 'Every 4 hours', |
| 1193 | 'mail.every.43200': 'Every 12 hours', |
| 1194 | 'mail.every.86400': 'Once a day', |
| 1195 | 'mail.every.secs': 'Every {n} seconds', |
| 1196 | 'mail.every_for': 'How often {folder} refreshes itself', |
| 1197 | 'mail.cfg.head': 'How often each folder goes and looks, and which of them may. Every refresh costs credits, so nothing polls until you say so.', |
| 1198 | 'mail.cfg.self': 'Holds every folder below.', |
| 1199 | 'mail.cfg.title': 'Settings for {address}', |
| 1200 | 'mail.folders_err': 'The folder list could not be fetched: {reason}', |
| 1201 | // The role comes from the mail server, but the name is only built where the |
| 1202 | // role is one of ours: `mail.js:1754` fences it on `ROLE_ORDER`, and a role |
| 1203 | // outside that list keeps the server's own spelling instead. So the set is |
| 1204 | // this app's after all, and closed. |
| 1205 | // i18n-family: mail.folder. = inbox sent drafts trash junk archive flagged all |
| 1206 | 'mail.folder.inbox': 'Inbox', |
| 1207 | 'mail.folder.sent': 'Sent', |
| 1208 | 'mail.folder.drafts': 'Drafts', |
| 1209 | 'mail.folder.trash': 'Bin', |
| 1210 | 'mail.folder.junk': 'Spam', |
| 1211 | 'mail.folder.archive': 'Archive', |
| 1212 | 'mail.folder.all': 'All mail', |
| 1213 | 'mail.folder.flagged': 'Starred', |
| 1214 | |
| 1215 | // The message list. |
| 1216 | 'mail.drafts_head': 'Drafts · {n}', |
| 1217 | 'mail.no_subject': '(no subject)', |
| 1218 | 'mail.no_recipient': '(no recipient)', |
| 1219 | 'mail.unknown_sender': '(unknown)', |
| 1220 | 'mail.nothing_yet': 'Nothing here yet. Press <b>⟳</b> to sync.', |
| 1221 | |
| 1222 | // Syncing. {address} is a mailbox, {folder} the folder inside it. |
| 1223 | 'mail.note.syncing': 'Syncing {folder} in {address}…', |
| 1224 | 'mail.note.fetching_older': 'Fetching older mail from {folder} in {address}…', |
| 1225 | 'mail.note.rebuilt': 'The mailbox was rebuilt on the server; resyncing from the start…', |
| 1226 | 'mail.note.up_to_date': 'Up to date.', |
| 1227 | 'mail.note.no_older': 'No older mail left to fetch.', |
| 1228 | 'mail.note.new.one': '{n} new message', |
| 1229 | 'mail.note.new.other': '{n} new messages', |
| 1230 | 'mail.note.older.one': '{n} older message', |
| 1231 | 'mail.note.older.other': '{n} older messages', |
| 1232 | 'mail.note.still_older.one': '{n} older still on the server', |
| 1233 | 'mail.note.still_older.other': '{n} older still on the server', |
| 1234 | |
| 1235 | // The foot of the list: what the cap held back. |
| 1236 | 'mail.more.note.one': '{n} older message still on the server. Daimond fetches the newest {batch} at a time, so a whole mailbox is never pulled down at once.', |
| 1237 | 'mail.more.note.other': '{n} older messages still on the server. Daimond fetches the newest {batch} at a time, so a whole mailbox is never pulled down at once.', |
| 1238 | 'mail.more.next': 'Fetch next {n}', |
| 1239 | 'mail.more.stop': 'Stop', |
| 1240 | 'mail.more.all': 'Fetch all', |
| 1241 | |
| 1242 | // Walking a whole mailbox down. {count} is `mail.all.count`, already worded. |
| 1243 | 'mail.all.title': 'Fetch all {n} remaining messages?', |
| 1244 | 'mail.all.body': 'They arrive in batches of {batch} and are written into the workspace, so a large mailbox takes a while and uses disk. Syncing is metered against your credits by the megabyte. Stop part-way and what has arrived stays.', |
| 1245 | 'mail.all.ok': 'Fetch all', |
| 1246 | 'mail.all.progress': 'Fetched {got} of {total}…', |
| 1247 | 'mail.all.count.one': '{n} message', |
| 1248 | 'mail.all.count.other': '{n} messages', |
| 1249 | 'mail.all.done': 'Done. {count} fetched.', |
| 1250 | 'mail.all.done_left': 'Done. {count} fetched, {left} still on the server.', |
| 1251 | 'mail.all.stopped': 'Stopped. {count} fetched.', |
| 1252 | 'mail.all.stopped_left': 'Stopped. {count} fetched, {left} still on the server.', |
| 1253 | |
| 1254 | // Quoting and forwarding. The separator is what the reader sees; the header |
| 1255 | // names under it (From:, Date:, Subject:, To:) are the message's own and |
| 1256 | // stay spelled as headers are. "Re:" and "Fwd:" are likewise left alone — |
| 1257 | // they are what a receiving client threads on. |
| 1258 | // Two sentences, not one with a fallback phrase dropped into the date slot: |
| 1259 | // a quote whose date cannot be read gets a sentence with no date in it. |
| 1260 | 'mail.quote.head': 'On {date}, {who} wrote:', |
| 1261 | 'mail.quote.head_undated': '{who} wrote:', |
| 1262 | 'mail.quote.they': 'they', |
| 1263 | 'mail.fwd.sep': '---------- Forwarded message ----------', |
| 1264 | |
| 1265 | // Adding a mailbox. |
| 1266 | 'mail.add.title': 'Add a mailbox', |
| 1267 | 'mail.add.lead': 'Daimond’s gateway connects to your mail server, hands the messages back, and forgets your password. The password is encrypted on this device under your passphrase.', |
| 1268 | 'mail.add.ok': 'Add and sync', |
| 1269 | 'mail.add.address': 'Email address', |
| 1270 | 'mail.add.password': 'App password', |
| 1271 | 'mail.add.password_ph': 'The app password from your provider', |
| 1272 | 'mail.add.imap': 'IMAP server', |
| 1273 | 'mail.add.smtp': 'SMTP server', |
| 1274 | 'mail.add.port': 'Port', |
| 1275 | 'mail.add.guess': 'Daimond will need this provider’s server names (often imap.{domain} for reading, smtp.{domain} for sending).', |
| 1276 | 'mail.add.err_address': 'That is not an email address.', |
| 1277 | 'mail.add.err_password': 'The app password is required.', |
| 1278 | 'mail.add.err_imap': 'Daimond needs the IMAP server name.', |
| 1279 | 'mail.add.err_port': 'IMAP runs on port 993 (TLS) or 143.', |
| 1280 | 'mail.add.err_smtp': 'Daimond needs the SMTP server name to send from this mailbox.', |
| 1281 | 'mail.add.err_smtp_port': 'Mail is submitted on port 587 or 465.', |
| 1282 | |
| 1283 | // Provider guidance, shown under the address as soon as the domain is |
| 1284 | // recognised. These carry markup. "App Password" is the provider's own name |
| 1285 | // for the thing, so keep whatever the provider calls it in that language. |
| 1286 | // i18n-indirect: daimond.js p.note = mail.preset.gmail mail.preset.gmail_short mail.preset.outlook mail.preset.yahoo mail.preset.icloud mail.preset.fastmail |
| 1287 | // i18n-indirect: daimond.js unreachable[dom] = mail.unreachable.proton mail.unreachable.tuta |
| 1288 | 'mail.preset.gmail': 'Gmail needs an <b>App Password</b> (Google Account → Security → 2-Step Verification → App passwords), not your Google password.', |
| 1289 | 'mail.preset.gmail_short': 'Gmail needs an <b>App Password</b>, not your Google password.', |
| 1290 | 'mail.preset.outlook': 'Outlook needs an <b>app password</b> if two-step verification is on.', |
| 1291 | 'mail.preset.yahoo': 'Yahoo requires an <b>app password</b> generated in Account Security.', |
| 1292 | 'mail.preset.icloud': 'iCloud requires an <b>app-specific password</b>.', |
| 1293 | 'mail.preset.fastmail': 'Fastmail requires an app password with IMAP access.', |
| 1294 | 'mail.unreachable.proton': 'Proton mail is only reachable through the Proton Bridge running on your own machine, which Daimond’s gateway cannot connect to.', |
| 1295 | 'mail.unreachable.tuta': 'Tuta offers no IMAP, so no mail client can read it, Daimond included.', |
| 1296 | |
| 1297 | // What can go wrong. |
| 1298 | 'mail.err.unlock_first': 'Unlock Daimond first. The mail password is encrypted under your passphrase.', |
| 1299 | 'mail.err.draft_needs_mailbox': 'A draft needs a mailbox to be from.', |
| 1300 | 'mail.err.draft_unreadable': 'That draft could not be read.', |
| 1301 | 'mail.err.msg_unreadable': 'That message could not be read.', |
| 1302 | 'mail.err.send_from_added': 'Send from a mailbox you have added and synced.', |
| 1303 | 'mail.err.no_recipients': 'A message with no recipients cannot be sent.', |
| 1304 | 'mail.err.add_mailbox_first': 'Add a mailbox before writing a message.', |
| 1305 | 'mail.err.service_unavailable': 'The account service is unavailable.', |
| 1306 | 'mail.err.service_unreachable': 'Could not reach the Daimond account service. Try again shortly.', |
| 1307 | 'mail.err.cap.one': 'This unlock covers {n} mailbox. Remove one to add another.', |
| 1308 | 'mail.err.cap.other': 'This unlock covers {n} mailboxes. Remove one to add another.', |
| 1309 | |
| 1310 | 'mail.err.protocol_pending': 'Daimond cannot fetch mail yet: the private tunnel is finished and the mail protocol is not wired to it. Nothing was sent, and your password never left this device.', |
| 1311 | |
| 1312 | // ── The tunnel ───────────────────────────────────────────── |
| 1313 | // TLS terminates in the browser, so these are the only words anybody gets about |
| 1314 | // a connection the gateway cannot see into. Each names the repair, because a |
| 1315 | // refusal a reader cannot act on is a refusal they will simply retry. |
| 1316 | 'mail.tunnel.close.auth': 'Daimond signed this device out, so the mail connection was refused. Unlock Daimond and fetch again.', |
| 1317 | 'mail.tunnel.close.pro': 'Email is part of Pro, and this account has not unlocked it. Buy Pro under Credits and mail opens.', |
| 1318 | 'mail.tunnel.close.host': 'Daimond will only reach a mail server this account has bound, and {host} is not one of them. Add the mailbox again — Daimond binds its servers as it does so — then fetch.', |
| 1319 | 'mail.tunnel.close.port': 'Mail connects on ports 993, 143, 465 and 587, and {port} is none of them. Correct the port in the mailbox’s settings.', |
| 1320 | 'mail.tunnel.close.credits': 'Your credits ran out part way through. Top up under Credits and fetch again; whatever already came down is on this device.', |
| 1321 | 'mail.tunnel.close.concurrent': 'This account already holds four mail connections. Let another mailbox finish, or close another Daimond tab, then fetch again.', |
| 1322 | 'mail.tunnel.close.unresolved': '{host} does not resolve to an address Daimond may dial. Check the server name in the mailbox’s settings — a typo reads exactly like this — or whether this network’s DNS can see it.', |
| 1323 | 'mail.tunnel.close.toobig': 'The mail connection sent more at once than the gateway carries, so it was closed. Fetch again.', |
| 1324 | 'mail.tunnel.close.done': 'The mail server ended the connection before the fetch had finished. Fetch again; whatever came down is on this device.', |
| 1325 | 'mail.tunnel.close.unreachable': '{host} could not be reached. Check the server name in the mailbox’s settings, or try again shortly.', |
| 1326 | 'mail.tunnel.close.no_socket': 'The mail connection could not be opened at all. Check that you are online, then fetch again.', |
| 1327 | 'mail.tunnel.close.idle': 'The mail server stopped answering, so the connection was closed. Fetch again to carry on.', |
| 1328 | 'mail.tunnel.close.expired': 'That was taking too long, so the mail connection was closed. Fetch again and it carries on from where it stopped.', |
| 1329 | 'mail.tunnel.close.other': 'The mail connection closed unexpectedly (code {code}). Fetch again.', |
| 1330 | 'mail.tunnel.err.no_server': 'That mailbox has no server or port set. Open its settings and fill them in.', |
| 1331 | 'mail.tunnel.err.no_client': 'Daimond could not start an encrypted mail connection — {reason}', |
| 1332 | 'mail.tunnel.err.slow': '{host} did not finish an encrypted handshake within {secs} seconds. Try again shortly.', |
| 1333 | 'mail.tunnel.err.no_reply': '{host} did not answer the {what} step within {secs} seconds. Fetch again.', |
| 1334 | 'mail.tunnel.err.cert': 'Daimond could not verify {host}’s certificate ({fault}), so it sent nothing. Your password stays on this device until the server can prove who it is.', |
| 1335 | 'mail.tunnel.err.cert_name': '{host} offered a certificate issued for a different server ({fault}), so Daimond sent nothing. Check the server name in the mailbox’s settings.', |
| 1336 | 'mail.tunnel.err.cert_expired': '{host} offered an expired certificate ({fault}), so Daimond sent nothing. If this machine’s clock is wrong, correct it and fetch again.', |
| 1337 | 'mail.tunnel.err.cert_issuer': '{host} offered a certificate from an issuer Daimond does not trust ({fault}), so it sent nothing. A self-signed certificate looks like this, and so does an intercepted connection.', |
| 1338 | 'mail.tunnel.err.starttls': 'The encrypted channel to {host} could not be started — {reason}', |
| 1339 | 'mail.tunnel.err.starttls_early': '{host} sent something before the encrypted channel was up, so Daimond dropped the connection rather than trust it. Nothing was sent.', |
| 1340 | |
| 1341 | // ── Workspace files ──────────────────────────────────────── |
| 1342 | 'work.new_file': 'New file', |
| 1343 | 'work.new_folder': 'New folder', |
| 1344 | 'work.upload': 'Upload files', |
| 1345 | 'work.parent': 'Parent folder', |
| 1346 | 'work.filter_ph': 'Filter…', |
| 1347 | 'work.breadcrumb': 'Folder path', |
| 1348 | |
| 1349 | // ── The phone shell ──────────────────────────────────────── |
| 1350 | 'sheet.panel': 'Panel', |
| 1351 | 'sheet.ask': 'Ask', |
| 1352 | 'sheet.ask_ph': 'Ask about this…', |
| 1353 | // {thing} is a panel's name, lower-cased by the caller. A language where |
| 1354 | // that reads badly should rewrite the sentence around it. |
| 1355 | 'sheet.ask_about': 'Ask about this {thing}…', |
| 1356 | |
| 1357 | // ── Identity: creating and unlocking ─────────────────────── |
| 1358 | 'identity.your_name': 'Your name', |
| 1359 | 'identity.passphrase': 'Passphrase', |
| 1360 | 'identity.confirm_passphrase': 'Confirm passphrase', |
| 1361 | 'identity.show_passphrase': 'Show passphrase', |
| 1362 | 'identity.hide_passphrase': 'Hide passphrase', |
| 1363 | 'identity.generate_another': 'Generate another', |
| 1364 | 'identity.wrote_it_down': 'I have written this down somewhere safe', |
| 1365 | 'identity.choose_own': 'Choose my own passphrase instead', |
| 1366 | 'identity.skip': 'Skip for now', |
| 1367 | 'identity.create_account': 'Create account', |
| 1368 | 'identity.unlock': 'Unlock', |
| 1369 | 'identity.forget': 'Forget this identity…', |
| 1370 | 'identity.use_generated': 'Use a generated passphrase instead', |
| 1371 | 'identity.title_create': 'Create your account', |
| 1372 | 'identity.title_unlock': 'Unlock Daimond', |
| 1373 | 'identity.title_welcome': 'Welcome back, {name}', |
| 1374 | 'identity.title_linked': 'Linked. Now unlock it', |
| 1375 | 'identity.lead_create': 'Choose a name. Daimond generates the passphrase itself, below. If you already use Daimond on another device, do not make a second account: choose “Have a pairing code?” to link this one. (Opening a real folder for agents to edit needs Chrome, Edge or Brave.)', |
| 1376 | 'identity.lead_unlock': 'Enter your passphrase to unlock this device and decrypt your saved key.', |
| 1377 | 'identity.lead_linked': 'This device is linked to your account. Enter the SAME passphrase you use on your other device, not a new one, to bring your chats and files here.', |
| 1378 | 'identity.lead_linked_named': 'This device is linked to your account “{name}”. Enter the SAME passphrase you use on your other device, not a new one, to bring your chats and files here.', |
| 1379 | // {bits} is a number read off the shipped wordlist, not a constant. |
| 1380 | 'identity.gen_note': 'Eight words picked at random by this device, about {bits} bits, far past anything that can be guessed. It is the key to everything you store here, and nobody can reset it for you. Write it on paper. A password manager may also offer to keep it, which is safe at this strength.', |
| 1381 | |
| 1382 | // ── Passkeys ─────────────────────────────────────────────── |
| 1383 | // Every one of these is a dead end explained: a passkey that will not open |
| 1384 | // leaves the passphrase as the only way in, so each says which it is. |
| 1385 | 'passkey.err_need_passphrase': 'A passphrase is required to enrol a passkey.', |
| 1386 | 'passkey.err_no_identity': 'There is no identity to protect with a passkey.', |
| 1387 | 'passkey.err_bad_passphrase': 'That passphrase did not match.', |
| 1388 | 'passkey.err_cannot_create': 'This browser or device cannot create a passkey.', |
| 1389 | 'passkey.err_create_failed': 'Passkey creation was cancelled or failed.', |
| 1390 | 'passkey.err_none_created': 'No passkey was created.', |
| 1391 | 'passkey.err_no_prf': 'This authenticator does not support the PRF extension, so it cannot unlock Daimond.', |
| 1392 | 'passkey.err_not_sealed': 'The passkey could not be sealed. Nothing was saved.', |
| 1393 | 'passkey.err_not_saved': 'The passkey could not be saved on this device.', |
| 1394 | 'passkey.err_not_enrolled': 'No passkey is enrolled on this device.', |
| 1395 | 'passkey.err_unreadable': 'The passkey could not be read.', |
| 1396 | 'passkey.err_unreadable_use_pass': 'The passkey could not be read. Use your passphrase.', |
| 1397 | 'passkey.err_wrong_identity': 'The passkey did not match this identity. Use your passphrase.', |
| 1398 | 'passkey.err_out_of_date': 'This passkey is out of date. Unlock with your passphrase, then re-add it.', |
| 1399 | 'passkey.err_cannot_use': 'This browser or device cannot use a passkey.', |
| 1400 | 'passkey.err_no_identity_support': 'Identity support is unavailable.', |
| 1401 | 'passkey.err_no_offer': 'No Daimond passkey was offered, or it carried no PRF secret.', |
| 1402 | 'passkey.err_no_account_carried': 'That passkey is not set up to carry an account. On the device that has your account, open Settings and add the passkey again.', |
| 1403 | 'passkey.err_did_not_open': 'That passkey did not open the account.', |
| 1404 | 'passkey.err_stored_unreadable': 'The stored account could not be read.', |
| 1405 | 'passkey.err_stored_locked': 'The stored account did not open. Use your passphrase.', |
| 1406 | 'passkey.err_not_resealed': 'The passkey could not be re-sealed.', |
| 1407 | |
| 1408 | // ── Cross-device sync ────────────────────────────────────── |
| 1409 | 'sync.syncing': 'Syncing…', |
| 1410 | 'sync.synced': 'Synced', |
| 1411 | 'sync.off': 'Sync off', |
| 1412 | 'sync.off_reason': 'Cross-device sync is part of Pro, which this account does not hold.', |
| 1413 | // The chip is the only place this is said, so it also has to be the way to |
| 1414 | // the offer: it is clickable in this state, and opens Pro in Credits. |
| 1415 | 'sync.off_click': 'Click to see Pro in Credits.', |
| 1416 | 'sync.off_pitch': 'Sync is off because this account is not on Pro. Pro adds cross-device sync, cloud storage and Email.', |
| 1417 | // A refused parcel. Say what is too large, and what usually makes it so: |
| 1418 | // "too large" on its own leaves nothing to do about it. |
| 1419 | 'sync.too_big': 'Sync paused', |
| 1420 | 'sync.too_big_reason': 'This device’s parcel is too large to send, so its work has stopped travelling. Usually one very large Diamond or workspace file is the cause; shrink or remove it and sync resumes on its own.', |
| 1421 | // A Diamond the parcel had no room for. Named, because the only thing the user can |
| 1422 | // do about it is find that Diamond, and said aloud rather than logged, because |
| 1423 | // nothing else on screen would look any different. |
| 1424 | 'sync.diamonds_left.one': '{n} Diamond did not fit in this device’s sync parcel and will not reach your other devices until it is smaller: {names}', |
| 1425 | 'sync.diamonds_left.other': '{n} Diamonds did not fit in this device’s sync parcel and will not reach your other devices until they are smaller: {names}', |
| 1426 | // A file the parcel had no room for, named the way a Diamond that did not fit is |
| 1427 | // named. The same news in the same shape; not an error -- the budget is doing what |
| 1428 | // it is for, and nothing at the far end is deleted. |
| 1429 | 'sync.files_left.one': '{n} file did not fit in this device’s sync parcel and will not reach your other devices until there is room for it: {names}', |
| 1430 | 'sync.files_left.other': '{n} files did not fit in this device’s sync parcel and will not reach your other devices until there is room for them: {names}', |
| 1431 | // A session that has ended and could not be taken again. Same label as the |
| 1432 | // two below for the same reason: what the user needs to know is that this |
| 1433 | // device's work is not travelling, and the difference is in the hover. |
| 1434 | 'sync.signed_out': 'Sync paused', |
| 1435 | 'sync.signed_out_reason': 'This device is signed out of the Daimond account service and could not sign in again, so its work is not reaching your other devices. It resumes as soon as the service is reachable. If it does not, lock Daimond and unlock it.', |
| 1436 | // A reconcile that did not finish. Both of these mean the same thing to the |
| 1437 | // user — this device's work is still only here — so they share a label and |
| 1438 | // differ in the reason. |
| 1439 | 'sync.paused': 'Sync paused', |
| 1440 | 'sync.busy_reason': 'Another device is saving to this account at the same time, so this device’s work has not been sent yet. It goes on the next change.', |
| 1441 | 'sync.merge_reason': 'Some of what arrived from the other device could not be merged here, so this device has not sent over the top of it. Reloading the page usually clears it.', |
| 1442 | 'sync.last_synced': 'Last synced {when}.', |
| 1443 | 'sync.last_never': 'Nothing has synced from this device yet.', |
| 1444 | 'sync.when_just_now': 'just now', |
| 1445 | 'sync.when_mins': '{n}m ago', |
| 1446 | 'sync.when_hours': '{n}h ago', |
| 1447 | 'sync.when_days': '{n}d ago', |
| 1448 | |
| 1449 | // ── The account's public handle ───────────────────────────── |
| 1450 | // The public name, minted by the gateway and the same on every device of |
| 1451 | // the account. Three refusals and a shrug, because "no" on its own leaves |
| 1452 | // the user guessing which no it was. |
| 1453 | 'handle.taken': 'Someone else already has that handle. Try another.', |
| 1454 | 'handle.invalid': 'A handle is 3 to 24 characters: lower-case letters, numbers, and hyphens between them.', |
| 1455 | 'handle.reserved': 'That handle is kept by Daimond and cannot be taken.', |
| 1456 | 'handle.failed': 'The handle could not be changed just now. Try again shortly.', |
| 1457 | |
| 1458 | // ── Cloud storage cleanup ────────────────────────────────── |
| 1459 | // A deletion the gateway would not carry out on one request. Say the two |
| 1460 | // numbers, and say plainly that nothing of theirs has gone. |
| 1461 | 'chunks.sweep_held': 'Cleanup paused', |
| 1462 | 'chunks.sweep_held_reason': 'Cloud storage holds {n} of its {m} stored pieces that no file on this account still refers to. They have NOT been deleted, because no single request may remove more than half of what is stored. Nothing of yours is missing, and the space is freed by the next sync that can account for it all.', |
| 1463 | // The chip is a control, so these five carry what pressing it does. They were |
| 1464 | // written as `t('key', 'English')` -- and `t`'s second argument is VARS, not a |
| 1465 | // fallback, so a key that is not here is PAINTED ON THE SCREEN as itself, in |
| 1466 | // every language including this one. |
| 1467 | 'chunks.upload_refused': 'Uploads paused', |
| 1468 | 'chunks.upload_refused_title': 'Cloud storage refused an upload', |
| 1469 | 'chunks.sweep_confirm_ask': 'Delete them now? Nothing you can still see is touched, and the space is freed.', |
| 1470 | 'chunks.sweep_confirm_ok': 'Delete them', |
| 1471 | 'chunks.sweep_confirm_title': 'Free the unreferenced pieces?', |
| 1472 | |
| 1473 | // ── Pairing a second device ──────────────────────────────── |
| 1474 | // The quotation marks around a button's name are curly on purpose; keep |
| 1475 | // whatever quotation marks the target language uses. |
| 1476 | 'pair.link_another': 'Link another device', |
| 1477 | 'pair.link_this': 'Link this device', |
| 1478 | 'pair.have_code': 'Have a pairing code?', |
| 1479 | 'pair.making_code': 'Making a one-time code…', |
| 1480 | 'pair.scan_lead': 'On your other phone, point its camera at this to open Daimond and link it:', |
| 1481 | 'pair.no_camera': 'Without a camera, open Daimond there, choose “Have a pairing code?”, and enter:', |
| 1482 | 'pair.type_lead': 'On your other device, open Daimond, choose “Have a pairing code?”, and enter:', |
| 1483 | 'pair.code_expiry': 'This code works once and expires in about {mins} minutes. You will unlock the other device with your usual passphrase.', |
| 1484 | 'pair.code_ph': 'pairing code', |
| 1485 | 'pair.code_check': 'This is the code from your other device. It should match the one shown there.', |
| 1486 | 'pair.scanned_lead': 'Scanned from your other device. Tap “Link this device” below to bring your account here.', |
| 1487 | 'pair.manual_lead': 'On the device you already use, choose “Link another device” and type the code it shows here.', |
| 1488 | 'pair.linked': 'This device is linked', |
| 1489 | 'pair.linked_note': 'It now holds your account. Tap Unlock, then enter the SAME passphrase you use on your other device, not a new one.', |
| 1490 | 'pair.linked_named': 'It now holds your account “{name}”. Tap Unlock, then enter the SAME passphrase you use on your other device, not a new one.', |
| 1491 | // Naming the device while linking it: the one moment the user is holding the |
| 1492 | // device in question. Optional — an unnamed device still syncs. |
| 1493 | 'pair.name_this': 'Name this device (optional)', |
| 1494 | 'pair.name_ph': 'e.g. Kitchen laptop', |
| 1495 | 'pair.named_note': 'This device will appear as “{name}” in your device list. You can change it later there.', |
| 1496 | 'pair.err_no_identity': 'There is no identity on this device to link.', |
| 1497 | 'pair.err_unreadable_local': 'Could not read this device’s identity.', |
| 1498 | 'pair.err_sign_in_first': 'Sign in on this device before linking another.', |
| 1499 | 'pair.err_enter_code': 'Enter the pairing code from your other device.', |
| 1500 | 'pair.err_bad_code': 'That code is invalid or has expired. Make a new one on your other device.', |
| 1501 | 'pair.err_bundle_unreadable': 'The linked identity was unreadable.', |
| 1502 | 'pair.err_bundle_import': 'The linked identity could not be imported.', |
| 1503 | 'pair.err_create': 'Could not create a pairing code.', |
| 1504 | 'pair.err_link': 'Could not link this device.', |
| 1505 | // The one-time presentation handover. Say that it happened and that it is |
| 1506 | // this device's own from now on, or a German interface on a new phone reads |
| 1507 | // as a fault rather than as the other device's setting. |
| 1508 | 'pair.look_carried': 'Your theme, language and panel layout came across too. Each device keeps its own from now on, so changing one leaves the other alone.', |
| 1509 | |
| 1510 | // ── The appearance menu ──────────────────────────────────── |
| 1511 | // `menu.view_note` is prose about the choice rather than a view, and it is |
| 1512 | // still declared: a variant is a key this table holds under the prefix, not |
| 1513 | // a promise that something in the app is called that. |
| 1514 | // i18n-family: menu.view_ = simple max simple_help max_help note |
| 1515 | 'menu.view': 'View', |
| 1516 | 'menu.view_simple': 'Simple', |
| 1517 | 'menu.view_max': 'Max', |
| 1518 | 'menu.view_simple_help': 'Each thing shows its name and whether it is running. Everything else is one press away.', |
| 1519 | 'menu.view_max_help': 'Models, cost and context beside each thing, so you can compare them without opening anything.', |
| 1520 | 'menu.view_note': 'Sets the shape of every tile too. Use a tile’s cog to set that tile differently.', |
| 1521 | 'menu.theme': 'Theme', |
| 1522 | // The bands and the palettes, both from `DaimondTheme` in daimond.js, which |
| 1523 | // this app owns. `_help` is optional and only Amber has one: `workspace.js` |
| 1524 | // forms the name and asks `t` for it, and the key coming back unchanged is |
| 1525 | // how it learns there is no note. |
| 1526 | // i18n-family: menu.tone_ = light mid dark |
| 1527 | // i18n-family: menu.theme_ = light mist linen lollypop sage dusk dark amber midnight forest plum |
| 1528 | // i18n-family: menu.theme_ = amber_help |
| 1529 | // i18n-indirect: workspace.js help = open -- the probe above: `menu.theme_<name>_help` is formed for all eleven palettes and ten of them are absent on purpose |
| 1530 | 'menu.tone_light': 'Light', |
| 1531 | 'menu.tone_mid': 'Intermediate', |
| 1532 | 'menu.tone_dark': 'Dark', |
| 1533 | 'menu.theme_light': 'Paper', |
| 1534 | 'menu.theme_mist': 'Mist', |
| 1535 | 'menu.theme_linen': 'Linen', |
| 1536 | 'menu.theme_lollypop': 'Lollypop', |
| 1537 | 'menu.theme_sage': 'Sage', |
| 1538 | 'menu.theme_dusk': 'Dusk', |
| 1539 | 'menu.theme_dark': 'Ember', |
| 1540 | 'menu.theme_amber': 'Amber', |
| 1541 | 'menu.theme_amber_help': 'Warm and blue-poor, for reading at night.', |
| 1542 | 'menu.theme_midnight': 'Midnight', |
| 1543 | 'menu.theme_forest': 'Forest', |
| 1544 | 'menu.theme_plum': 'Plum', |
| 1545 | 'menu.text_size': 'Text size', |
| 1546 | 'menu.smaller': 'Smaller text', |
| 1547 | 'menu.larger': 'Larger text', |
| 1548 | 'menu.dock_tiling': 'Dock tiling', |
| 1549 | 'menu.dock_auto': 'Auto', |
| 1550 | 'menu.dock_auto_help': 'A second column once the window is wide enough for it', |
| 1551 | 'menu.dock_grid_help.one': '{cols} column, up to {cells} panels', |
| 1552 | 'menu.dock_grid_help.other': '{cols} columns, up to {cells} panels', |
| 1553 | 'menu.language': 'Language', |
| 1554 | 'menu.language_help': 'What Daimond says. A language with no table yet stays in English.', |
| 1555 | 'menu.language_pending': 'not translated yet', |
| 1556 | 'menu.currency': 'Currency', |
| 1557 | 'menu.currency_help': 'What figures are shown in. Billing is always in US dollars.', |
| 1558 | 'menu.this_diamond': 'This Diamond', |
| 1559 | 'menu.arrangement_update': 'Update the saved arrangement', |
| 1560 | 'menu.arrangement_keep': 'Keep this arrangement with {name}', |
| 1561 | 'menu.arrangement_keep_this': 'Keep this arrangement with this Diamond', |
| 1562 | 'menu.arrangement_forget': 'Forget it', |
| 1563 | 'menu.arrangement_note': 'Opening this Diamond again restores the panels it was worked in. Nothing is remembered until you ask.', |
| 1564 | |
| 1565 | // ── Text-size steps ──────────────────────────────────────── |
| 1566 | // i18n-indirect: workspace.js STEP_KEYS[i] = size.small size.normal size.large size.larger |
| 1567 | 'size.small': 'Small', |
| 1568 | 'size.normal': 'Normal', |
| 1569 | 'size.large': 'Large', |
| 1570 | 'size.larger': 'Larger', |
| 1571 | |
| 1572 | // ── Dock grids ───────────────────────────────────────────── |
| 1573 | // i18n-indirect: workspace.js GRID_KEYS[k] || 'dock.automatic' = dock.one_column dock.2x2 dock.2x3 dock.3x2 dock.automatic |
| 1574 | 'dock.one_column': 'One column', |
| 1575 | 'dock.2x2': '2 by 2', |
| 1576 | 'dock.2x3': '2 by 3', |
| 1577 | 'dock.3x2': '3 by 2', |
| 1578 | 'dock.automatic': 'Automatic', |
| 1579 | |
| 1580 | // ── The panel gallery rows ───────────────────────────────── |
| 1581 | 'gallery.search_ph': 'Search panels', |
| 1582 | 'gallery.no_match': 'No panel by that name.', |
| 1583 | // i18n-family: gallery.zone_ = rail stage dock |
| 1584 | 'gallery.zone_rail': 'The rail', |
| 1585 | 'gallery.zone_stage': 'Beside the chat', |
| 1586 | 'gallery.zone_dock': 'The dock', |
| 1587 | 'gallery.note': 'Pinned panels sit in the top bar. Press <kbd>Ctrl</kbd> <kbd>K</kbd> to reach any panel from the keyboard.', |
| 1588 | 'gallery.state_unrevealed': 'not in use yet', |
| 1589 | 'gallery.state_full': 'dock full', |
| 1590 | 'gallery.state_open': 'open', |
| 1591 | 'gallery.state_no_room': 'no room in the bar', |
| 1592 | 'gallery.pin': 'Keep {name} in the top bar', |
| 1593 | 'gallery.unpin': 'Remove {name} from the top bar', |
| 1594 | |
| 1595 | // ── Spending ─────────────────────────────────────────────── |
| 1596 | 'spend.intro': 'Two pots, kept apart. Inference runs on your own provider key; credits pay the gateway for the few things that leave the browser.', |
| 1597 | 'spend.inference': 'Inference', |
| 1598 | 'spend.inference_hint': 'billed to your own provider key', |
| 1599 | 'spend.credits': 'Credits', |
| 1600 | 'spend.credits_hint': 'gateway services: web, mail, sync', |
| 1601 | 'spend.no_usage': 'No usage recorded.', |
| 1602 | 'spend.no_account': 'No gateway account yet. Credits pay for the few things that leave the browser: fetching a web page, sending or syncing mail, cross-device sync. Add a passphrase and credits to begin.', |
| 1603 | 'spend.this_week': 'this week', |
| 1604 | 'spend.this_month': 'this month', |
| 1605 | 'spend.session': 'this session', |
| 1606 | 'spend.balance': 'balance', |
| 1607 | 'spend.tok': 'tok', |
| 1608 | // The three cells of the rail's spend row. Short: they sit under a figure. |
| 1609 | // `day` is local midnight to now -- a calendar day, not a rolling 24h, so a |
| 1610 | // late-evening figure does not still carry yesterday morning's spend. |
| 1611 | // i18n-family: spend.period_ = day week month |
| 1612 | 'spend.period_day': 'Today', |
| 1613 | 'spend.period_week': 'Week', |
| 1614 | 'spend.period_month': 'Month', |
| 1615 | 'spend.no_turns': 'No turns in this window yet.', |
| 1616 | 'spend.nothing_yet': 'Nothing yet.', |
| 1617 | 'spend.nothing_spent': 'Nothing spent here yet.', |
| 1618 | 'spend.no_movements': 'No credit movements yet.', |
| 1619 | 'spend.where_credits_went': 'Where credits went', |
| 1620 | 'spend.unknown_model': '(unknown)', |
| 1621 | 'spend.meter_help': 'See where your spending goes', |
| 1622 | 'spend.rates_note': 'Figures are converted at approximate rates ({date}); you are billed in US dollars.', |
| 1623 | // Table headings. Keep them short: they sit above numbers in a narrow panel. |
| 1624 | 'spend.col_model': 'Model', |
| 1625 | 'spend.col_turns': 'Turns', |
| 1626 | 'spend.col_tokens': 'Tokens', |
| 1627 | 'spend.col_cost': 'Cost', |
| 1628 | 'spend.col_key': 'Key', |
| 1629 | 'spend.col_left': 'Left', |
| 1630 | 'spend.col_period_spend': 'Spent', |
| 1631 | 'spend.provider_keys': 'Provider keys', |
| 1632 | 'spend.left_auto': 'What the provider says is left on this key.', |
| 1633 | 'spend.left_manual': 'Your own figure, counted down by what Daimond estimates you have spent since.', |
| 1634 | 'spend.left_unknown': 'This provider does not say what is left, and you have not told Daimond.', |
| 1635 | // Where the headline figure came from. A total the providers billed is not an |
| 1636 | // estimate, and marking it "≈" said the opposite of the truth. |
| 1637 | 'spend.all_reported': 'Every turn in this window came with a cost from the provider, so this is what was charged.', |
| 1638 | 'spend.part_reported': '{amount} of this was reported by the providers; the rest is priced from Daimond\'s rate table.', |
| 1639 | 'spend.none_reported': 'Priced from Daimond’s rate table. No provider reported a cost for these turns.', |
| 1640 | 'spend.none_reported_unknown': 'Priced from Daimond’s rate table, which is missing one of these models, so the figure is a rough guide.', |
| 1641 | // Why a total the user remembers as larger has fallen: the old rate table ran |
| 1642 | // high, and the estimates it made were corrected once, here, on this device. |
| 1643 | 'spend.repriced': 'Estimates made before the rate table was corrected have been repriced on this device; under the old table this period read {amount}.', |
| 1644 | 'spend.col_when': 'When', |
| 1645 | 'spend.col_what': 'What', |
| 1646 | 'spend.col_amount': 'Amount', |
| 1647 | 'spend.col_balance': 'Balance', |
| 1648 | // Credit categories, as the gateway tags them. The first seven are metered |
| 1649 | // spends and are `SPEND_CATEGORIES` in gateway/src/schema.rs; the rest are |
| 1650 | // credit-side movements, named by their kind alone. |
| 1651 | // |
| 1652 | // `cat_infer` is model spend on the key Daimond mints and reconciles against |
| 1653 | // credits, which is a different pot from the Inference section above it — |
| 1654 | // that one is billed to the user's own provider key and never touches |
| 1655 | // credits. Hence "on credits" rather than "Inference" alone: the two sit on |
| 1656 | // one screen and the qualifier is the whole difference between them. It also |
| 1657 | // leads with the word that matters, because the dock is narrow enough to |
| 1658 | // truncate a label to its first two words. |
| 1659 | // |
| 1660 | // The category comes from the gateway, and the name is only built where the |
| 1661 | // category is one this build knows: `spend.js:103` fences it on `CATS`, and |
| 1662 | // anything else falls to `cat_unlisted` below. So the set is closed, and |
| 1663 | // `dev/verify_spendcats.mjs` is what keeps `CATS` itself in step with the |
| 1664 | // Rust. `fallback` and `unlisted` are named outright rather than built, and |
| 1665 | // are declared here because a variant is a key under the prefix. |
| 1666 | // i18n-family: spend.cat_ = web search mail sync storage infer other |
| 1667 | // i18n-family: spend.cat_ = topup refund grant adjust fallback unlisted |
| 1668 | 'spend.cat_web': 'Web pages', |
| 1669 | 'spend.cat_search': 'Web searches', |
| 1670 | 'spend.cat_mail': 'Mail', |
| 1671 | 'spend.cat_sync': 'Cross-device sync', |
| 1672 | 'spend.cat_storage': 'Stored files', |
| 1673 | 'spend.cat_infer': 'Inference on credits', |
| 1674 | 'spend.cat_other': 'Other services', |
| 1675 | 'spend.cat_topup': 'Credits bought', |
| 1676 | 'spend.cat_refund': 'Refunds', |
| 1677 | 'spend.cat_grant': 'Gifts & grants', |
| 1678 | 'spend.cat_adjust': 'Adjustments', |
| 1679 | 'spend.cat_fallback': 'Other', // a category the gateway did not name |
| 1680 | // A category the gateway DID name and this build cannot. Money left the |
| 1681 | // balance with nothing to say for it, and that is what the row says: showing |
| 1682 | // the gateway's own token instead is how three weeks of untranslated |
| 1683 | // "infer" reached eight languages unnoticed. |
| 1684 | 'spend.cat_unlisted': 'Not accounted for', |
| 1685 | |
| 1686 | // ── Tools ────────────────────────────────────────────────── |
| 1687 | // `tools.head` and `tools.shop_fine` are placed inside markup, so any HTML |
| 1688 | // in them is rendered. Keep the tags that are there and add none. |
| 1689 | // The Tools panel. Six keys that used to be here went with the panel it was |
| 1690 | // rewritten out of -- `tools.head`, `built_in`, `unlocked`, `sec_unlocked`, |
| 1691 | // `sec_shop` and `shop_fine` -- and are gone rather than kept against a |
| 1692 | // caller that no longer exists. These three outlived it and are still read. |
| 1693 | 'tools.unlock_price': 'Unlock for {price}', |
| 1694 | 'tools.unreachable': 'The account service could not be reached, so what is unlocked here is unknown.', |
| 1695 | 'tools.no_service': 'The account service is unavailable.', |
| 1696 | 'tools.intro': 'What Daimond can do for you. Most of it is included. A pack adds new ground, and is bought once in dollars rather than out of your credits.', |
| 1697 | 'tools.count': '<b>{have} of {all}</b> available on this account.', |
| 1698 | 'tools.sec_included': 'Included', |
| 1699 | 'tools.sec_packs': 'Packs', |
| 1700 | 'tools.packs_none': 'No packs are on sale yet. When one is, it appears here.', |
| 1701 | 'tools.packs_fine': 'A pack is bought once and kept. It is paid for in dollars, through the same checkout as Pro, and never out of your credits — so a pack costs the same whether you run Daimond on its credits or on your own API key.', |
| 1702 | 'tools.status_included': 'Included', |
| 1703 | 'tools.status_owned': 'Yours', |
| 1704 | 'tools.locked_why': 'Not bought on this account: this is the {pack} pack. Daimond refuses it and says so rather than half-doing it.', |
| 1705 | 'tools.expand': 'What it does ({n})', |
| 1706 | 'tools.collapse': 'Hide what it does', |
| 1707 | 'tools.fn_pack': 'in the {pack} pack', |
| 1708 | // The ten capabilities of `CAPS` in tools.js, plus the `other` that |
| 1709 | // `capOf` returns for a function nobody has placed. A pack landing with a |
| 1710 | // new capability lands here or the panel names it `tools.cap.x.name`. |
| 1711 | // i18n-family: tools.cap. = files.name files.blurb cloud.name cloud.blurb |
| 1712 | // i18n-family: tools.cap. = work.name work.blurb show.name show.blurb asking.name asking.blurb |
| 1713 | // i18n-family: tools.cap. = checking.name checking.blurb machine.name machine.blurb reading.name reading.blurb |
| 1714 | // i18n-family: tools.cap. = browsing.name browsing.blurb typeset.name typeset.blurb |
| 1715 | // i18n-family: tools.cap. = dispatch.name dispatch.blurb graph.name graph.blurb |
| 1716 | // i18n-family: tools.cap. = social.name social.blurb email.name email.blurb |
| 1717 | // i18n-family: tools.cap. = other.name other.blurb |
| 1718 | 'tools.cap.files.name': 'Your files', |
| 1719 | 'tools.cap.files.blurb': 'Daimond reads, writes and edits the files in your workspace — a spreadsheet’s cells and a document’s words included — finds things across all of them at once, and tidies up after itself.', |
| 1720 | 'tools.cap.cloud.name': 'Files kept in the cloud', |
| 1721 | 'tools.cap.cloud.blurb': 'A file this device is not holding is brought down from your cloud storage at the moment Daimond needs to read it, rather than everything being kept everywhere.', |
| 1722 | 'tools.cap.work.name': 'Keeping the work together', |
| 1723 | 'tools.cap.work.blurb': 'Daimond records which files belong to a piece of work, so something you made yourself is listed with it instead of sitting unremarked in a folder.', |
| 1724 | 'tools.cap.asking.name': 'Asking you a question', |
| 1725 | 'tools.cap.asking.blurb': 'Daimond puts one decision to you at a time, as answers you tap: each option says what it would concretely mean, one of them is named as the recommendation with the reason for it, and you can always answer in your own words instead.', |
| 1726 | 'tools.cap.show.name': 'Showing you a file', |
| 1727 | 'tools.cap.show.blurb': 'Daimond puts a file on your screen beside the chat — a PDF as its typeset pages, a picture drawn, a table as a table — rather than only describing it to you.', |
| 1728 | 'tools.cap.checking.name': 'Checking its own work', |
| 1729 | 'tools.cap.checking.blurb': 'Daimond runs one of this project’s own checks and reports what passed, what failed, and which of the check’s own break tests proved nothing.', |
| 1730 | 'tools.cap.social.name': 'Talking to other people about Daimond', |
| 1731 | 'tools.cap.social.blurb': 'Daimond reads what people have reported or asked for about Daimond itself, and — with your say-so each time, and after showing you exactly what would go out — reports or backs something on your behalf.', |
| 1732 | 'tools.cap.machine.name': 'Your computer', |
| 1733 | 'tools.cap.machine.blurb': 'With Daimond’s machine hand installed, Daimond builds, tests and runs command-line tools inside the folder you granted it, and nowhere else.', |
| 1734 | 'tools.cap.reading.name': 'Reading the web', |
| 1735 | 'tools.cap.reading.blurb': 'Daimond searches with the engine you chose and reads what a page actually says, without either of you leaving Daimond.', |
| 1736 | 'tools.cap.browsing.name': 'Using a website', |
| 1737 | 'tools.cap.browsing.blurb': 'Daimond opens a page beside the chat and works it — clicking, typing, scrolling — while you watch it happen.', |
| 1738 | 'tools.cap.typeset.name': 'Typesetting a document', |
| 1739 | 'tools.cap.typeset.blurb': 'Daimond turns a Typst source into a finished PDF, properly typeset, here in the browser.', |
| 1740 | 'tools.cap.dispatch.name': 'Sending workers out', |
| 1741 | 'tools.cap.dispatch.blurb': 'Daimond breaks a large job into bounded tasks and sends a worker to each, several at a time, then folds what they bring back into one answer.', |
| 1742 | 'tools.cap.graph.name': 'How your work relates', |
| 1743 | 'tools.cap.graph.blurb': 'Daimond reads and records the relations between your Diamonds, files, pages and chats — what supersedes what, what produced what.', |
| 1744 | 'tools.cap.email.name': 'Your email', |
| 1745 | 'tools.cap.email.blurb': 'Daimond reads the mailboxes you have synced to this device — listing them, finding a message by who it is from or its subject, and reading one in full — and writes a draft reply for you to review. It never sends: only you can press Send.', |
| 1746 | 'tools.cap.other.name': 'Not yet described', |
| 1747 | 'tools.cap.other.blurb': 'Daimond has these and this panel has not been told what to call them. Open it to see what they are.', |
| 1748 | |
| 1749 | |
| 1750 | // ── The rail's Diamonds and chats ────────────────────────── |
| 1751 | 'rail.no_diamonds': 'No Diamonds yet.', |
| 1752 | 'rail.no_match': 'No Diamonds match.', |
| 1753 | 'rail.no_chats': 'No chats yet.', |
| 1754 | // A chat tile with nothing behind it is two different things: one nobody |
| 1755 | // said anything in, and one whose transcript is on another device. The |
| 1756 | // second is the one a reader mistakes for lost work, so it says which. |
| 1757 | 'rail.not_synced': 'not synced yet', |
| 1758 | 'rail.not_synced_help': 'This conversation is on another of your devices and has not arrived here yet.', |
| 1759 | // Shown under the search box when nothing is tagged, so that an empty |
| 1760 | // filing system cannot be read as a missing one. |
| 1761 | 'rail.tag_hint': 'No tags yet. Tag a Diamond and filter chips appear here.', |
| 1762 | 'rail.tag_hint_help': 'Open a Diamond, then Tags on its crystal. Starter tags are offered there: {tags}.', |
| 1763 | 'rail.dblclick_rename': 'Double-click to rename', |
| 1764 | 'rail.rename_diamond': 'Rename Diamond', |
| 1765 | 'rail.rename': 'Rename', |
| 1766 | 'rail.rename_failed': 'Rename failed', |
| 1767 | 'rail.delete_failed': 'Delete failed', |
| 1768 | 'rail.create': 'Create', |
| 1769 | 'rail.name': 'Name', |
| 1770 | 'rail.model': 'Model', |
| 1771 | 'rail.worker_model': 'Model for workers', |
| 1772 | 'rail.worker_model_help': 'The daimon dispatches workers several at a time. Left as it is, they run on the Diamond’s own model.', |
| 1773 | 'rail.err_name': 'Give the Diamond a name.', |
| 1774 | 'rail.err_model': 'Choose a model for this Diamond to think with.', |
| 1775 | 'rail.err_no_key': 'That provider has no readable key yet. Unlock, or add one.', |
| 1776 | 'rail.err_no_key_worker': 'The workers’ provider has no readable key yet, so they would fall back to the Diamond’s own model. Unlock the key, or choose another provider.', |
| 1777 | 'rail.create_failed': 'Could not create Diamond', |
| 1778 | 'rail.created_unreadable': 'Diamond created, but not readable', |
| 1779 | 'rail.created_unreadable_body': '"{name}" was written but could not be read back, so it is not in the rail. Reload the page. If it is still missing, this device’s storage is refusing to serve what it accepted.', |
| 1780 | |
| 1781 | // ── When a chat happened, and keeping it ─────────────────── |
| 1782 | // A chat carries no name, so the rail says WHEN: a day heading, and the |
| 1783 | // time within that day on each tile. Neither repeats the other. |
| 1784 | 'rail.day_today': 'Today', |
| 1785 | 'rail.day_yesterday': 'Yesterday', |
| 1786 | 'rail.day_earlier': 'Earlier', |
| 1787 | // Under Today only, and lower case: the heading above has already spoken, |
| 1788 | // so the tile finishes the phrase rather than starting a sentence. |
| 1789 | 'rail.when_now': 'just now', |
| 1790 | 'rail.when_min': '{n} min ago', |
| 1791 | 'rail.when_hr': '{n} hr ago', |
| 1792 | // What a nameless chat is called inside a sentence — a toast, a label — |
| 1793 | // where the bare time would leave the sentence naming nothing. |
| 1794 | 'rail.a_chat': 'a chat', |
| 1795 | 'rail.chat_from': 'the chat from {when}', |
| 1796 | 'rail.show_preview': 'Show the first message', |
| 1797 | 'rail.show_preview_help': 'Show the first thing you said in each chat, under the time. Turn it off and the rail says only when.', |
| 1798 | // The way across, offered on the tile and again in the trash. The body is |
| 1799 | // the one place the difference between a chat and a Diamond is stated. |
| 1800 | 'tile.keep': 'Keep', |
| 1801 | 'tile.keep_help': 'Make a Diamond of this chat, with the whole conversation as its first artefact.', |
| 1802 | 'keep.title': 'Keep as a Diamond', |
| 1803 | 'keep.body': 'Name it, and this conversation is kept whole inside it. Chats expire; Diamonds do not.', |
| 1804 | 'keep.ok': 'Keep it', |
| 1805 | 'keep.gone': 'That chat is no longer here', |
| 1806 | 'keep.gone_body': 'It was destroyed on this device or another one. Nothing was made.', |
| 1807 | 'keep.made': '{name} kept, with the conversation inside it.', |
| 1808 | 'keep.made_bare': '{name} made, but the conversation could not be written into it.', |
| 1809 | |
| 1810 | // ── The PPTW: pause, play, traffic light ─────────────────── |
| 1811 | // One control at six placements. The state words are lower case because |
| 1812 | // they are appended to an action in the accessible name — "Pause Alpha — |
| 1813 | // running" — and a capital mid-sentence would read as a second label. |
| 1814 | 'pause.everything': 'Everything', |
| 1815 | 'pause.everything_help': 'Pause or resume everything that can spend: every Diamond, every chat, the workers, the mailboxes and any page fetched for you.', |
| 1816 | // {name} holds two different kinds of thing and no spacing suits both, so |
| 1817 | // this has a known rough edge in ja, zh-Hans and fr, and it is left alone |
| 1818 | // deliberately. It takes either a user-named Diamond or chat -- usually |
| 1819 | // Latin script -- or a translated node name. The ja template spaces the |
| 1820 | // particle off the name ('{name} を一時停止') because that is right for |
| 1821 | // "Alpha を一時停止" and wrong for "ウェブアクセス を一時停止"; zh-Hans |
| 1822 | // spaces it ('暂停 {name}') for the same reason and with the same cost |
| 1823 | // between two Chinese words; fr's 'Mettre {name} en pause' wants "l'accès" |
| 1824 | // once {name} is a common noun rather than a name. Every existing node |
| 1825 | // already reads this way. Gluing would fix the translated case and break |
| 1826 | // the commoner one, so nothing in this table fixes both -- only a call |
| 1827 | // site that knows which kind of name it is holding could. |
| 1828 | 'pause.act_pause': 'Pause {name}', |
| 1829 | 'pause.act_play': 'Resume {name}', |
| 1830 | // The four answers `pause.js`'s `stateOf` can give for a node. They are |
| 1831 | // counted over the ARMED leaves -- the ones with something set up to spend |
| 1832 | // without being asked -- and not over every leaf: a control whose subject is |
| 1833 | // entirely manual has nothing to be running, and used to read green. |
| 1834 | // i18n-family: pause.state_ = play pause mixed idle |
| 1835 | 'pause.state_play': 'running', |
| 1836 | 'pause.state_pause': 'paused', |
| 1837 | 'pause.state_mixed': 'partly paused', |
| 1838 | // The fourth, and it is RED like `pause` while meaning something else. It |
| 1839 | // covers both "you have set nothing up" and "what is here cannot fire" -- an |
| 1840 | // action switched off, or one missing its mailbox or its instruction -- so the |
| 1841 | // words must be true of all three. "Nothing set up" was not: the Optimiser |
| 1842 | // ships WITH an action, switched off, and that light would have said there was |
| 1843 | // none. The colour cannot make the distinction and this does not pretend to; |
| 1844 | // what it states is the question the light answers. |
| 1845 | 'pause.state_idle': 'nothing here will act on its own', |
| 1846 | 'pause.this': 'this', |
| 1847 | 'pause.workers': 'Workers', |
| 1848 | 'pause.mail': 'Mail', |
| 1849 | 'pause.mail_polling': 'Mailbox polling', |
| 1850 | 'pause.unnamed_chat': 'Unnamed chat', |
| 1851 | // The refusals enforcement speaks at the spend boundary. {node} is a |
| 1852 | // HUMAN name -- DaimondPause.label(), not the node id -- because |
| 1853 | // "root/diamonds/a1b2/self" names the node exactly and tells the reader |
| 1854 | // nothing. All four say the same three things: what did NOT happen, that no |
| 1855 | // money moved, and where the control is. The web one once stayed silent |
| 1856 | // about the control, because `root/web` had no widget anywhere in the app; |
| 1857 | // the Web panel header has one now, and dev/verify_search_i18n.mjs asserts |
| 1858 | // all four name it, in every language. |
| 1859 | 'pause.web': 'Web', |
| 1860 | 'pause.refused_title': 'Paused', |
| 1861 | // `pauseWords` in gateway.js is handed the key by its two callers, which name |
| 1862 | // it outright; the helper itself is not one this check reads. |
| 1863 | // i18n-indirect: gateway.js key = pause.refused.mail pause.refused.web |
| 1864 | 'pause.refused.turn': '{node} is paused. No turn started, nothing spent. Press play on it to resume.', |
| 1865 | 'pause.refused.dispatch': '{node} is paused. No agents dispatched, nothing spent. Press play on it to resume.', |
| 1866 | 'pause.refused.web': '{node} is paused. The page was not fetched and nothing was spent. Press play on it to resume.', |
| 1867 | 'pause.refused.mail': '{node} is paused. The mailbox was not contacted and nothing was spent. Press play on it to resume.', |
| 1868 | |
| 1869 | // ── Tags ─────────────────────────────────────────────────── |
| 1870 | 'tag.only_agents': 'Show only agents tagged "{tag}"', |
| 1871 | 'tag.only_diamonds': 'Show only Diamonds tagged "{tag}"', |
| 1872 | 'tag.clear_filter': 'Clear the "{tag}" filter', |
| 1873 | 'tag.remove': 'Remove "{tag}"', |
| 1874 | 'tag.add': 'Add "{tag}"', |
| 1875 | 'tag.starters': 'person, project, topic, org', |
| 1876 | 'tag.add_btn': 'Add', |
| 1877 | 'tag.add_ph': 'Add a tag', |
| 1878 | 'tag.on_diamond': 'On this Diamond', |
| 1879 | 'tag.all': 'All your tags', |
| 1880 | 'tag.all_used': 'Every tag you have is on this Diamond already.', |
| 1881 | 'tag.none_yet': 'No tags yet.', |
| 1882 | 'tag.save_failed': 'Could not save the tags', |
| 1883 | // The rail's boolean filter: the tags wanted, the tags refused, and how two |
| 1884 | // or more wanted tags combine. A chip in the rail cycles off, wanted, |
| 1885 | // refused, off; a chip in the summary comes out of the filter altogether. |
| 1886 | 'tag.exclude_next': 'Showing Diamonds tagged "{tag}". Click again to hide them.', |
| 1887 | 'tag.clear_exclude': 'Stop hiding Diamonds tagged "{tag}"', |
| 1888 | 'tag.not_tagged': 'Not tagged "{tag}"', |
| 1889 | // i18n-indirect: daimond.js m[1] = tag.mode_all tag.mode_any |
| 1890 | // i18n-indirect: daimond.js m[2] = tag.mode_all_help tag.mode_any_help |
| 1891 | 'tag.mode_all': 'All', |
| 1892 | 'tag.mode_any': 'Any', |
| 1893 | 'tag.mode_all_help': 'Show only Diamonds carrying every one of these tags', |
| 1894 | 'tag.mode_any_help': 'Show Diamonds carrying any one of these tags', |
| 1895 | 'tag.mode_aria': 'How the chosen tags combine', |
| 1896 | 'tag.clear_all': 'Clear', |
| 1897 | 'tag.clear_all_help': 'Take every tag out of the filter', |
| 1898 | // The one row the pool of chips sits behind, under the search box. It is |
| 1899 | // there whenever a tag is, because a filter nobody can see is a filter |
| 1900 | // nobody finds -- and it is only a row, because a vocabulary of thirty tags |
| 1901 | // standing open costs the rail's two lists four rows on every screen. |
| 1902 | // {n} is how many tags are behind it. |
| 1903 | 'tag.pool_toggle': 'Filter by tag ({n})', |
| 1904 | 'tag.pool_show_help': 'Show every tag you can filter on', |
| 1905 | 'tag.pool_hide_help': 'Put the tags away. Anything you are filtering on stays on, and stays in view.', |
| 1906 | // The chips that are doing the filtering, shown on their own while the tags |
| 1907 | // are put away. |
| 1908 | 'tag.active_aria': 'Tags the rail is filtering on', |
| 1909 | // Deleting a tag from the pool, which takes it off every Diamond that carries it. |
| 1910 | 'tag.delete_help': 'Delete the tag "{tag}" everywhere', |
| 1911 | 'tag.delete_title': 'Delete this tag?', |
| 1912 | 'tag.delete_body_used.one': 'Delete the tag "{tag}"? It comes off the {n} Diamond carrying it. Nothing else about that Diamond changes.', |
| 1913 | 'tag.delete_body_used.other': 'Delete the tag "{tag}"? It comes off all {n} Diamonds carrying it. Nothing else about them changes.', |
| 1914 | 'tag.delete_body_unused': 'Delete the tag "{tag}"? No Diamond carries it, so this only takes it off the list.', |
| 1915 | 'tag.delete_ok': 'Delete the tag', |
| 1916 | 'tag.deleted': 'Tag "{tag}" deleted.', |
| 1917 | 'tag.deleted_from.one': 'Tag "{tag}" deleted from {n} Diamond.', |
| 1918 | 'tag.deleted_from.other': 'Tag "{tag}" deleted from {n} Diamonds.', |
| 1919 | 'tag.editor_note': 'Tags file this Diamond in the rail. They are never sent to a model and never enter the crystal.', |
| 1920 | |
| 1921 | // ── Starting and interrupting a turn ─────────────────────── |
| 1922 | 'chat.choose_model': 'Choose a model to start this chat.', |
| 1923 | 'chat.no_key_start': 'That provider has no readable key yet. Unlock, or add one, to start this chat.', |
| 1924 | 'chat.connect_start': 'Connect a provider to start this chat.', |
| 1925 | 'chat.pending_hint': 'Pick a model in this chat\u2019s tile and press \u25b6 Start to begin.', |
| 1926 | 'chat.start_selected': 'Start with the selected model', |
| 1927 | 'chat.stop': 'Stop', |
| 1928 | 'turn.interrupted': 'Interrupted. The browser closed before this finished.', |
| 1929 | 'turn.interrupted_early': 'Interrupted before it could answer.', |
| 1930 | 'turn.offline': 'The connection dropped before this finished. Nothing was lost — pick it up where it stopped.', |
| 1931 | 'turn.offline_early': 'The connection dropped before an answer arrived. Nothing was lost — ask again from here.', |
| 1932 | 'turn.peer_sent': 'Sent to your other devices.', |
| 1933 | 'turn.peer_none': 'No awake device picked this up.', |
| 1934 | 'turn.peer_claimed': 'Another device is picking this up.', |
| 1935 | 'turn.peer_claimed_named': '{name} is picking this up.', |
| 1936 | 'turn.peer_running': 'Running on your other device.', |
| 1937 | 'turn.peer_running_named': '{name} is doing this.', |
| 1938 | 'turn.peer_failed': 'Your other device couldn’t finish this.', |
| 1939 | 'turn.peer_takeback': 'Take back', |
| 1940 | 'turn.peer_runhere': 'Run here', |
| 1941 | 'turn.peer_consent': 'Your other device needs your permission to {act}.', |
| 1942 | 'turn.peer_consent_named': '{name} needs your permission to {act}.', |
| 1943 | 'turn.peer_parked': 'This needed your permission — it will re-run when you’re back.', |
| 1944 | 'turn.peer_rerun': 'Re-run', |
| 1945 | 'turn.consent_act_type': 'type into {host}', |
| 1946 | 'turn.consent_act_click': 'act on {host}', |
| 1947 | 'turn.consent_act_search': 'search the web', |
| 1948 | 'turn.consent_act_reach': 'reach {host}', |
| 1949 | 'turn.continue': 'Continue', |
| 1950 | 'turn.continue_help': 'Run this turn again from your message.', |
| 1951 | 'astat.no_model': 'No model connected', |
| 1952 | 'astat.model_help': 'The models Daimond can run, and the keys behind them', |
| 1953 | |
| 1954 | // ── Folding a chat into a Diamond ────────────────────────── |
| 1955 | 'fold.nothing': 'Nothing to fold', |
| 1956 | 'fold.chat_empty': 'This chat is empty. Send a message first, then fold it into a Diamond.', |
| 1957 | 'fold.turns_empty': 'The turns you chose have no content to fold in.', |
| 1958 | 'fold.agent_empty': 'This agent produced no summary to fold.', |
| 1959 | 'fold.into': 'Fold into…', |
| 1960 | 'fold.n_turns_into.one': 'Fold {n} turn into…', |
| 1961 | 'fold.n_turns_into.other': 'Fold {n} turns into…', |
| 1962 | 'fold.no_diamonds': 'No Diamonds yet. Create one:', |
| 1963 | 'fold.new_diamond': '+ New Diamond…', |
| 1964 | 'fold.connect_first': 'Connect a provider to fold into a Diamond.', |
| 1965 | 'fold.create_and_fold': 'Create and fold', |
| 1966 | 'fold.no_key': 'That Diamond’s provider has no readable key. Unlock, or add one, to fold into it.', |
| 1967 | 'fold.nothing_new': 'Nothing new to fold', |
| 1968 | 'fold.nothing_new_body': '"{chat}" has not changed since it was folded into "{diamond}".', |
| 1969 | 'fold.proposing': 'Proposing fold…', |
| 1970 | 'fold.busy_title': 'That Diamond is working', |
| 1971 | 'fold.busy_body': '“{diamond}” has a turn in flight, and a fold rewrites the same crystal. Wait for it to finish, then fold.', |
| 1972 | 'fold.diamond_gone': 'Diamond is gone', |
| 1973 | 'fold.diamond_gone_body': 'The Diamond that dispatched this agent no longer exists.', |
| 1974 | 'fold.diamond_gone_chat_body': 'The Diamond you chose no longer exists; it may have been deleted in another tab. Nothing was folded.', |
| 1975 | 'fold.empty_reply': 'The model returned nothing to fold, so nothing was proposed. Try again.', |
| 1976 | 'fold.proposed_toast': 'Fold proposed. Accept or Reject it below.', |
| 1977 | 'fold.proposed_elsewhere': 'Fold proposed on "{diamond}". Open it to Accept or Reject.', |
| 1978 | 'fold.pending_badge': 'fold waiting', |
| 1979 | 'fold.pending_badge_help': 'A proposed fold is waiting on this Diamond. Open it to accept or reject the change.', |
| 1980 | // The fold diff's heading. Four shapes rather than one with glue, because a |
| 1981 | // language that puts the target first cannot reorder a fragment. |
| 1982 | 'diff.folding_chat': 'Folding "{chat}". Review the change, then Accept or Reject.', |
| 1983 | 'diff.folding_chat_into': 'Folding "{chat}" into "{diamond}". Review the change, then Accept or Reject.', |
| 1984 | 'diff.proposed': 'Proposed fold. Review the change, then Accept or Reject.', |
| 1985 | 'diff.proposed_into': 'Proposed fold into "{diamond}". Review the change, then Accept or Reject.', |
| 1986 | 'diff.no_change': 'No change proposed. The crystal already covers this.', |
| 1987 | 'diff.no_change_into': 'No change proposed into "{diamond}". The crystal already covers this.', |
| 1988 | 'diff.accept': 'Accept fold', |
| 1989 | 'diff.reject': 'Reject', |
| 1990 | 'diff.nothing_to_apply': 'Nothing to apply. The proposal matches the current crystal.', |
| 1991 | // What accepting would take OUT. A fold that only adds needs no warning; one |
| 1992 | // that drops a key the crystal already holds is the change worth naming, and |
| 1993 | // {keys} is the list of them, joined by the caller. |
| 1994 | 'fold.keys_lost': 'Accepting this removes: {keys}', |
| 1995 | |
| 1996 | // ── The crystal ──────────────────────────────────────────── |
| 1997 | 'crystal.page': 'Page', |
| 1998 | 'crystal.page_help': 'Ask the daimon to change how this crystal looks, not what it says', |
| 1999 | 'crystal.page_note': 'Change this Diamond\'s PAGE (crystal.html), not its memory (crystal.json). Read crystal.html first, then edit it, and leave crystal.json alone. Keep it self-contained: all CSS and JavaScript inline, images only as data: URIs, no fetch, no external files, no eval. Keep its ready, rendered and height messages, and let rendered name every top-level key of the data that has content. A page may also save files into this Diamond with the save message and read them back with asset, so an interactive page can keep what the user does. What I want: ', |
| 2000 | 'crystal.history': 'History', |
| 2001 | 'crystal.tags': 'Tags', |
| 2002 | 'crystal.tags_help': 'File this Diamond in the rail', |
| 2003 | 'crystal.marks_unread': 'The attachments could not be read, so this turn works only in the Diamond.', |
| 2004 | 'crystal.empty': 'The crystal is empty. Steer it below to begin.', |
| 2005 | 'crystal.empty_paren': '(empty)', |
| 2006 | 'crystal.save_failed': 'Could not save the crystal', |
| 2007 | 'crystal.back': 'Back to the crystal', |
| 2008 | 'crystal.memory': 'Memory', |
| 2009 | 'crystal.memory_help': 'View and edit this Diamond’s memory — the crystal it carries into every turn', |
| 2010 | 'crystal.memory_invalid': 'That is not valid crystal JSON, so nothing was saved.', |
| 2011 | 'crystal.no_history': 'No history yet.', |
| 2012 | 'crystal.view': 'View', |
| 2013 | 'crystal.restore': 'Restore', |
| 2014 | 'crystal.restore_v': 'Restore v{v}', |
| 2015 | 'crystal.restore_title': 'Restore a version', |
| 2016 | 'crystal.restore_body': 'Restore the crystal to v{v}? The current text is kept in the history, so this can itself be undone.', |
| 2017 | 'crystal.restore_failed': 'Could not restore', |
| 2018 | 'crystal.at_version': 'Crystal at v{v}', |
| 2019 | 'crystal.read_version_failed': 'Could not read that version', |
| 2020 | 'crystal.delta': 'Delta', |
| 2021 | 'crystal.delta_help': 'The raw input this fold was made from', |
| 2022 | 'crystal.delta_at': 'Delta folded at v{v}', |
| 2023 | 'crystal.read_delta_failed': 'Could not read the delta', |
| 2024 | 'crystal.view_switch': 'Which face of this Diamond', |
| 2025 | 'crystal.view_crystal': 'Crystal', |
| 2026 | 'crystal.view_chat': 'Chat', |
| 2027 | // Full screen, on the crystal face. The label names the DESTINATION rather |
| 2028 | // than the state, because while the mode is on this control is the way out |
| 2029 | // and the only chrome left on screen. |
| 2030 | 'crystal.full': 'Full screen', |
| 2031 | 'crystal.full_help': 'Fill the screen with this page', |
| 2032 | 'crystal.full_exit': 'Exit full screen', |
| 2033 | 'crystal.full_exit_help': 'Put the rail and the top bar back', |
| 2034 | 'crystal.chat_empty': 'Nothing said yet. The crystal is what this Diamond knows; this is how it came to know it.', |
| 2035 | 'crystal.dispatch_after_error': 'The turn ended badly, so the agents it asked for were not started.', |
| 2036 | |
| 2037 | // ── A capp, delivered from the guide ─────────────────────── |
| 2038 | // One template ships, and `{name}` is its name rather than a word in the |
| 2039 | // English: a second template must not need a second set of these. |
| 2040 | 'capp.title': '{name}', |
| 2041 | 'capp.make_body': 'This makes a Diamond called “{name}” and puts the {name} page inside it. What you record stays in that Diamond, on this device.', |
| 2042 | 'capp.make_ok': 'Make it', |
| 2043 | // A second one is not a second log, it is a lost one. |
| 2044 | 'capp.exists_body': 'You already have a Diamond called “{name}”, and what you have logged is in it. Making a second one would leave those entries behind.', |
| 2045 | 'capp.exists_ok': 'Open it', |
| 2046 | 'capp.missing_body': 'This build of Daimond does not carry the {name} template, so there is nothing to put in a Diamond. Nothing has been made. Ask your daimon for a page instead, or update Daimond and try again.', |
| 2047 | 'capp.page_failed': 'The Diamond was made, but its page could not be written: {why}', |
| 2048 | // Keeping a capp's page up to date. A capp made before capps carried a |
| 2049 | // version cannot be told apart from one the user has edited, so it is ASKED |
| 2050 | // rather than replaced — an offer, not a warning. `{name}` is the capp's own |
| 2051 | // name, `{files}` the list of files left alone, `{why}` a reason already in |
| 2052 | // the reader's language. |
| 2053 | // |
| 2054 | // "lanes" is the Life log page's own word for its columns, and that page is |
| 2055 | // in English whatever the app is set to. It is left in English here for the |
| 2056 | // same reason Diamond is: a translated word for a thing labelled otherwise |
| 2057 | // on the screen sends the reader looking for something that is not there. |
| 2058 | 'capp.legacy_body': 'This {name} was made before capps carried a version, so Daimond cannot tell which parts of its page are the ones it was given. Bring its page up to the current one? Your lanes and your entries are left alone.', |
| 2059 | // Says WHAT it updates. The Reset control beside it does something quite |
| 2060 | // different, and a bare "Update" invites the two to be confused. |
| 2061 | 'capp.legacy_ok': 'Update the page', |
| 2062 | 'capp.update_kept': 'The page has been brought up to date. These files are yours and were left as they are: {files}.', |
| 2063 | 'capp.update_failed': 'The page could not be replaced: {why}', |
| 2064 | // A capp's page resets to ITS OWN template, not to a blank or standard page: |
| 2065 | // `crystal.page_reset_confirm` is the one that says "the standard one" and it |
| 2066 | // is a different sentence for a different case. Nothing here may imply the |
| 2067 | // page comes back empty. |
| 2068 | 'capp.page_reset_confirm': 'Replace this Diamond’s page with the current {name} page? What you have logged is not touched.', |
| 2069 | 'crystal.steering': 'Steering…', |
| 2070 | 'crystal.no_key_steer': 'This Diamond’s provider has no readable key. Unlock, or add one, to steer it.', |
| 2071 | 'crystal.page_failed': 'This Diamond’s page did not load, so its data is shown instead.', |
| 2072 | 'crystal.page_partial': 'This Diamond’s page did not show everything it holds, so its data is shown instead.', |
| 2073 | 'crystal.page_reset': 'Reset the page', |
| 2074 | 'crystal.page_reset_confirm': 'Replace this Diamond’s page with the standard one? Its data is not touched.', |
| 2075 | 'crystal.ask': 'Ask the daimon to change this page', |
| 2076 | 'crystal.edit_json': 'Edit as JSON', |
| 2077 | 'crystal.json_invalid': 'That is not valid JSON, so nothing was saved.', |
| 2078 | 'crystal.field_title': 'Title', |
| 2079 | 'crystal.field_summary': 'Summary', |
| 2080 | 'crystal.field_sections': 'Sections', |
| 2081 | 'crystal.field_facts': 'Facts', |
| 2082 | 'crystal.field_open': 'Open threads', |
| 2083 | 'crystal.field_links': 'Links', |
| 2084 | 'crystal.field_heading': 'Heading', |
| 2085 | 'crystal.field_body': 'Body', |
| 2086 | 'crystal.add_section': 'Add a section', |
| 2087 | 'crystal.remove': 'Remove', |
| 2088 | 'crystal.other_fields': 'Other fields', |
| 2089 | 'crystal.other_fields_note': 'Kept as they are, and shown here so nothing vanishes.', |
| 2090 | |
| 2091 | // ── Artefacts ────────────────────────────────────────────── |
| 2092 | 'arte.count.one': '{n} artefact', |
| 2093 | 'arte.count.other': '{n} artefacts', |
| 2094 | 'arte.strip_help': 'What this Diamond produced or consulted', |
| 2095 | 'arte.refer_help': 'Refer to this in the steer box', |
| 2096 | 'arte.drop_help': 'Not an artefact of this Diamond', |
| 2097 | 'arte.drop_named': 'Drop "{name}" as an artefact', |
| 2098 | 'arte.file_fenced': 'That path is outside what this Diamond may read', |
| 2099 | 'arte.file_fenced_body': 'The fence stopped the read. Grant the folder to this Diamond, or move the file inside one it already has.', |
| 2100 | 'arte.file_gone': 'That file is not there any more', |
| 2101 | 'arte.file_gone_body': '“{path}” is an artefact of this Diamond, but it cannot be read now; it may have been renamed, moved or deleted.', |
| 2102 | 'arte.nothing_to_open': 'Nothing to open', |
| 2103 | 'arte.no_viewer': 'This artefact is a \u201c{kind}\u201d, which this version has no viewer for.', |
| 2104 | |
| 2105 | // ── Attach to focus (ATTACH_CONTRACT.md) ──────────── |
| 2106 | 'attach.to_focus': 'Attach to current focus', |
| 2107 | 'attach.note': 'Note', |
| 2108 | 'attach.read': 'Read', |
| 2109 | 'attach.note_help': 'The path is worth knowing. Costs a few tokens.', |
| 2110 | 'attach.read_help': 'The contents are wanted now. A file can run to thousands of tokens.', |
| 2111 | 'attach.add': 'Attach', |
| 2112 | 'attach.view_stack': 'Stack', |
| 2113 | 'attach.view_icons': 'Icons', |
| 2114 | 'attach.prefix_note': 'Note {paths}', |
| 2115 | 'attach.prefix_read': 'Read {paths} in full.', |
| 2116 | 'attach.cleared': 'Attachments cleared', |
| 2117 | 'attach.list': 'Attachments', |
| 2118 | 'attach.pick_title': 'Attach files and folders', |
| 2119 | 'attach.pick_empty': 'Nothing here.', |
| 2120 | 'attach.already': 'Already attached', |
| 2121 | 'attach.not_here': 'Held by this chat, but it lives {where}, which is not the workspace you have open. This chat cannot reach it from here.', |
| 2122 | // The workspace group in the chat footer. Its heading is not here: it is |
| 2123 | // `astat.workspace_browser` / `astat.workspace_native`, so the footer, the |
| 2124 | // account strip and the guide say the same words about the same idea. |
| 2125 | 'attach.ws_mark': 'Workspace', |
| 2126 | 'attach.ws_add': 'Mark a folder into the workspace', |
| 2127 | 'attach.add_mark': 'Mark a folder in', |
| 2128 | 'attach.mark_note': 'Ticking a FOLDER marks it in: this workspace may then read and change what is inside. Ticking a file only puts it in front of the model.', |
| 2129 | 'attach.mark_focus': 'Mark this folder into {name}, so its daimon may read and change what is inside', |
| 2130 | 'attach.ws_help': 'The folders this chat may read and change. Marking one in is the permission, and nothing will ask you again.', |
| 2131 | 'attach.ws_on': 'In this chat’s workspace: it may read and change what is inside. Press to take it out.', |
| 2132 | 'attach.ws_off': 'Not in the workspace, so this chat cannot open it. Press to mark it in.', |
| 2133 | 'attach.ws_empty': 'No folder is marked in. This chat can reach nothing of yours, only its own working folder. Mark one in with the + above.', |
| 2134 | 'attach.group_prompt': 'In front of the model', |
| 2135 | 'attach.group_prompt_help': 'Named or quoted in the prompt when you send. This grants no reach.', |
| 2136 | 'attach.read_block': 'The contents of {path}:', |
| 2137 | 'attach.read_cut': 'That is as much of {path} as fits here.', |
| 2138 | |
| 2139 | // ── This Diamond's workspace ─────────────────────────────── |
| 2140 | 'dws.title': 'This Diamond\u2019s workspace', |
| 2141 | 'dws.count.one': '{n} item', |
| 2142 | 'dws.count.other': '{n} items', |
| 2143 | 'dws.none_yet': 'Nothing kept here yet', |
| 2144 | 'dws.mode_all': 'Everything', |
| 2145 | 'dws.mode_diamond': 'This Diamond', |
| 2146 | 'dws.empty': 'Nothing is kept with this Diamond yet. What you keep here is what its daimon can open.', |
| 2147 | // An attachment records the workspace it was made in, so one made on the |
| 2148 | // machine can say so when the browser sandbox is what is open. |
| 2149 | 'dws.not_here': 'Kept with this Diamond, but it lives {where}, which is not the workspace you have open. Its daimon cannot reach it from here.', |
| 2150 | 'dws.in_machine': 'in the folder "{name}" on this machine', |
| 2151 | 'dws.in_browser': 'in the browser workspace', |
| 2152 | 'dws.a_folder': 'a folder', |
| 2153 | 'dws.attach_dir': 'Keep this folder with {name}', |
| 2154 | 'dws.detach_dir': 'Stop keeping this folder with {name}', |
| 2155 | 'dws.elsewhere': 'Lives in the workspace', |
| 2156 | 'dws.readonly': 'Read only', |
| 2157 | 'dws.showing': 'Showing', |
| 2158 | 'dws.reach': 'Reach', |
| 2159 | 'dws.reach_help': 'The folders this Diamond’s daimon may write in. Shown in both trees, because it is true in both.', |
| 2160 | 'dws.reach_own': 'its own folder', |
| 2161 | 'dws.reach_search': 'A search that names no path looks in these and nowhere else.', |
| 2162 | 'dws.reach_none': 'Nothing else is marked in, so {name}’s daimon can write nowhere else of yours — and a search that names no path looks only here.', |
| 2163 | 'dws.mark_here': 'Mark “{name}” in', |
| 2164 | 'dws.mark_here_help': 'Put the folder you are looking at into {name}’s workspace. Its daimon may then read and change what is inside, and a search that names no path will look there.', |
| 2165 | 'dws.kits': 'Toolchains', |
| 2166 | 'dws.kits_help': 'Which compilers and package managers a command from this Diamond may reach on your computer. Off unless you say so; no daimon chooses this.', |
| 2167 | 'dws.kit_on': 'Grant the {kit} toolchain to {name}', |
| 2168 | 'dws.kit_off': 'Take the {kit} toolchain back from {name}', |
| 2169 | 'dws.kit_failed': 'That toolchain grant was not saved', |
| 2170 | 'dws.kit_none': 'No toolchain. A command can reach this Diamond\u2019s files and nothing else on your computer.', |
| 2171 | |
| 2172 | // \u2500\u2500 Links between Diamonds \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 |
| 2173 | 'link.count.one': '{n} linked Diamond', |
| 2174 | 'link.count.other': '{n} linked Diamonds', |
| 2175 | 'link.strip_help': 'How this Diamond stands to your other Diamonds', |
| 2176 | 'link.none': 'No links to another Diamond yet.', |
| 2177 | 'link.this': 'this', |
| 2178 | 'link.rel_blank': 'linked', |
| 2179 | 'link.out_help': 'This Diamond \u2192 {rel} \u2192 \u201c{name}\u201d', |
| 2180 | 'link.in_help': '\u201c{name}\u201d \u2192 {rel} \u2192 this Diamond', |
| 2181 | 'link.open_other': 'Show \u201c{name}\u201d', |
| 2182 | 'link.note_help': 'Show the note on this link', |
| 2183 | 'link.drop': 'Remove link', |
| 2184 | 'link.drop_help': 'Remove this link', |
| 2185 | 'link.drop_confirm': 'Remove the \u201c{rel}\u201d link with \u201c{name}\u201d? Both Diamonds stay; only the link goes.', |
| 2186 | 'link.drop_failed': 'Could not remove the link', |
| 2187 | 'link.gone': 'That Diamond is not there any more', |
| 2188 | 'link.gone_body': '“{ref}” is linked from this Diamond, but no Diamond of that id can be read now; it may have been deleted.', |
| 2189 | 'link.gone_name': '(deleted Diamond)', |
| 2190 | 'link.add_btn': 'Link', |
| 2191 | 'link.add_title': 'Link this Diamond to another', |
| 2192 | 'link.pick_label': 'Diamond', |
| 2193 | 'link.pick_ph': 'Find a Diamond by name', |
| 2194 | 'link.pick_none': 'No Diamond matches that.', |
| 2195 | 'link.pick_empty': 'There is no other Diamond to link to yet.', |
| 2196 | 'link.change_pick': 'Change', |
| 2197 | 'link.rel_label': 'Relation', |
| 2198 | 'link.rel_sug_help': 'Suggestions only; any word will do', |
| 2199 | 'link.rel_use': 'Use \u201c{rel}\u201d', |
| 2200 | 'link.note_label': 'Note', |
| 2201 | 'link.note_ph': 'Why, in a sentence (optional)', |
| 2202 | 'link.direction_note': 'The link runs from this Diamond to the one you pick.', |
| 2203 | 'link.need_target': 'Choose the Diamond to link to first.', |
| 2204 | 'link.save': 'Add link', |
| 2205 | 'link.graph_open': 'Graph', |
| 2206 | |
| 2207 | // ── Agents ───────────────────────────────────────────────── |
| 2208 | 'agents.none_yet': 'No agents yet. Ask a Diamond to start one and it appears here.', |
| 2209 | 'agents.no_match': 'No agents match. Clear the search or filter.', |
| 2210 | 'agents.fold_in': 'Fold in', |
| 2211 | 'agents.fold_in_help': 'Fold this agent\u2019s summary into "{name}"', |
| 2212 | 'agents.folded': 'folded', |
| 2213 | 'agents.already_folded': 'Already folded', |
| 2214 | 'agents.already_folded_body': 'This agent\u2019s summary has already been folded into the crystal.', |
| 2215 | 'agents.a_diamond': 'Diamond', |
| 2216 | 'agents.clear_diamond_filter': 'Clear the Diamond filter', |
| 2217 | 'agents.clear_chat_filter': 'Clear the chat filter', |
| 2218 | 'instructions.chip_help': 'Your standing instructions, given to every agent. Click to open.', |
| 2219 | 'instructions.chip_empty': 'Nothing here yet. This file goes to every agent Daimond runs. Click to write it.', |
| 2220 | 'instructions.chip_two': 'Two layers are in force, yours and this project’s. Click to open yours.', |
| 2221 | |
| 2222 | // ── The System section: Daimond's own store ──────────────── |
| 2223 | 'sys.head': 'System', |
| 2224 | 'sys.note': 'Daimond\u2019s own files. They live in the browser, not in the folder you have open, and they travel between your devices.', |
| 2225 | 'sys.empty': 'nothing here yet', |
| 2226 | 'sys.up': 'Up one folder', |
| 2227 | |
| 2228 | // ── What each kind of agent is told ──────────────────────── |
| 2229 | // The five roles of `Prompts.roles` in daimond.js, each carrying the key for |
| 2230 | // its own name and note. |
| 2231 | // i18n-indirect: daimond.js r.label = role.chat role.daimon role.worker role.reducer role.compactor |
| 2232 | // i18n-indirect: daimond.js r.blurb = role.chat_help role.daimon_help role.worker_help role.reducer_help role.compactor_help |
| 2233 | 'role.chat': 'Chat', |
| 2234 | 'role.chat_help': 'The agent you talk to.', |
| 2235 | 'role.daimon': 'Diamond daimon', |
| 2236 | 'role.daimon_help': 'Keeps a Diamond\u2019s crystal, and dispatches workers.', |
| 2237 | 'role.worker': 'Dispatched worker', |
| 2238 | 'role.worker_help': 'One task, its own context, reports back.', |
| 2239 | 'role.reducer': 'Crystal fold', |
| 2240 | 'role.reducer_help': 'Folds one delta into the crystal.', |
| 2241 | // ── Triggered actions ────────────────────────────────────── |
| 2242 | 'trig.head': 'When this Diamond acts', |
| 2243 | 'trig.none': 'Nothing set. This Diamond answers when you prompt it and at no other time.', |
| 2244 | 'trig.activity': 'After {n} minutes of your activity', |
| 2245 | 'trig.mail': 'When mail arrives in {folder} ({mailbox})', |
| 2246 | 'trig.edit': 'Edit this action', |
| 2247 | 'trig.choose': 'Which action', |
| 2248 | 'trig.opt_on': '▶ {what}', |
| 2249 | 'trig.opt_off': '⏸ {what}', |
| 2250 | 'trig.nothing_written': 'nothing written', |
| 2251 | 'trig.edit_this': 'Edit the {what}', |
| 2252 | 'trig.copy_this': 'Copy the {what}', |
| 2253 | 'trig.copy': 'Copy its instruction and context', |
| 2254 | 'trig.copied': 'Copied.', |
| 2255 | 'trig.copy_failed': 'The clipboard refused. Open the action and copy from the box.', |
| 2256 | 'trig.remove': 'Remove', |
| 2257 | 'trig.remove_body': 'Remove “{what}”? This Diamond will stop acting on it.', |
| 2258 | 'trig.add': 'Add an action', |
| 2259 | 'trig.add_kind': 'What sets it off', |
| 2260 | // The trigger editor builds its rows from helpers handed a key: `field`, |
| 2261 | // `longText` and the tile dialog's `row`, whose callers name these outright. |
| 2262 | // i18n-indirect: daimond.js pair[1] = trig.kind_activity trig.kind_mail |
| 2263 | // i18n-indirect: daimond.js labelKey = trig.minutes trig.mailbox trig.folder trig.instruction trig.context tile.model_daimon tile.model_workers tile.model_vision |
| 2264 | // i18n-indirect: daimond.js phKey = trig.instruction_ph trig.context_ph |
| 2265 | // i18n-indirect: daimond.js helpKey = tile.model_daimon_help tile.worker_model_help tile.model_vision_help |
| 2266 | 'trig.kind_activity': 'Minutes of my activity', |
| 2267 | 'trig.kind_mail': 'Mail arriving', |
| 2268 | 'trig.note': 'These live in {path}, where you and this Diamond can both read them.', |
| 2269 | 'trig.edit_title': 'Edit: {what}', |
| 2270 | 'trig.minutes': 'Minutes', |
| 2271 | 'trig.minutes_note': 'Minutes you are working, not minutes on the clock. A tab left open overnight counts none of them.', |
| 2272 | 'trig.mailbox': 'Mailbox', |
| 2273 | 'trig.folder': 'Folder', |
| 2274 | 'trig.no_mailbox': 'No mailbox set up yet', |
| 2275 | 'trig.instruction': 'Instruction', |
| 2276 | 'trig.instruction_ph': 'What to ask this Diamond to do when it fires…', |
| 2277 | 'trig.context': 'Context', |
| 2278 | 'trig.context_ph': 'Background it needs the first time, and only the first time…', |
| 2279 | 'trig.context_note': 'Sent once, in front of the first instruction. Change it and it is sent again.', |
| 2280 | 'trig.section': 'Triggered actions', |
| 2281 | |
| 2282 | 'role.compactor': 'Context fold', |
| 2283 | 'role.compactor_help': 'Summarises a conversation that has outgrown its window. Its answer becomes what the chat remembers.', |
| 2284 | |
| 2285 | // ── The Workspace panel ──────────────────────────────────── |
| 2286 | // The disclosure at the foot of the Workspace tree. Not "hidden files": the |
| 2287 | // row is the panel saying what it is not showing, so the name describes the |
| 2288 | // contents rather than labelling them, and the count says what opening it costs. |
| 2289 | 'files.rest': 'The rest of this folder', |
| 2290 | 'files.rest_count.one': '{n} entry', |
| 2291 | 'files.rest_count.other': '{n} entries', |
| 2292 | 'files.rest_help': 'Everything else in this folder: the files and folders whose names begin with a dot, and the ones Daimond keeps for its own work \u2014 your rules and skills, your mail, the prompt each kind of agent is given. Nothing here is off limits to you; it is only out of the way. Open it to read or change any of it.', |
| 2293 | 'files.back': 'Back', |
| 2294 | 'files.edit': 'Edit', |
| 2295 | 'files.stop_editing': 'Stop editing', |
| 2296 | 'files.line_numbers': 'Line numbers', |
| 2297 | 'files.download': 'Download', |
| 2298 | 'files.download_help': 'Save to this machine', |
| 2299 | 'files.hold_add': 'Keep this file with {name}', |
| 2300 | 'files.hold_drop': 'Stop keeping this file with {name}', |
| 2301 | 'files.mode_browser': 'Browser', |
| 2302 | 'files.browser_switch': 'Switch the agent back to the private in-browser workspace.', |
| 2303 | 'files.browser_help': 'The agent works in a private in-browser workspace. This is what syncs.', |
| 2304 | 'files.mode_machine': 'Machine', |
| 2305 | 'files.machine_here': 'The agent reads and writes this real folder. It does not sync.', |
| 2306 | 'files.machine_pick': 'Let the agent work in a real folder on this machine. It will not sync.', |
| 2307 | 'files.machine_needs_chromium': 'Real folders need a Chromium-based browser.', |
| 2308 | 'files.machine_reconnect': 'Reconnect {name}', |
| 2309 | 'files.mode_cloud': 'Cloud', |
| 2310 | 'files.cloud_on_machine': 'Cloud storage carries your browser workspace, not a folder on this machine.', |
| 2311 | 'files.cloud_help': 'Where your workspace lives, so it can be larger than this device.', |
| 2312 | 'files.import_folder': 'Import a folder…', |
| 2313 | 'files.import_folder_help': 'Copy a folder from this machine into the workspace, so it syncs', |
| 2314 | 'files.save_copy': 'Save a copy…', |
| 2315 | 'files.save_copy_help': 'Write the workspace out to a folder on this machine', |
| 2316 | 'files.delete': 'Delete', |
| 2317 | 'files.rename_move': 'Rename or move', |
| 2318 | 'files.rename_hint': 'A name, or a path, to move it somewhere else.', |
| 2319 | 'files.err_name': 'Enter a name.', |
| 2320 | 'files.rename_failed': 'Could not rename: {reason}', |
| 2321 | 'files.no_match': 'Nothing matches "{filter}".', |
| 2322 | 'files.new_doc_here': 'New document. Save writes it into {place}.', |
| 2323 | 'files.new_file_hint': 'Name it DAIMOND.md to write standing instructions every agent will follow.', |
| 2324 | 'files.create_failed': 'Could not create file: {reason}', |
| 2325 | 'files.create_folder_failed': 'Could not create the folder: {reason}', |
| 2326 | 'files.delete_file_body': 'Delete "{name}"? This cannot be undone.', |
| 2327 | 'files.delete_folder_body': 'Delete the folder "{name}" and everything inside it? This cannot be undone.', |
| 2328 | 'files.delete_failed': 'Could not delete {name}: {reason}', |
| 2329 | 'files.upload_failed': 'Could not upload {name}: {reason}', |
| 2330 | 'files.uploaded.one': 'Uploaded {n} file.', |
| 2331 | 'files.uploaded.other': 'Uploaded {n} files.', |
| 2332 | 'files.saving': 'Saving…', |
| 2333 | 'files.saved': 'Saved.', |
| 2334 | 'files.save_failed': 'Save failed: {reason}', |
| 2335 | 'files.save_cancelled': 'Save cancelled. The file changed on disk.', |
| 2336 | 'files.discard': 'Discard', |
| 2337 | 'files.discard_title': 'Discard your changes?', |
| 2338 | 'files.keep_editing': 'Keep editing', |
| 2339 | 'files.unsaved_body': 'Your changes to {path} have not been saved. Close the editor and lose them?', |
| 2340 | 'files.editing_stopped': 'Editing stopped. Nothing was written.', |
| 2341 | 'files.overwrite': 'Overwrite', |
| 2342 | 'files.conflict_title': 'It changed while you were editing', |
| 2343 | 'files.conflict_body': 'This file changed on disk since you opened it, most likely an agent. Save anyway and overwrite those changes?', |
| 2344 | 'files.changed_while_editing': 'This file changed on disk; an agent edited it. Your edits are kept, and saving asks before overwriting.', |
| 2345 | 'files.reloaded': 'Reloaded. The file changed on disk.', |
| 2346 | 'files.compile': 'Compile', |
| 2347 | 'files.compiling': 'compiling', |
| 2348 | 'files.compiling_path': 'Compiling {path} …', |
| 2349 | 'files.compiled': 'Compiled → {path} ({size})', |
| 2350 | 'files.compile_failed': 'Compile failed: {reason}', |
| 2351 | 'files.binary_note': 'A binary file of {size}. It is stored and synced like everything else here, but there is nothing to show. Download it to open it in something that understands it.', |
| 2352 | // Cloud storage, seen from the Workspace panel. |
| 2353 | 'files.cloud': 'Cloud', |
| 2354 | 'files.cloud_storage': 'Cloud storage', |
| 2355 | 'files.cloud_empty': 'Nothing in cloud storage yet. Your workspace is limited to what this browser grants.', |
| 2356 | 'files.cloud_locked': 'Unlock to use cloud storage. Without it your workspace is limited to what this browser grants.', |
| 2357 | 'files.cloud_some_away': '{n} files in cloud storage; {away} of them not on this device.', |
| 2358 | 'files.cloud_all_here': '{n} files in cloud storage, all of them also on this device.', |
| 2359 | 'files.cloud_intro': 'Your workspace lives in cloud storage, and this device holds as much of it as it can. {n} files, {total} in total;', |
| 2360 | 'files.cloud_away': '{n} of them ({bytes}) are not on this device.', |
| 2361 | 'files.cloud_here': 'all of them are also on this device.', |
| 2362 | 'files.cloud_none': 'Nothing is in cloud storage yet. Without it your workspace is limited to what this browser grants you.', |
| 2363 | 'files.cloud_none_quota': 'Nothing is in cloud storage yet. Without it your workspace is limited to what this browser grants you, {quota} here.', |
| 2364 | 'files.cloud_cap': 'This browser has granted {quota}, of which {used} is used. Files are freed automatically as it fills, unless you have pinned them.', |
| 2365 | 'files.reclaim': 'Free up space', |
| 2366 | 'files.reclaim_help': 'Free up space on this device now', |
| 2367 | 'files.get_help': 'Bring this file onto this device ({size})', |
| 2368 | 'files.pinned_short': 'Pinned to this device', |
| 2369 | 'files.pinned_help': 'Pinned: always kept on this device. Click to release.', |
| 2370 | 'files.pin_help': 'Pin: never free this one to make room.', |
| 2371 | 'files.free_help': 'Keep it in cloud storage; drop the copy here', |
| 2372 | 'files.fetch': 'Fetch', |
| 2373 | 'files.fetch_body': 'Bring "{path}" onto this device? That downloads {size}.', |
| 2374 | 'files.fetching': 'Fetching {path}…', |
| 2375 | |
| 2376 | // ── Where the agent works: the root, and its scope ────────── |
| 2377 | // Changing the root is a separate act from switching between the browser sandbox and the |
| 2378 | // disk. The Machine chip used to do both, so a user who had gone to the sandbox and back was |
| 2379 | // shown the folder picker every time -- and had no way to state the root's scope, which is the |
| 2380 | // one thing they most need to know before pointing an agent at a directory. |
| 2381 | 'files.change_root': 'Change folder…', |
| 2382 | 'files.change_root_help': 'Point the agent at a different folder on this machine', |
| 2383 | 'files.machine_scope': 'The agent works only inside {name}. Nothing outside that folder is visible to Daimond.', |
| 2384 | 'files.machine_return': 'Put the agent back to work in {name}', |
| 2385 | 'files.forget_root': 'Forget this folder', |
| 2386 | 'files.forget_root_help': 'Stop offering this folder. Daimond keeps no record of it, and asks you to pick one next time.', |
| 2387 | 'files.root_forgotten': 'Forgotten. Daimond no longer holds a record of that folder.', |
| 2388 | |
| 2389 | // Diamond files an earlier build left in a folder, brought back into Daimond's own storage. |
| 2390 | // Said out loud rather than done quietly: files moved, and the copies in the user's project |
| 2391 | // are still there for them to delete when they are satisfied. |
| 2392 | 'files.adopted_title.one': 'A Diamond was brought back', |
| 2393 | 'files.adopted_title.other': '{n} Diamonds were brought back', |
| 2394 | 'files.adopted_body': 'Daimond found its own files in this folder and copied them back into its own storage, where they sync: {names}. The copies were left where they are, under diamonds/; delete them once you are satisfied.', |
| 2395 | 'files.adopted_kept': 'These differed from what Daimond already held, so both copies were kept:\n{paths}', |
| 2396 | // And what did not come back. The copy runs under a budget, and a file past it is |
| 2397 | // left where it is — which the user has to be told, or a Diamond arrives missing a |
| 2398 | // part of itself and looks whole. |
| 2399 | 'files.adopt_left_title': 'Some files stayed in the folder', |
| 2400 | 'files.adopted_skipped': 'These were not copied, being over 8 MiB or unreadable, so a Diamond that needs one is here without it, or not here at all:\n{paths}', |
| 2401 | |
| 2402 | // ── The file viewer ──────────────────────────────────────── |
| 2403 | // A file is shown as what it IS: decoded where the browser can decode it, |
| 2404 | // laid out where it is text-shaped, and dumped as bytes otherwise. The floor |
| 2405 | // is the tier that matters, so its wording says what the file is and why the |
| 2406 | // bytes are what a reader is looking at, rather than apologising. |
| 2407 | // |
| 2408 | // {fmt} is a format's name, from the `fileview.fmt.*` lookup the viewer does |
| 2409 | // per media variant; where a locale has no entry the library's own English |
| 2410 | // label ("PDF document") stands. |
| 2411 | // |
| 2412 | // That lookup is an OPEN extension point with no members, deliberately: the |
| 2413 | // variant comes from `fe2o3_stds::media`, whose set of formats grows in the |
| 2414 | // Rust library and not here, so any list written in this file would be wrong |
| 2415 | // by the next release. A locale that wants to name a PDF in its own language |
| 2416 | // adds `fileview.fmt.<variant>` and it is picked up; adding none costs the |
| 2417 | // library's English and nothing else. |
| 2418 | // i18n-family: fileview.fmt. = open -- the variants are media names from fe2o3_stds::media, which this app does not own |
| 2419 | // |
| 2420 | // {shown}, {total}, {from} and {to} are |
| 2421 | // already formatted numbers -- grouped by the reader's own locale -- so they |
| 2422 | // are never re-formatted here. |
| 2423 | 'fileview.read_failed': 'This file could not be read: {reason}', |
| 2424 | 'fileview.disagree': 'The name says {named}. The bytes say {found}, and the bytes are what is shown.', |
| 2425 | 'fileview.empty': 'This file is empty.', |
| 2426 | 'fileview.too_large': 'A {fmt} of {size} is too large to hold in memory here. Its bytes follow; download it to open it elsewhere.', |
| 2427 | 'fileview.decode_failed': 'This browser could not decode this {fmt}.', |
| 2428 | 'fileview.frame_title': 'The contents of {name}', |
| 2429 | 'fileview.json_bad': 'This is not one JSON value, so it is shown as text.', |
| 2430 | 'fileview.tree_capped': 'The tree is cut short here; the file is larger than it shows.', |
| 2431 | 'fileview.rows_capped': 'Showing the first {shown} rows of {total}.', |
| 2432 | 'fileview.capped': 'Showing the first {shown} of {total}.', |
| 2433 | // The honest floor. Two sentences rather than one with a hole in it: with the |
| 2434 | // format unknown, "no viewer here for a Unknown" is what the hole fills with. |
| 2435 | 'fileview.hex_note': 'There is no viewer here for a {fmt}, so these are its bytes.', |
| 2436 | 'fileview.hex_note_unknown': 'Nothing here recognises this file, so these are its bytes.', |
| 2437 | 'fileview.hex_prev': 'Earlier bytes', |
| 2438 | 'fileview.hex_next': 'Later bytes', |
| 2439 | 'fileview.hex_at': 'Bytes {from} to {to} of {total}', |
| 2440 | |
| 2441 | // A Word document or a spreadsheet, unpacked into what it SAYS rather than |
| 2442 | // drawn as it prints. Every line here is about the gap between those two, |
| 2443 | // so none of them may be shortened into reassurance: "reading view" is the |
| 2444 | // whole warning, and a file whose macros are not run has to say so before a |
| 2445 | // reader assumes this opened it the way their office suite would. |
| 2446 | // |
| 2447 | // {why} is a reason from the reader library and arrives in English. {n}, |
| 2448 | // {shown}, {rows} and {cols} are already formatted numbers. The English |
| 2449 | // writes "(s)" rather than a plural pair because the count is the key's own |
| 2450 | // argument; a language that pluralises properly does so in its own file. |
| 2451 | 'fileview.office_too_large': 'A {fmt} of {size} is too large to unpack here. Its bytes follow.', |
| 2452 | 'fileview.office_failed': 'This document could not be read: {why}', |
| 2453 | 'fileview.office_reading': 'Reading view. This is what the document says, not how it prints.', |
| 2454 | 'fileview.office_tracked': '{n} tracked insertion(s) are shown as accepted; deletions are not shown.', |
| 2455 | 'fileview.office_macros': 'This file contains macros. They are not run and not read.', |
| 2456 | 'fileview.office_undrawn': '{total} things are not drawn: {parts}.', |
| 2457 | 'fileview.sheet_failed': 'This spreadsheet could not be read: {why}', |
| 2458 | 'fileview.sheet_stored': 'Values are as stored in the file. Formulas are not recalculated.', |
| 2459 | 'fileview.sheet_capped': 'Showing {shown} of {rows} rows and {cols} columns of this sheet.', |
| 2460 | 'fileview.sheet_formulas': '{n} cell(s) here carry a formula; the value shown is the stored one.', |
| 2461 | 'fileview.sheet_missing': '{n} sheet(s) are named by this workbook and could not be read: {names}.', |
| 2462 | |
| 2463 | // Saving a copy, and making an edit. Two sentences here are load-bearing and |
| 2464 | // neither is reassurance. |
| 2465 | // |
| 2466 | // `fileview.save_help` says the file itself does not change, because that is |
| 2467 | // the question a person asks before pressing a button on somebody's document |
| 2468 | // and the answer is what makes it safe to press. `fileview.edited` says the |
| 2469 | // same thing from the other side: once an edit has been applied the panel is |
| 2470 | // showing prose nothing else in the app can see, and a reader who closed it |
| 2471 | // believing the file had changed would have lost the edit without being told. |
| 2472 | // |
| 2473 | // {why} is a reason from the editor and arrives in English. {names} are paths |
| 2474 | // out of the user's own Markdown, so they arrive in no language at all -- the |
| 2475 | // phrase around them is composed below rather than handed over finished. |
| 2476 | // {which} is `fileview.edit_nth` in this locale's own words, so the sentence |
| 2477 | // quotes the label the reader can actually see. |
| 2478 | // Two of these are reached through a variable and both are CLOSED lists: the |
| 2479 | // save-as buttons come from one table in viewer.js and the edit row's fields |
| 2480 | // from one helper, so a key added to either without a line here goes red. |
| 2481 | // i18n-indirect: viewer.js key = fileview.edit_find fileview.edit_replace fileview.edit_nth fileview.edit_cell fileview.edit_value |
| 2482 | 'fileview.save': 'Save a copy', |
| 2483 | 'fileview.save_help': 'Save a copy of this to your own device. The file here is not changed.', |
| 2484 | 'fileview.save_failed': 'This could not be saved: {why}', |
| 2485 | 'fileview.save_capped': 'Only the start of this file is on screen, so it is not written out as a document: what came back would be a document missing its end.', |
| 2486 | 'fileview.save_as': 'Save as a document', |
| 2487 | 'fileview.save_as_help': 'Write this text out as a real document and save it to your own device. The file here is not changed.', |
| 2488 | 'fileview.save_as_failed': 'This could not be saved as {fmt}: {why}', |
| 2489 | // What a document written from this text will NOT carry. Composed here from a |
| 2490 | // kind, a count and the source names, exactly as `fileview.undrawn_*` is -- |
| 2491 | // `office_write_left` used to hand back a finished English sentence, which was |
| 2492 | // the one string in that panel a translation pass could not reach. |
| 2493 | // |
| 2494 | // Two forms per kind: a bare count, and the same count with the sources named. |
| 2495 | // A named form is a different sentence and not the first with a list bolted |
| 2496 | // on, which is why both are keys rather than one plus a separator. |
| 2497 | // i18n-family: fileview.left_ = image notes image_named notes_named |
| 2498 | 'fileview.write_left': 'Not everything in this text reaches the document: {parts}.', |
| 2499 | 'fileview.left_image': '{n} image(s)', |
| 2500 | 'fileview.left_notes': '{n} slide(s) of speaker’s notes', |
| 2501 | 'fileview.left_image_named': '{n} image(s): {names}', |
| 2502 | 'fileview.left_notes_named': '{n} slide(s) of speaker’s notes: {names}', |
| 2503 | 'fileview.edit': 'Make an edit', |
| 2504 | 'fileview.edit_find': 'Find', |
| 2505 | 'fileview.edit_replace': 'Replace with', |
| 2506 | 'fileview.edit_nth': 'Which one', |
| 2507 | 'fileview.edit_note': 'Leave “{which}” blank to change every one. Everything else in the file is left byte for byte as it was.', |
| 2508 | 'fileview.edit_apply': 'Apply', |
| 2509 | 'fileview.edit_failed': 'That edit was not made: {why}', |
| 2510 | 'fileview.edit_nothing': 'the editor returned no document', |
| 2511 | 'fileview.edited': 'Edited here, {n} time(s). The file itself has not changed — save a copy to keep this.', |
| 2512 | 'fileview.edit_sheet': 'Sheet', |
| 2513 | 'fileview.edit_cell': 'Cell', |
| 2514 | 'fileview.edit_value': 'Value', |
| 2515 | 'fileview.edit_cell_note': 'A value beginning with “=” is stored as a formula. Nothing is recalculated, here or in the file.', |
| 2516 | |
| 2517 | // What the reading view did not draw, by kind and by count, joined into |
| 2518 | // `fileview.office_undrawn` above. The number and the kind are BOTH the |
| 2519 | // information: "3 text boxes, 1 chart" tells a reader whether to go and |
| 2520 | // open the file properly, where "some content is not shown" tells them only |
| 2521 | // that this viewer cannot be trusted. |
| 2522 | // |
| 2523 | // CLOSED, and it can be, because the kinds are the nine variants of |
| 2524 | // `Undrawable` in fe2o3_file's docx reader rather than anything this file |
| 2525 | // invents. A tenth added there fails this check, which is the point: the |
| 2526 | // alternative is a raw key painted on the screen in every language. |
| 2527 | // i18n-family: fileview.undrawn_ = image chart diagram textbox object |
| 2528 | // i18n-family: fileview.undrawn_ = equation footnote endnote comment |
| 2529 | 'fileview.undrawn_image': '{n} image(s)', |
| 2530 | 'fileview.undrawn_chart': '{n} chart(s)', |
| 2531 | 'fileview.undrawn_diagram': '{n} diagram(s)', |
| 2532 | 'fileview.undrawn_textbox': '{n} text box(es)', |
| 2533 | 'fileview.undrawn_object': '{n} embedded object(s)', |
| 2534 | 'fileview.undrawn_equation': '{n} equation(s)', |
| 2535 | 'fileview.undrawn_footnote': '{n} footnote(s)', |
| 2536 | 'fileview.undrawn_endnote': '{n} endnote(s)', |
| 2537 | 'fileview.undrawn_comment': '{n} comment(s)', |
| 2538 | |
| 2539 | // ── One message, read ────────────────────────────────────── |
| 2540 | 'msg.unknown_sender': '(unknown sender)', |
| 2541 | 'msg.reply_to': 'Reply-to', |
| 2542 | 'msg.reply': 'Reply', |
| 2543 | 'msg.reply_all': 'Reply all', |
| 2544 | 'msg.forward': 'Forward', |
| 2545 | 'msg.save_help': 'Save into the workspace', |
| 2546 | 'msg.saved_to': 'Saved · {path}', |
| 2547 | 'msg.save_failed': 'Could not save {name}', |
| 2548 | 'msg.pictures_blocked': 'Pictures and other remote content are blocked. Loading them tells the sender you opened this.', |
| 2549 | 'msg.load_pictures': 'Load pictures', |
| 2550 | 'msg.no_text': '(This message has no readable text part.)', |
| 2551 | |
| 2552 | // ── Writing one ──────────────────────────────────────────── |
| 2553 | 'compose.new_message': 'New message', |
| 2554 | 'compose.draft': 'Draft', |
| 2555 | 'compose.remove_attachment': 'Remove this attachment', |
| 2556 | 'compose.err_no_to': 'Say who it is going to.', |
| 2557 | 'compose.send_title': 'Send this message?', |
| 2558 | 'compose.send_body': 'It will be posted through {from}, and cannot be recalled.', |
| 2559 | 'compose.discard_title': 'Discard this draft?', |
| 2560 | 'compose.discard_body': 'The draft is deleted and what is written in it is lost.', |
| 2561 | |
| 2562 | // ── Identity, at the gate ────────────────────────────────── |
| 2563 | 'identity.err_enter_pass': 'Enter a passphrase.', |
| 2564 | 'identity.err_choose_name': 'Choose a name.', |
| 2565 | 'identity.err_confirm_written': 'Confirm you have written the passphrase down first.', |
| 2566 | 'identity.err_too_short_gen': 'That passphrase is too short. Generate another, or choose your own.', |
| 2567 | 'identity.err_too_short': 'Use a passphrase of at least 8 characters.', |
| 2568 | 'identity.err_mismatch': 'The passphrases do not match.', |
| 2569 | 'identity.err_create': 'Could not create the account.', |
| 2570 | 'identity.err_wrong_pass': 'That passphrase did not match. Try again.', |
| 2571 | 'identity.err_locked': 'Daimond identity is locked.', |
| 2572 | 'identity.err_no_webcrypto': 'WebCrypto is unavailable in this browser.', |
| 2573 | 'identity.err_unsupported_crypto': 'This browser cannot do the cryptography this account needs (Ed25519/X25519). Update your browser, or open the account in a newer one.', |
| 2574 | |
| 2575 | // ── Passkeys, in the interface ───────────────────────────── |
| 2576 | 'passkey.have_one': 'I have a passkey for Daimond', |
| 2577 | 'passkey.use_one': 'Use a passkey', |
| 2578 | 'passkey.browser_cannot': 'This browser cannot use your passkey, so the passphrase is the way in here.', |
| 2579 | 'passkey.adopt_note': 'If you added a passkey on another device, it brings that account here without a pairing code or a passphrase.', |
| 2580 | 'passkey.looking': 'Looking for your passkey…', |
| 2581 | 'passkey.waiting': 'Waiting for your passkey…', |
| 2582 | 'passkey.err_unusable': 'The passkey could not be used.', |
| 2583 | 'passkey.err_did_not_work': 'That passkey did not work. Use your passphrase.', |
| 2584 | 'passkey.err_enter_pass': 'Enter your passphrase.', |
| 2585 | 'passkey.add_title': 'Add a passkey', |
| 2586 | 'passkey.add_body': 'Confirm your passphrase to protect it with a passkey. Your device then asks you to create it: Face ID, Touch ID, Windows Hello or a security key.', |
| 2587 | 'passkey.continue': 'Continue', |
| 2588 | 'passkey.not_added': 'Passkey not added', |
| 2589 | 'passkey.err_create_failed2': 'The passkey could not be created.', |
| 2590 | 'passkey.err_create_retry': 'The passkey could not be created. You can try again from Settings.', |
| 2591 | 'passkey.added': 'Passkey added', |
| 2592 | 'passkey.added_synced': 'You can now unlock Daimond with your passkey. It syncs between your own devices, so it brings this account to a new phone or laptop without a pairing code. Your passphrase still works, and remains the fallback.', |
| 2593 | 'passkey.added_local': 'You can now unlock Daimond on this device with your passkey. The account service could not be reached, so for now it works here only; add it again once you are online and it will carry the account to your other devices. Your passphrase still works, and remains the fallback.', |
| 2594 | 'passkey.added_offer': 'Next time, Daimond will ask for your passkey instead of your passphrase. You can remove it from Settings at any time.', |
| 2595 | 'passkey.remove_title': 'Remove passkey?', |
| 2596 | 'passkey.remove_body': 'The passkey comes off this device and stops opening your account anywhere else, so you unlock with your passphrase. The passkey itself stays in your authenticator until you delete it there.', |
| 2597 | 'passkey.removed': 'Passkey removed', |
| 2598 | 'passkey.removed_body': 'This device will ask for your passphrase from now on.', |
| 2599 | 'passkey.offer_title': 'Unlock with your face?', |
| 2600 | 'passkey.offer_body': 'Daimond can unlock with Face ID, Touch ID or your device PIN instead of the passphrase. Your passkey syncs between your devices, so it carries this account to them as well. Your passphrase keeps working either way.', |
| 2601 | |
| 2602 | // ── Forgetting an account ────────────────────────────────── |
| 2603 | 'forget.credits_title': 'Save your credits first?', |
| 2604 | 'forget.credits_body': 'This account holds {amount}, kept on Daimond’s server and unlocked only by this identity. A backup saves the identity encrypted, so another device can take it over, but only with this account’s passphrase, which the backup does not contain. Lose the passphrase and nothing brings the balance back. Export a backup now?', |
| 2605 | 'forget.skip': 'Skip', |
| 2606 | 'forget.title': 'Forget this account?', |
| 2607 | 'forget.ok': 'Erase everything', |
| 2608 | 'forget.body': 'This erases your passphrase, your encrypted API key, and all of your chats, Diamonds and spend history on this device.', |
| 2609 | 'forget.tail': 'There is no recovery. Everything is gone.', |
| 2610 | 'forget.body_secondary': 'This removes the account “{name}” from this browser: its passphrase, keys, chats, Diamonds, spend and files.', |
| 2611 | 'forget.tail_secondary': 'There is no recovery. Other accounts here are untouched.', |
| 2612 | 'forget.abandons': 'It also abandons {amount} held on the server. Only this identity unlocks it, and only this account\u2019s passphrase opens a backup of the identity.', |
| 2613 | |
| 2614 | // ── Changing a name, and a passphrase ────────────────────── |
| 2615 | 'rename.title': 'Change name', |
| 2616 | 'rename.failed': 'Could not rename', |
| 2617 | 'changepass.lead': 'This replaces your current passphrase. There is no recovery, so write the new one down before you continue.', |
| 2618 | 'changepass.gen_note': 'Eight words picked at random by this device, about {bits} bits, far past anything that can be guessed. It becomes the key to everything you store here, and nobody can reset it for you. Write it on paper. A password manager may also offer to update it, which is safe at this strength.', |
| 2619 | 'changepass.new_ph': 'New passphrase', |
| 2620 | 'changepass.again_ph': 'Type it once more', |
| 2621 | 'changepass.change_it': 'Change it', |
| 2622 | 'changepass.select_above': 'Select it above', |
| 2623 | 'changepass.err_same_gen': 'That matches your current passphrase. Generate another.', |
| 2624 | 'changepass.err_short': 'Use at least 8 characters.', |
| 2625 | 'changepass.err_same': 'That is your current passphrase. Choose a different one.', |
| 2626 | 'changepass.failed': 'That did not work', |
| 2627 | 'changepass.failed_body': 'Nothing was changed, and your current passphrase still works — carry on using it. This device could not rewrite its stored keys, which is not something you did wrong. Try again in a moment.', |
| 2628 | 'changepass.careful': 'Careful', |
| 2629 | 'changepass.key_not_resealed': 'The passphrase changed, but your API key could not be re-encrypted. Re-enter it in Settings.', |
| 2630 | 'changepass.mail_not_resealed': 'These mailboxes could not be re-encrypted under the new passphrase and need their passwords again: {list}.', |
| 2631 | 'changepass.mail_not_unsealed': 'These mailboxes could not be read under the old passphrase, so they still need their passwords set again: {list}.', |
| 2632 | // The message store, which is sealed under the passphrase like everything |
| 2633 | // else and so has to be carried across a change of it. It names no list: a |
| 2634 | // person has one message store, not a set of them. |
| 2635 | 'changepass.post_not_resealed': 'Your private messages could not be re-encrypted under the new passphrase.', |
| 2636 | 'changepass.post_not_unsealed': 'Your private messages could not be read under your old passphrase, so they have been left as they were.', |
| 2637 | 'changepass.models_not_resealed': 'These providers could not be re-encrypted under the new passphrase and need their keys again: {list}.', |
| 2638 | 'changepass.models_not_unsealed': 'These providers already had unreadable keys before the change, and still need their keys set again: {list}.', |
| 2639 | 'changepass.all_of_them': 'all of them', |
| 2640 | // The name of the API key in a list of what did not survive. The sentence a |
| 2641 | // person reads is `changepass.key_not_resealed` above. |
| 2642 | 'changepass.the_api_key': 'your API key', |
| 2643 | 'changepass.search_not_resealed': 'These search services could not be re-encrypted under the new passphrase and need their keys again: {list}.', |
| 2644 | 'changepass.search_not_unsealed': 'These search services already had unreadable keys before the change, and still need their keys set again: {list}.', |
| 2645 | 'changepass.voice_not_resealed': 'Your forge voice could not be re-encrypted under the new passphrase. Set it again from the line the forge printed for you.', |
| 2646 | 'changepass.voice_not_unsealed': 'Your forge voice could not be read under the old passphrase, so it still needs setting again from the line the forge printed for you.', |
| 2647 | 'changepass.settle_not_resealed': 'Your settle voice could not be re-encrypted under the new passphrase. Paste it again from the line the forge printed for you.', |
| 2648 | 'changepass.settle_not_unsealed': 'Your settle voice could not be read under the old passphrase, so it still needs pasting again from the line the forge printed for you.', |
| 2649 | // A module that seals something failed in a way it had no words of its own |
| 2650 | // for. Generic on purpose and rare by design: every participant in |
| 2651 | // js/rekey.js carries its own sentence, and this is what is left if one does |
| 2652 | // not — a failure named badly still being better than a failure unmentioned. |
| 2653 | 'changepass.rekey_generic': '{who}: {list}.', |
| 2654 | 'changepass.rekey_failed': 'Some of what is stored here could not be re-encrypted under the new passphrase. Check your keys and mailbox passwords in Settings.', |
| 2655 | 'changepass.changed': 'Passphrase changed', |
| 2656 | 'changepass.changed_body': 'Your new passphrase is active.', |
| 2657 | 'changepass.passkey_stale': 'Your passkey could not be updated, so it will ask for the new passphrase. Re-add it from Settings.', |
| 2658 | |
| 2659 | // ── Backups ──────────────────────────────────────────────── |
| 2660 | 'backup.unreadable': 'That backup file could not be read.', |
| 2661 | 'backup.not_a_backup': 'That is not a Daimond backup.', |
| 2662 | // A file from a NEWER Daimond. Both numbers are said because the only useful |
| 2663 | // next step is to run the Daimond that wrote it; keep both placeholders. |
| 2664 | 'backup.version_title': 'That backup is newer than this Daimond', |
| 2665 | 'backup.version_body': 'The file was written in backup format {found}, and this build reads format {known}. Nothing has been restored, and nothing in the file has been changed. Update Daimond and open it again.', |
| 2666 | 'backup.identity_title': 'Account identity restored', |
| 2667 | 'backup.identity_body': 'The backup carried the identity for “{name}”, so this browser is now that account, with the credits and Pro licence held on the server for it. Unlock with that account’s passphrase; the backup does not contain it, and nothing else opens the account.', |
| 2668 | 'backup.identity_kept': 'This browser already holds an account, so the identity for \u201c{name}\u201d in that backup was left alone. Everything else was restored.', |
| 2669 | 'backup.identity_failed': 'The identity in that backup could not be restored.', |
| 2670 | 'backup.restored': 'Backup restored', |
| 2671 | 'backup.n_files.one': '{n} workspace file', |
| 2672 | 'backup.n_files.other': '{n} workspace files', |
| 2673 | 'backup.n_diamonds.one': '{n} diamond', |
| 2674 | 'backup.n_diamonds.other': '{n} diamonds', |
| 2675 | 'backup.restored_body': 'Restored {files} and {diamonds}. Daimond will reload to open your restored workspace.', |
| 2676 | // Appended to the line above when a backup held another account's files. There are |
| 2677 | // only two places they could go — a folder named after a stranger inside this |
| 2678 | // workspace, or into that account's private storage — and neither is a restore, so |
| 2679 | // they are left out and the user is told rather than left to notice. |
| 2680 | 'backup.n_foreign.one': '{n} file in that backup belongs to another account at that browser, so it was not restored and stays only in the backup.', |
| 2681 | 'backup.n_foreign.other': '{n} files in that backup belong to another account at that browser, so they were not restored and stay only in the backup.', |
| 2682 | |
| 2683 | // ── The provider form ────────────────────────────────────── |
| 2684 | 'models.other': 'Other (type manually)…', |
| 2685 | 'models.enter_key_first': 'Enter your API key to load models…', |
| 2686 | 'models.key_rejected': 'That API key was rejected. Check it and try again.', |
| 2687 | 'models.list_failed': 'Could not load models automatically. Pick "Other" and type a model id.', |
| 2688 | |
| 2689 | |
| 2690 | // ── The provider form's refusals ─────────────────────────── |
| 2691 | 'byok.err_provider': 'Choose a provider first.', |
| 2692 | 'byok.err_base_url': 'Enter the provider base URL.', |
| 2693 | 'byok.err_bad_url': 'That base URL is not a web address. It should start with https:// and name the provider’s host.', |
| 2694 | 'byok.err_key': 'Paste your API key.', |
| 2695 | 'byok.err_model': 'Choose a model, or wait a moment for the list to load.', |
| 2696 | 'byok.err_rejected': 'The provider rejected that API key. Check it and try again.', |
| 2697 | |
| 2698 | // ── Confirming something irreversible ────────────────────── |
| 2699 | 'confirm.yes_do_it': 'Yes, do it', |
| 2700 | 'confirm.irreversible': 'Daimond wants to do something that cannot be undone', |
| 2701 | 'fold.nothing_chosen': 'Nothing chosen', |
| 2702 | 'fold.nothing_chosen_body': 'Tick the turns you want to fold, then press Fold selected.', |
| 2703 | |
| 2704 | // ── Coming back from a checkout ──────────────────────────── |
| 2705 | 'checkout.cancelled': 'Cancelled', |
| 2706 | 'checkout.cancelled_body': 'Nothing was charged and your balance is unchanged.', |
| 2707 | 'checkout.card_saved': 'Card saved', |
| 2708 | 'checkout.card_saved_body': 'Daimond can now top up your credits automatically. Set the limits below, then switch it on. Nothing has been charged.', |
| 2709 | 'checkout.card_pending_body': 'Stripe has taken the card. It may take a moment to appear here; reopen Credits shortly. Nothing has been charged.', |
| 2710 | 'checkout.pro_unlocked': 'Pro unlocked', |
| 2711 | 'checkout.pro_unlocked_body': 'You have Daimond Pro. Cross-device sync, cloud storage and Email are on. Pro runs for five years from purchase. No subscription, and nothing renews.', |
| 2712 | 'checkout.received': 'Payment received', |
| 2713 | 'checkout.pro_pending_body': 'Your Pro unlock is being confirmed and will appear here shortly. Reopen Credits in a moment.', |
| 2714 | 'checkout.credits_pending_body': 'Your credits are still being confirmed. They will appear here shortly.', |
| 2715 | |
| 2716 | // ── Going out to a checkout ──────────────────────────────── |
| 2717 | // The two ways buying can fail before Stripe is ever reached: the account |
| 2718 | // service did not answer, and it answered without the address to go to. Both |
| 2719 | // arrive at the user through `friendlyError`, which shows them verbatim. |
| 2720 | // A card and a credit pack are named apart, because a buyer who pressed |
| 2721 | // "Save a card" should not be told about a checkout they did not start. |
| 2722 | 'gateway.acct_unreachable': 'Could not reach the Daimond account service. Try again shortly.', |
| 2723 | 'gateway.session_no_url': 'The checkout session came back without a URL.', |
| 2724 | 'gateway.card_no_url': 'The card session came back without a URL.', |
| 2725 | |
| 2726 | // ── A few more from the Workspace and Agents panels ──────── |
| 2727 | 'files.folder_open_failed': 'Could not open that folder.', |
| 2728 | 'files.import': 'Import', |
| 2729 | 'files.import_body': 'Copy "{name}" into the workspace? Everything in it then syncs to your other devices and counts towards your storage.', |
| 2730 | 'files.this_file': 'this file', |
| 2731 | 'files.unsaved_close': 'Your changes to {path} have not been saved. Close it and lose them?', |
| 2732 | 'agents.no_diamond': 'no Diamond', |
| 2733 | 'agents.no_diamond_help': 'This run has no Diamond', |
| 2734 | 'agents.only_from': 'Show only agents from "{name}"', |
| 2735 | 'agents.only_from_chat': 'Show only agents from this chat.', |
| 2736 | // A run dispatched from a chat rather than a Diamond. It has no crystal, so |
| 2737 | // say where its report actually is instead of naming a thing that is missing. |
| 2738 | 'agents.from_chat': 'a chat', |
| 2739 | 'agents.from_chat_help': 'Sent from the chat “{name}”.', |
| 2740 | // The same chip when the chat has no name: the label is then the rail's derived |
| 2741 | // phrase, and quoting a description as if it were a name is what this avoids. |
| 2742 | 'agents.from_chat_help_when': 'Sent from a chat you have not named, last used {when}.', |
| 2743 | 'agents.no_diamond_fold': 'This agent was sent from a chat, so there is no crystal to fold it into. Its report is in that conversation, and its full text is under Read.', |
| 2744 | 'agents.report_one': 'An agent you sent has finished.', |
| 2745 | 'agents.report_n': '{n} agents you sent have finished.', |
| 2746 | 'agents.no_task': 'An agent was asked for with no task, so nothing was started.', |
| 2747 | 'changepass.title': 'Change passphrase', |
| 2748 | 'changepass.enter_current': 'Enter your current passphrase.', |
| 2749 | 'changepass.next': 'Next', |
| 2750 | |
| 2751 | // ── Errors the app rewrites for the user ─────────────────── |
| 2752 | 'err.unreachable': 'Could not reach that endpoint. Check the base URL in Settings, and your connection.', |
| 2753 | 'err.rejected_401': 'Your API key was rejected (401). Open Settings and check it.', |
| 2754 | 'err.denied_403': 'The provider denied access (403). Check your key and plan.', |
| 2755 | 'err.notfound_404': 'The provider answered 404. If earlier steps in this turn worked, the base URL is fine and the model could not serve this particular request — most often one carrying an image. Otherwise check the base URL in Settings.', |
| 2756 | 'err.ratelimit_429': 'The provider is rate-limiting you (429). Wait a moment and retry.', |
| 2757 | 'err.server_5xx': 'The provider had a server error. Try again shortly.', |
| 2758 | 'err.generic': 'Something went wrong. Try again.', |
| 2759 | 'err.reply_cut': 'The reply ran out of room at {max} tokens, so a tool call arrived incomplete and could not run. Raise the reply length in Settings, or ask for a smaller change', |
| 2760 | |
| 2761 | // ── How long a reply may be ──────────────────────────────── |
| 2762 | 'settings.max_tokens': 'Longest reply', |
| 2763 | 'settings.max_tokens_auto': 'Automatic', |
| 2764 | 'settings.tokens': 'tokens', |
| 2765 | 'settings.max_tokens_note': 'How long a single reply may be. Too low and a large file arrives cut in half.', |
| 2766 | 'settings.max_tokens_ceiling': 'accepts up to', |
| 2767 | |
| 2768 | // ── How far one turn may go ───────────────────── |
| 2769 | 'settings.max_rounds': 'Steps per turn', |
| 2770 | 'settings.crystal_limits': 'Size limits', |
| 2771 | 'settings.crystal_cap': 'Crystal size limit', |
| 2772 | 'settings.crystal_cap_note': 'A crystal is a Diamond’s summary, so it has a ceiling. Past it, a daimon puts the detail in a file in the Diamond’s scope.', |
| 2773 | 'settings.crystal_cap_auto': 'Default', |
| 2774 | 'settings.crystal_page_cap': 'Page size limit', |
| 2775 | 'settings.crystal_page_cap_note': 'The page that renders a Diamond’s data. It travels in every sync, so it shares the budget with the data itself.', |
| 2776 | 'settings.max_rounds_auto': 'Default', |
| 2777 | 'settings.steps': 'steps', |
| 2778 | 'settings.max_rounds_note': 'How many tool-calls one turn may take.', |
| 2779 | 'settings.max_rounds_help': 'It says so when it stops, and you can tell it to carry on.', |
| 2780 | 'settings.fold_at': 'Fold the conversation at', |
| 2781 | 'settings.fold_at_auto': 'Default', |
| 2782 | 'settings.fold_at_note': 'How full the context gets before earlier chat is summarised.', |
| 2783 | 'settings.fold_at_help': 'It says so in the thread when it folds. Lower folds sooner and more often; higher keeps more of the conversation word for word.', |
| 2784 | 'settings.fold_model': 'Fold with', |
| 2785 | 'settings.fold_model_own': 'The conversation’s own model', |
| 2786 | 'settings.fold_model_group': 'Fold with instead', |
| 2787 | 'settings.fold_model_note': 'When a conversation outgrows its window it is summarised, and the summary becomes what the model remembers. This chooses what writes it, for chats on the same provider; every other chat folds with its own model.', |
| 2788 | |
| 2789 | // ── The permission ladder ────────────────────────────────── |
| 2790 | // What Daimond does WITHOUT ASKING. Nothing here moves the fence, the |
| 2791 | // system-call filter, a Diamond's folders or the journal — say so plainly, |
| 2792 | // because a person choosing bypass is entitled to know exactly what they |
| 2793 | // have and have not switched off. |
| 2794 | 'permmode.title': 'Permission mode', |
| 2795 | 'permmode.lead': 'What Daimond does without asking', |
| 2796 | // The hover said 'Permission mode: what Daimond does without asking' -- the CATEGORY, |
| 2797 | // which is the one thing a person hovering a button marked Guarded already knows. What |
| 2798 | // it governs was written and hidden a click away in the rung blurbs. So the tooltip is |
| 2799 | // composed in `handmode.js` from the current rung's name and blurb, and this is only |
| 2800 | // what a surface with no rung to hand can say. |
| 2801 | 'permmode.chip_help': 'What Daimond does without asking', |
| 2802 | 'permmode.chip_aria': 'Permission mode: {mode}. Change it.', |
| 2803 | 'permmode.astat': 'Permissions: {mode}', |
| 2804 | 'permmode.never': 'No mode moves the fence, a Diamond’s folders, or the journal.', |
| 2805 | 'permmode.failed': 'That permission mode could not be set, so nothing changed.', |
| 2806 | |
| 2807 | // ── This chat's network ──────────────────────────────────── |
| 2808 | // The rungs above are one setting for the whole app; this is one chat's own state, |
| 2809 | // which is why it is a section and not a fourth rung. Before it existed there was no |
| 2810 | // way to see whether a chat was cut off, no way to grant it in advance, and -- once |
| 2811 | // answered -- no way to change your mind: the answer was written once and kept. |
| 2812 | // |
| 2813 | // The words are the four `DaimondApp::net_state` returns, and they are read by four |
| 2814 | // literal `t()` calls in a switch rather than a composed key, because `i18ncheck` |
| 2815 | // cannot follow a key it has to build. |
| 2816 | 'permmode.net_head': 'Commands and the network', |
| 2817 | // What this section is NOT, said beside the choice. It answers whether a |
| 2818 | // COMMAND keeps its network; a page Daimond fetches is a different door, asked |
| 2819 | // once in each conversation. The old head said "The network" and was read as |
| 2820 | // covering both, and the sentence here said "once for each site" -- true when it |
| 2821 | // was written, and false a day later, when the owner ruled that asking about |
| 2822 | // every new site was itself the defect. Both halves are now what the code does. |
| 2823 | 'permmode.net_not_fetch': 'This is about commands. A page Daimond fetches for itself is a separate question, asked once in each conversation, and a yes there covers every site until that conversation ends.', |
| 2824 | 'permmode.net_open': 'Nothing has been read from outside, so nothing is withheld.', |
| 2825 | 'permmode.net_cut': 'Something written by somebody else has been read, so commands run with no network.', |
| 2826 | 'permmode.net_allowed': 'Commands may reach the network.', |
| 2827 | 'permmode.net_refused': 'Commands run with no network.', |
| 2828 | 'permmode.net_bypass': 'Nothing is withheld in Bypass.', |
| 2829 | 'permmode.net_each': 'Ask once per chat', |
| 2830 | 'permmode.net_always': 'Always allow', |
| 2831 | 'permmode.net_never': 'Never', |
| 2832 | 'permmode.net_cut_mark': 'no network', |
| 2833 | |
| 2834 | // The three rungs of the ladder, each with a name and a note, built by |
| 2835 | // `handmode.js:44-45` from `MODES`. Declared ONE-WAY because `permmode.` is a |
| 2836 | // namespace before it is a set: sixteen keys above and below these six live |
| 2837 | // under the same prefix and are not rungs at all. The reverse sweep would |
| 2838 | // demand every one of them be declared a rung, which is a worse falsehood |
| 2839 | // than the one it catches, so it is given up HERE and nowhere else -- the |
| 2840 | // eleven other families keep it. What that costs: a seventh key under |
| 2841 | // `permmode.` that nobody declares is not reported. |
| 2842 | // i18n-family: permmode. = one-way ask ask_blurb guarded guarded_blurb bypass bypass_blurb -- the prefix is a namespace, not a set: thirty-five keys sit under it and six are rungs |
| 2843 | 'permmode.ask': 'Ask every time', |
| 2844 | 'permmode.ask_blurb': 'Every command is put to you first. So is reaching the web, once in each conversation.', |
| 2845 | 'permmode.guarded': 'Guarded', |
| 2846 | 'permmode.guarded_blurb': 'Commands run. Once something written by somebody else has been read, Daimond asks before reaching out.', |
| 2847 | 'permmode.bypass': 'Bypass', |
| 2848 | 'permmode.bypass_blurb': 'Nothing is asked, whatever has been read.', |
| 2849 | |
| 2850 | 'permmode.bypass_title': 'Stop asking?', |
| 2851 | 'permmode.bypass_body': 'Commands run on your machine and pages are fetched without asking — including after Daimond has read a page, an email or a build log written by somebody else.\n\nThat last case is what you give up: where something you did not write could talk Daimond into sending your work somewhere.\n\nUntouched: the fence a command runs inside, each Diamond’s own folders, the system-call filter, and the journal you can check afterwards — and text from outside is still marked as somebody else’s words.\n\nYou will not be asked again.', |
| 2852 | 'permmode.bypass_ok': 'Use bypass', |
| 2853 | |
| 2854 | 'permmode.run_title': 'Run this command?', |
| 2855 | 'permmode.run_body': 'Daimond wants to run a command on your machine.\n\n{cmd}\n\nin {cwd}\n\nThe “ask every time” permission mode puts every command to you first.', |
| 2856 | 'permmode.run_ok': 'Run it', |
| 2857 | |
| 2858 | // The network question a marked chat's first command puts (`hand/REVIEW.md` §1.13, |
| 2859 | // remedy 2). Not the same question as `run_body` above and it must not read like it: |
| 2860 | // that one asks whether a command may RUN, this one asks whether it may REACH OUT, |
| 2861 | // and a no here still runs the command. Both halves of the answer are still said, |
| 2862 | // because a person deciding needs to know what a yes covers and what a no costs. |
| 2863 | // |
| 2864 | // IT SAID "THIS TURN" AND MEANT THIS CHAT. The memory lives on the chat's own engine |
| 2865 | // object, which lasts until the tab is reloaded -- so a user who read the word and |
| 2866 | // expected to be asked again next message was told something untrue by the one |
| 2867 | // sentence in it that was meant to be reassuring. It now says which conversation it |
| 2868 | // covers, and points at the control that can change it afterwards. |
| 2869 | 'permmode.net_title': 'Let this command reach the network?', |
| 2870 | 'permmode.net_body': 'Daimond has read something written by somebody else, so commands are running with no network. Whatever it read could choose where this one goes.\n\n{cmd}\n\nin {cwd}\n\nYes lets this one command through. No runs it anyway, with no network — and so does saying nothing.', |
| 2871 | 'permmode.net_ok': 'Allow the network', |
| 2872 | 'permmode.net_remember': 'Allow this in every chat, and stop asking', |
| 2873 | |
| 2874 | // A dispatched worker asking to act on a page. Nobody is reading its |
| 2875 | // transcript, so the app puts the question for it — and every clause here is |
| 2876 | // load-bearing: who is acting, why the app is asking, what a click can do, |
| 2877 | // that it cannot be undone, and that the yes covers this one act only. |
| 2878 | 'permmode.act_worker_title': 'An agent wants to act on a page', |
| 2879 | 'permmode.act_worker_body': 'An agent working on its own wants to click something on {host}. You are not driving it and it cannot ask you itself, so Daimond is asking. Clicking can spend your money, send a message or submit a form, and none of that can be taken back. Saying yes allows this one click and nothing after it.', |
| 2880 | 'permmode.type_worker_body': 'An agent working on its own wants to type this into {host} and send it:\n\n{text}\n\nYou are not driving it and it cannot ask you itself, so Daimond is asking. Text sent to a site cannot be recalled. Saying yes allows this one act and nothing after it.', |
| 2881 | 'permmode.search_worker_body': 'An agent working on its own wants to search the web for:\n\n{query}\n\nYou are not driving it and it cannot ask you itself, so Daimond is asking. The query is what leaves this device. Saying yes allows this one search.', |
| 2882 | |
| 2883 | // ── When conversations stop being saved ──────────────────── |
| 2884 | // Said outright, and while the user can still act. The failure this replaces |
| 2885 | // was silent: the work went on being answered and stopped being stored, and |
| 2886 | // the user found out on the next reload. |
| 2887 | 'store.alarm': 'Your conversations are not being saved.', |
| 2888 | 'store.alarm_advice': 'Everything on screen is still here, but a reload would lose it. Download a copy now.', |
| 2889 | 'store.alarm_download': 'Download a copy', |
| 2890 | 'store.alarm_retry': 'Try again', |
| 2891 | 'store.full': 'there is no room left in this browser’s storage for this site', |
| 2892 | // A read that returned nothing where the last good write left {n}. The |
| 2893 | // number is the count, so keep the placeholder. |
| 2894 | 'store.empty_read': 'this browser returned none of the {n} conversations it is holding', |
| 2895 | |
| 2896 | // ── The delivery check ───────────────────────────────────── |
| 2897 | // A page checking itself. The check NAMES are what a reader scans down, so |
| 2898 | // keep them short; the details are the sentence under a failure. |
| 2899 | // |
| 2900 | // The page around them (verify.html) is here too. It used to be English in |
| 2901 | // every language, under a verdict that was translated -- so a reader was told |
| 2902 | // "manifest self-consistent" in their own language beneath a heading that was |
| 2903 | // not. The verdict lines are the sentences a person acts on, so they carry |
| 2904 | // their emphasis: {NOT} in `verdict_no` is deliberate and should stay loud in |
| 2905 | // whatever way the language has. |
| 2906 | 'verify.page_title': 'Verify Daimond', |
| 2907 | 'verify.page_lede': 'Check that the Daimond this page came from is the published open source. The evidence is a hash you can reproduce yourself.', |
| 2908 | 'verify.checking': 'Checking this build…', |
| 2909 | 'verify.recheck': 'Check again', |
| 2910 | 'verify.hashing': 'hashing served files… {done} / {total}', |
| 2911 | 'verify.ids': 'build {build} · bundle {bundle}', |
| 2912 | 'verify.verdict_ok': 'This build is the published source, and it was sealed.', |
| 2913 | 'verify.verdict_no': 'This build does NOT match the published source.', |
| 2914 | 'verify.verdict_partial': 'Could not fully verify. The checks say why.', |
| 2915 | 'verify.independent': 'The independent check.', |
| 2916 | 'verify.independent_body': 'A page cannot fully vouch for itself. Build the public source and run the verifier; nothing this server controls is in that loop:', |
| 2917 | // Stands in the command below where the page could not read its own origin. |
| 2918 | 'verify.this_site': 'this site', |
| 2919 | // {url} is the transparency log's address, held by the page and never by a |
| 2920 | // translation -- keep the placeholder and the anchor around the words. |
| 2921 | 'verify.green_means': 'Green means the bytes this site served are the source you just built, and that build is a sealed entry in the public <a href="{url}">transparency log</a>.', |
| 2922 | 'verify.check_manifest': 'manifest', |
| 2923 | 'verify.check_self': 'manifest self-consistent', |
| 2924 | 'verify.check_log': 'public transparency log', |
| 2925 | 'verify.check_sealed': 'sealed in the public log', |
| 2926 | 'verify.check_files': 'every served file matches the manifest', |
| 2927 | 'verify.no_manifest': 'this build was served without a manifest.json, so it cannot be checked', |
| 2928 | 'verify.self_mismatch': 'the manifest bundle hash does not match its file list', |
| 2929 | 'verify.log_broken': 'the public log is not an intact chain: {reason}', |
| 2930 | 'verify.log_unreachable': 'could not reach the public log (offline, or blocked by policy); run verify/check.mjs to be sure', |
| 2931 | 'verify.on_record': '{n} releases on record', |
| 2932 | 'verify.never_published': 'this served bundle is NOT in the public history; it was never published', |
| 2933 | 'verify.unreadable': '(unreadable)', |
| 2934 | 'verify.files_differ': '{n} differ: {list}', |
| 2935 | 'verify.files_ok': '{n} files', |
| 2936 | 'verify.chain_order': 'entry {n} out of order', |
| 2937 | 'verify.chain_break': 'entry {n} does not chain on {prev}', |
| 2938 | 'verify.chain_hash': 'entry {n} hash mismatch', |
| 2939 | 'verify.caveat': 'A page cannot vouch for itself: a tampered server could tamper with this check too. For an independent verdict, build the public source and run verify/check.mjs, or use the delivery-verify browser extension. This server can touch neither.', |
| 2940 | |
| 2941 | // ── Versions and the release history ─────────────────────── |
| 2942 | 'rel.version': 'Version', |
| 2943 | 'rel.prerelease': 'Pre-release', |
| 2944 | 'rel.unsealed': 'Unsealed', |
| 2945 | 'rel.update_ready': 'update ready', |
| 2946 | 'rel.not_published': 'not published', |
| 2947 | 'rel.planned': 'planned', |
| 2948 | 'rel.next': 'next', |
| 2949 | 'rel.you_are_here': 'you are here', |
| 2950 | 'rel.today': 'today', |
| 2951 | 'rel.yesterday': 'yesterday', |
| 2952 | 'rel.days_ago.one': '{n} day ago', |
| 2953 | 'rel.days_ago.other': '{n} days ago', |
| 2954 | 'rel.months_ago.one': '{n} month ago', |
| 2955 | 'rel.months_ago.other': '{n} months ago', |
| 2956 | 'rel.years_ago.one': '{n} year ago', |
| 2957 | 'rel.years_ago.other': '{n} years ago', |
| 2958 | 'rel.title_named': '{name}, build {build}, published {date}.', |
| 2959 | 'rel.title_prerelease': 'Pre-release, build {build}, published {date}.', |
| 2960 | 'rel.title_behind': 'A newer build has been published; reload to take it.', |
| 2961 | 'rel.title_click': 'Click for what changed.', |
| 2962 | 'rel.title_unlogged': 'This build is not in the published log. Click for the history.', |
| 2963 | 'rel.no_date': 'Not released yet, and not promised for a date.', |
| 2964 | 'rel.no_history': 'No published history could be read.', |
| 2965 | 'rel.none_declared': 'No release has been declared yet. You are running a build ahead of the first one.', |
| 2966 | 'rel.sealed_no': 'sealed #{n}', |
| 2967 | 'rel.sealed_builds.one': '{n} sealed build', |
| 2968 | 'rel.sealed_builds.other': '{n} sealed builds', |
| 2969 | 'rel.foot': 'A release is declared; a build is deployed. Every build here is recorded in a chain that cannot be rewritten without breaking. There is deliberately no way back to an older one.', |
| 2970 | |
| 2971 | // ── Updates ──────────────────────────────────────────────── |
| 2972 | 'update.checking': 'Checking for updates…', |
| 2973 | 'update.latest': 'You are on the latest version', |
| 2974 | 'update.ready': 'Update ready', |
| 2975 | 'update.click_now': 'Click to update now.', |
| 2976 | 'update.ready_help': 'Update ready. It applies when this finishes, or click to force it.', |
| 2977 | 'update.updated': 'Daimond updated', |
| 2978 | 'update.stale': 'Daimond is out of date and must reload to keep working. Click to reload.', |
| 2979 | |
| 2980 | // ── Typst ────────────────────────────────────────────────── |
| 2981 | // {reason} is the compiler's own diagnostic, which stays as it came. |
| 2982 | 'typst.load_failed': 'Typst compiler failed to load: {reason}', |
| 2983 | 'typst.no_pdf': 'Typst produced no PDF (unknown compile error).', |
| 2984 | 'typst.compile_error': 'Typst compile error: {reason}', |
| 2985 | 'typst.pack_locked': 'Typesetting is part of a tool pack this account has not bought. The Tools panel names it and its price: bought once, kept for good, and paid for in money rather than out of your credits.', |
| 2986 | |
| 2987 | // The watched live document. A few words in a bar over the pages, because the |
| 2988 | // thing worth looking at is the document underneath. {heap}, {more} and {budget} |
| 2989 | // are megabytes of wasm heap, and the two long ones are the only place the |
| 2990 | // reader is told why a loop stopped, so they say what to do about it. |
| 2991 | 'typst.watch.starting': 'Laying out the pages…', |
| 2992 | 'typst.watch.building': 'Rebuilding…', |
| 2993 | 'typst.watch.live_preview': 'Live preview', |
| 2994 | 'typst.watch.stale': 'Showing the last build that worked', |
| 2995 | 'typst.watch.held': 'Rebuilding stopped', |
| 2996 | 'typst.watch.dead': 'The compiler has stopped', |
| 2997 | 'typst.watch.rebuild': 'Rebuild', |
| 2998 | 'typst.watch.nothing': 'The compiler produced nothing and gave no reason.', |
| 2999 | 'typst.watch.heap': 'The compiler is holding {heap} MB and another rebuild could need {more} MB more, which is past the {budget} MB it is allowed on this page. Rebuilding on every save has stopped, and the pages below are the last ones that built. The compiler cannot give that memory back — reload the page to start it fresh, or press Rebuild to try once anyway.', |
| 3000 | 'typst.watch.spin': 'A watched file keeps reporting that it has changed, and it is too large to read back and check: the pages have been laid out {n} times in a row for it. Rebuilding on every save has stopped, so that it cannot fill the compiler’s memory. Press Rebuild when you want the pages again. The file was {what}.', |
| 3001 | 'typst.watch.page': 'Page', |
| 3002 | 'typst.watch.zoom_out': 'Smaller', |
| 3003 | 'typst.watch.zoom_in': 'Bigger', |
| 3004 | // The two toggles in the bar say one word each, and their hover titles carry |
| 3005 | // the longer form: bar width is the scarce thing, not explanation. |
| 3006 | 'typst.watch.fit_width': 'Fit the width', |
| 3007 | 'typst.watch.fit_page': 'Fit the whole page', |
| 3008 | 'typst.watch.paper_dark': 'Dark', |
| 3009 | 'typst.watch.paper_light': 'Light', |
| 3010 | 'typst.watch.paper_dark_why': 'Dark paper, for reading at night', |
| 3011 | 'typst.watch.paper_light_why': 'Light paper', |
| 3012 | 'typst.watch.sections': 'Sections', |
| 3013 | 'typst.watch.sections_none': 'This document has no headings to list.', |
| 3014 | 'typst.watch.dead_why': 'The compiler ran out of memory on this document and cannot be restarted without reloading the page. The pages below are the last ones that built. Reload, and open the same file again.', |
| 3015 | |
| 3016 | // ── Toasts ───────────────────────────────────────────────── |
| 3017 | 'toast.copied': 'Copied', |
| 3018 | 'render.copy_failed': 'Failed', |
| 3019 | |
| 3020 | // ── The Graph pane ───────────────────────────────────────── |
| 3021 | // One still picture of the Diamonds and the links between them. It is an |
| 3022 | // instrument for checking the structure, so the wording states what is |
| 3023 | // there rather than praising it. |
| 3024 | 'graph.no_diamonds': 'No Diamonds yet, so there is nothing to draw.', |
| 3025 | 'graph.isolated': 'Not linked', |
| 3026 | 'graph.unnamed': 'Untitled', |
| 3027 | 'graph.link_mode': 'Link', |
| 3028 | 'graph.link_help': 'Click the Diamond a link starts at, then the one it points at. Escape cancels.', |
| 3029 | 'graph.organise': 'Organise', |
| 3030 | 'graph.organise_help': 'Lay the Diamonds out again to reduce crossings, and keep the new positions.', |
| 3031 | 'graph.pick_source': 'Click the source, then the target. Escape cancels.', |
| 3032 | 'graph.pick_target': 'From {name}. Click the target, or press Escape.', |
| 3033 | 'graph.menu_link': 'Link from here…', |
| 3034 | 'graph.menu_open': 'Open this Diamond', |
| 3035 | 'graph.menu_reset_node': 'Put this Diamond back', |
| 3036 | 'graph.menu_reset_all': 'Put every Diamond back', |
| 3037 | 'graph.menu_reset_view': 'Reset the view', |
| 3038 | 'graph.menu_edit_link': 'Edit this link…', |
| 3039 | 'graph.menu_drop_link': 'Delete this link', |
| 3040 | // The zoom control that takes in the whole picture. One word: it rides in a |
| 3041 | // row of zoom buttons beside the numbers. |
| 3042 | 'graph.fit': 'All', |
| 3043 | 'graph.fit_help': 'Scale the picture until every Diamond is on screen.', |
| 3044 | 'graph.edit_title': 'Link', |
| 3045 | 'graph.new_title': 'New link', |
| 3046 | // A link carries several relations, added one at a time. `graph.rel_pool` heads |
| 3047 | // the words already used elsewhere in this graph, offered so the same idea is |
| 3048 | // not written three ways. |
| 3049 | 'graph.rels_label': 'Relations', |
| 3050 | 'graph.rel_add_ph': 'part-of, blocks… one at a time', |
| 3051 | 'graph.rel_add': 'Add this relation', |
| 3052 | 'graph.rel_pool': 'Already in use', |
| 3053 | 'graph.rel_none': 'No relation yet.', |
| 3054 | 'graph.rel_remove': 'Remove {rel}', |
| 3055 | 'graph.rel_full': 'A link carries at most {n} characters of relation.', |
| 3056 | 'graph.rel_label': 'Relation', |
| 3057 | 'graph.rel_ph': 'part-of, blocks, supersedes…', |
| 3058 | 'graph.note_label': 'Note', |
| 3059 | 'graph.note_ph': 'Whatever the relation does not say.', |
| 3060 | 'graph.save': 'Save', |
| 3061 | 'graph.create': 'Create', |
| 3062 | 'graph.cancel': 'Cancel', |
| 3063 | 'graph.drop': 'Delete', |
| 3064 | 'graph.self_link': 'A link joins two different Diamonds.', |
| 3065 | 'graph.write_failed': 'That could not be written: {err}', |
| 3066 | 'graph.edit_help': 'Edit this link', |
| 3067 | 'graph.in_cycle': 'This Diamond is on a cycle.', |
| 3068 | 'graph.back_edge': 'This link closes a cycle. Cycles are allowed; it is drawn dashed so it can be seen.', |
| 3069 | 'graph.edge_tip': '{from} → {to}', |
| 3070 | 'graph.edge_rel': 'Relation: {rel}', |
| 3071 | 'graph.failed': 'The graph could not be read: {err}', |
| 3072 | 'graph.artefacts.one': '{n} link to a file, page or chat', |
| 3073 | 'graph.artefacts.other': '{n} links to files, pages or chats', |
| 3074 | 'graph.stat_diamonds.one': '{n} Diamond', |
| 3075 | 'graph.stat_diamonds.other': '{n} Diamonds', |
| 3076 | 'graph.stat_links.one': '{n} link between Diamonds', |
| 3077 | 'graph.stat_links.other': '{n} links between Diamonds', |
| 3078 | 'graph.stat_cycles.one': '{n} link closes a cycle', |
| 3079 | 'graph.stat_cycles.other': '{n} links close cycles', |
| 3080 | 'graph.stat_dangling.one': '{n} link points at a Diamond that is gone', |
| 3081 | 'graph.stat_dangling.other': '{n} links point at Diamonds that are gone', |
| 3082 | |
| 3083 | // ── The terminal ─────────────────────────────────────────── |
| 3084 | 'term.label': 'Terminal', |
| 3085 | 'term.menu_copy': 'Copy', |
| 3086 | 'term.menu_paste': 'Paste', |
| 3087 | 'term.menu_select_all': 'Select all', |
| 3088 | 'term.clipboard_denied': 'The browser would not hand over the clipboard. Use Ctrl-V, which always works.', |
| 3089 | 'termroot.browse': 'Change…', |
| 3090 | 'termroot.browse_wait': 'Asking…', |
| 3091 | 'termroot.browse_use': 'Use', |
| 3092 | 'grantroot.head': 'Daimond\'s folder', |
| 3093 | 'grantroot.note': 'Bounds every command. Applies on reload.', |
| 3094 | 'termroot.reset': 'Granted folder', |
| 3095 | 'termroot.head': 'Terminal folder', |
| 3096 | 'termroot.note': 'A terminal is you, so it may reach further than a daimon.', |
| 3097 | 'termroot.pinned': 'Set on this computer at a shell.', |
| 3098 | 'term.hint': 'Type to send keys straight to the program. With text selected, Ctrl-C copies it; with none, Ctrl-C interrupts the program. Ctrl-V pastes, Ctrl-Shift-A selects everything, and right-click offers the same three. Shift with Page Up or Page Down moves through what has scrolled past.', |
| 3099 | 'term.screen_label': 'Terminal screen, as text', |
| 3100 | 'term.screen_now': 'The screen now reads:', |
| 3101 | 'term.nothing_selected': 'Nothing is selected.', |
| 3102 | 'term.copied.one': '{n} line copied.', |
| 3103 | 'term.copied.other': '{n} lines copied.', |
| 3104 | 'term.printed_lines.one': '{n} line printed.', |
| 3105 | 'term.printed_lines.other': '{n} lines printed.', |
| 3106 | 'term.paste_warn': 'This paste is {n} lines. The program has not asked to be told when text is pasted, so every line after the first would run the moment it arrived.', |
| 3107 | 'term.paste_first': 'Paste the first line', |
| 3108 | 'term.paste_all': 'Paste all {n} lines', |
| 3109 | 'term.exited': 'The program exited with status {code}.', |
| 3110 | |
| 3111 | // ── The Terminal panel ───────────────────────────────────── |
| 3112 | // The sentences a refusal is made of live one layer down, in js/handpty.js and |
| 3113 | // in the hand itself, and reach the panel verbatim. What is here is the panel's |
| 3114 | // own furniture, plus the two conditions no lower layer can report: a page |
| 3115 | // missing one of its own scripts, and a build whose wasm cannot say what a |
| 3116 | // terminal would be allowed to touch. |
| 3117 | 'term.notices_label': 'Notices about this terminal', |
| 3118 | 'term.dismiss_notice': 'Dismiss this notice', |
| 3119 | 'term.gaps_count.one': 'Output is missing in {n} place.', |
| 3120 | 'term.gaps_count.other': 'Output is missing in {n} places.', |
| 3121 | // The Start button says which of the two things it would do. |
| 3122 | // i18n-indirect: daimond.js k = term.start term.restart |
| 3123 | 'term.start': 'Start a terminal', |
| 3124 | 'term.restart': 'Restart the terminal', |
| 3125 | 'term.stop': 'Stop the program', |
| 3126 | 'term.leave_hint': 'Press F6 to move the keyboard out of the terminal.', |
| 3127 | 'term.starting': 'Starting a terminal…', |
| 3128 | 'term.running': 'The terminal is running.', |
| 3129 | 'term.nothing_running': 'There is no program running in this terminal.', |
| 3130 | 'term.not_paired': 'No machine hand is paired with this browser, so there is no machine to open a terminal on. Everything else in Daimond works without one.', |
| 3131 | 'term.no_composer': 'This build cannot say what a terminal would be allowed to touch, so it will not open one. The Rust side of the app works that out, and this page is older than it. Update Daimond; commands and the file tools are unaffected.', |
| 3132 | 'term.unreadable_request': 'Daimond could not read its own answer about what this terminal would be allowed to touch, so nothing was started. Reload the app; if it happens again, the app needs updating.', |
| 3133 | 'term.no_relay_script': 'The terminal relay did not load in this page, so no terminal can be opened. Nothing is wrong with your machine. Reload Daimond.', |
| 3134 | 'term.no_renderer': 'The terminal itself did not load in this page, so there is nothing to draw one on. Nothing is wrong with your machine. Reload Daimond.', |
| 3135 | |
| 3136 | // The durable trail, shown on the lock screen only when the app is |
| 3137 | // actually looping -- three boots in ninety seconds. See breadcrumb.js. |
| 3138 | 'trail.looping': 'Daimond has restarted {n} times in the last minute. This is what it did; copy it into a bug report.', |
| 3139 | 'trail.copy': 'Copy', |
| 3140 | 'trail.copied': 'Copied', |
| 3141 | 'trail.clear': 'Clear', |
| 3142 | |
| 3143 | // A safe start: the app opens without the sync engine, so a device that |
| 3144 | // cannot stay open long enough to be used gets to be used. See safe.js. |
| 3145 | 'safe.armed': 'Daimond has started without syncing, so it should stay open. Your work is safe on this device, and reaches your other devices again once syncing is turned back on.', |
| 3146 | 'safe.offer': 'If Daimond will not stay open, start it without syncing. Your work stays on this device and nothing is deleted.', |
| 3147 | 'safe.turn_off': 'Start without syncing', |
| 3148 | 'safe.turn_on': 'Turn syncing back on', |
| 3149 | 'safe.chip': 'Sync off (safe start)', |
| 3150 | 'safe.chip_reason': 'Daimond started without syncing, so this device’s work is not reaching your other devices. Nothing is lost; it is all still here.', |
| 3151 | 'safe.chip_click': 'Click to turn syncing back on.', |
| 3152 | 'safe.turn_on_title': 'Turn syncing back on?', |
| 3153 | 'safe.turn_on_ask': 'Daimond restarts and tries to sync with your other devices again. If it starts closing itself, it goes back to a safe start on its own.', |
| 3154 | 'safe.turn_on_ok': 'Restart with syncing', |
| 3155 | |
| 3156 | |
| 3157 | // The sync switch and the durable trail, both reachable from the admin |
| 3158 | // panel. See safe.js and breadcrumb.js. |
| 3159 | 'home.sec_sync': 'Syncing and diagnostics', |
| 3160 | 'settings.sync': 'Syncing', |
| 3161 | 'settings.sync_on_note': 'Sending your work to your other devices.', |
| 3162 | 'settings.sync_off_note': 'Not syncing — your work stays here.', |
| 3163 | 'settings.sync_help': 'Stopping is immediate and loses nothing; your work stays on this device and this device stops reaching your others.', |
| 3164 | 'settings.trail': 'Diagnostics', |
| 3165 | 'settings.trail_copy': 'Copy the app’s own trail', |
| 3166 | 'settings.trail_note': 'A safe-to-share log of what Daimond last did.', |
| 3167 | 'settings.trail_help': 'Event names and a clock only: no keys, no message text, nothing from your files. Safe to paste into a bug report.', |
| 3168 | 'settings.trail_empty': 'Nothing recorded yet.', |
| 3169 | |
| 3170 | // ── The email doorbell ───────────────────────────────────── |
| 3171 | // One email, at most once a day, saying something is waiting. On by default |
| 3172 | // for a beta account, which is why the switch and the notice both exist: a |
| 3173 | // default that sends mail has to be reachable and has to be announced. |
| 3174 | // The switch says what was CHOSEN; `no_address` and `unconfigured` say |
| 3175 | // whether a bell could ring at all, which is a different fact. |
| 3176 | 'doorbell.title': 'Email doorbell', |
| 3177 | 'doorbell.asking': 'Checking…', |
| 3178 | 'doorbell.saving': 'Saving…', |
| 3179 | 'doorbell.turn_off': 'Turn the email doorbell off', |
| 3180 | 'doorbell.turn_on': 'Turn the email doorbell on', |
| 3181 | 'doorbell.on_note': 'When a message arrives and you have no Daimond open, we may send one email to the address on your account saying something is waiting. No sender, no subject, no count, and at most one in any 24 hours. Turning it off also stops any that is already waiting to go.', |
| 3182 | 'doorbell.off_note': 'No email will be sent. You will see a message when you next open Daimond, and nowhere else.', |
| 3183 | 'doorbell.default_note': 'This is the default for a beta account; you have not changed it.', |
| 3184 | 'doorbell.no_address': 'There is no email address on your account, so nothing can be sent whatever this is set to.', |
| 3185 | 'doorbell.unconfigured': 'This gateway cannot send email, so nothing will be sent whatever this is set to.', |
| 3186 | 'doorbell.err_save': 'That did not save. The setting is unchanged; try again.', |
| 3187 | 'doorbell.notice_title': 'One email, at most once a day', |
| 3188 | 'doorbell.notice_body': 'When somebody sends you a private message and you have no Daimond open, we may send one email to the address on your account saying that something is waiting. It carries no sender, no subject, no count and no link to any message — and at most one in any 24 hours.\n\nYou can turn it off whenever you like: open Settings from the cog beside your name, and it is the row called “Email doorbell”. Turning it off also stops any that is already waiting to go.', |
| 3189 | |
| 3190 | // ── Reporting a message ──────────────────────────────────── |
| 3191 | // One message, with the sender's signature and the one key that opens it, |
| 3192 | // and nothing else from the conversation. The operator reads the words out |
| 3193 | // of the SIGNED artefact, so a reporter cannot file words that differ from |
| 3194 | // the words that were signed -- which is why the sheet's rule sentence is a |
| 3195 | // promise the design keeps rather than a reassurance. |
| 3196 | // |
| 3197 | // The five reasons are the gateway's closed list (gateway/src/schema.rs:1226) |
| 3198 | // and are FETCHED, not compiled in, so the picker and the endpoint cannot |
| 3199 | // drift. The keys here are the reader's words for them. |
| 3200 | // i18n-family: report.reason_ = harassment threat spam impersonation other |
| 3201 | 'post.report': 'Report', |
| 3202 | 'report.title': 'Report this message', |
| 3203 | 'report.rule': 'These exact words go to the operator, with the sender’s signature and the one key that opens this message. Nothing else from this conversation goes: not the rest of the thread, not their other messages, not your other conversations.', |
| 3204 | 'report.signed_by': 'Signed by {fp}', |
| 3205 | 'report.why': 'Why are you reporting it?', |
| 3206 | 'report.reason_harassment': 'Abuse aimed at me', |
| 3207 | 'report.reason_threat': 'A threat of harm', |
| 3208 | 'report.reason_spam': 'Unwanted bulk messages', |
| 3209 | 'report.reason_impersonation': 'Pretending to be somebody else', |
| 3210 | 'report.reason_other': 'Something else', |
| 3211 | 'report.send': 'Send this report', |
| 3212 | 'report.cancel': 'Cancel', |
| 3213 | 'report.sending': 'Sending…', |
| 3214 | 'report.sent': 'Reported. The operator can now read this one message.', |
| 3215 | 'report.already': 'You have already reported this message. Nothing new was sent.', |
| 3216 | 'report.done': 'Close', |
| 3217 | 'report.err_no_message': 'That message is not one this device holds.', |
| 3218 | 'report.err_no_artefact': 'This build did not keep the signed form of that message, so there is nothing to prove who sent it. A report without it would be an accusation, so nothing was sent.', |
| 3219 | 'report.err_no_envelope': 'This build did not keep the sealed form of that message, so the report could not be checked against what the relay carried. Nothing was sent.', |
| 3220 | 'report.err_no_bridge': 'This build cannot read the message it is about to send, so it will not send it.', |
| 3221 | 'report.err_not_a_post': 'That is not a message; it is a {kind}.', |
| 3222 | 'report.err_no_reasons': 'Reporting is not available just now.', |
| 3223 | 'report.err_failed': 'That report was not filed. Nothing was sent.', |
| 3224 | |
| 3225 | // ── Sharing a Diamond ────────────────────────────────────── |
| 3226 | // A Diamond, sealed to one person's key and carried by the message relay. |
| 3227 | // Every refusal here names what was NOT sent, because a share that half |
| 3228 | // happened is worse than one that did not: a copy missing a file is not a |
| 3229 | // smaller copy, and the sentences say so rather than trimming. |
| 3230 | 'share.err_no_bridge': 'This build cannot share a Diamond: its share format is not loaded.', |
| 3231 | 'share.err_no_seal': 'This build cannot share a Diamond: the seal it would be sent under is not loaded.', |
| 3232 | 'share.err_no_store': 'This build can read a share but has nowhere to put one.', |
| 3233 | 'share.err_locked': 'Unlock Daimond to share: a share is signed with your own key.', |
| 3234 | 'share.err_empty': 'There is nothing in that Diamond to send yet.', |
| 3235 | 'share.err_nothing': 'There is nothing to share: name a Diamond or the files to send.', |
| 3236 | 'share.err_no_name': 'A share needs a name for what is in it.', |
| 3237 | 'share.err_note_long': 'That note is longer than {n} characters and was not sent. A share carries a line about what it is; a letter is a message.', |
| 3238 | 'share.err_too_many_files': 'That Diamond holds {n} files, and a share carries at most {max}.', |
| 3239 | 'share.err_too_big': 'That Diamond is {mb} MB, and a share carries at most {max} MB. It is refused rather than trimmed: a copy missing a file is not a smaller copy.', |
| 3240 | 'share.err_bad_key': 'That person has no usable key, so nothing was sent.', |
| 3241 | 'share.err_no_card': 'There is no sealing key for that person yet, so nothing can be sealed to them. Scan their code, or ask them to send you theirs.', |
| 3242 | 'share.err_not_addressed': 'That share is addressed to a different key from this one.', |
| 3243 | 'share.err_addr_mismatch': 'The share you were told about is not the share that arrived.', |
| 3244 | 'share.err_all_code': 'Everything in that share is a page, and the page was not accepted, so nothing has been added.', |
| 3245 | // The one question a share asks. A page is a program somebody else wrote and |
| 3246 | // Daimond will run it, so it is named as code and attributed before it lands. |
| 3247 | 'share.code_title': 'This share contains code', |
| 3248 | 'share.code_body': '“{name}” includes a page: a program written by somebody else, which Daimond will run when you open it. It came from {who}. Accept it only if you meant to receive a page from them.\n\nWhat would be added: {files}', |
| 3249 | 'share.code_ok': 'Accept the page', |
| 3250 | 'share.landed_name': 'A shared Diamond', |
| 3251 | 'share.landed_title': 'Shared Diamond added', |
| 3252 | 'share.landed_partial': 'These files could not be written into the Diamond: {list}. Everything else in the share is there.', |
| 3253 | // A SHARE THAT LANDED SHORT SAYS SO. `accept` answered `ok: true` beside a |
| 3254 | // count of the files it had left out and nothing anywhere read the count, so a |
| 3255 | // share of five files of which two were pages landed three and reported plain |
| 3256 | // success. `share.err_all_code` covered only the total case — nothing landing |
| 3257 | // at all — which is the one case a person cannot fail to notice. |
| 3258 | 'share.left_page': 'it is a page you did not accept', |
| 3259 | 'share.landed_ok': 'Added as a Diamond of your own. {n} file(s) arrived.', |
| 3260 | // The Share view of the Social panel. Both halves of the feature: taking one |
| 3261 | // in needs nothing but the file, and sending one needs a Diamond and somebody |
| 3262 | // to send it to. Every refusal names what to do instead, because a person told |
| 3263 | // only that the relay declined has been told nothing they can act on. |
| 3264 | 'share.panel_take_head': 'Open a share', |
| 3265 | 'share.panel_take_help': 'Take a {ext} somebody gave you. A page inside it is a program they wrote, and it is never written into your workspace without asking you first.', |
| 3266 | 'share.panel_take': 'Open a share file…', |
| 3267 | 'share.panel_send_head': 'Send a Diamond', |
| 3268 | 'share.panel_no_diamond': 'Open a Diamond to share it. A share carries the files of one Diamond, so there has to be one in front of you.', |
| 3269 | 'share.panel_no_people': 'Nobody here has a sealing key yet, so there is nobody a share can be sealed to. Show somebody your code, or read theirs.', |
| 3270 | 'share.panel_this': 'Sharing “{name}” — a copy they will own, not a view of yours.', |
| 3271 | 'share.panel_who': 'Who it goes to', |
| 3272 | 'share.panel_send': 'Share', |
| 3273 | 'share.panel_sealing': 'Sealing…', |
| 3274 | 'share.panel_sent': 'Sent to {who}.', |
| 3275 | 'share.panel_refused': 'The relay would not take it: {why} It is saved as a file instead — give them that.', |
| 3276 | // Which carrier, and the file route. The relay refuses a sealed envelope over |
| 3277 | // 64 KiB and a capp page is about that on its own, so the file is not a |
| 3278 | // fallback for an awkward case: it is the only way a capp can travel. The |
| 3279 | // sentences carry the SIZE, because the sender is the only person who can do |
| 3280 | // anything about it and the number says whether taking one file out is enough. |
| 3281 | 'share.by_relay': 'This share is {size} and goes straight to them through the relay.', |
| 3282 | 'share.by_file': 'This share is {size} and the relay carries at most {max}, so it travels as a file: save it and give them the file. It is sealed to them either way.', |
| 3283 | 'share.saved_as': 'Saved as {name}. Give them that file: it is sealed to them and to nobody else.', |
| 3284 | 'share.err_not_share': 'That file is not a Daimond share.', |
| 3285 | 'share.err_no_file': 'No file was chosen, so nothing was opened.', |
| 3286 | 'share.err_file_huge': 'That file is {size}, which is larger than any share can be, so it was not opened.', |
| 3287 | // ── The Trash ────────────────────────────────────────────── |
| 3288 | // Deleting a chat or a Diamond moves it here and asks nothing. The two |
| 3289 | // questions that cannot be taken back are both in this panel. |
| 3290 | 'panel.trash': 'Trash', |
| 3291 | |
| 3292 | // ── The Social panel ─────────────────────────────────────── |
| 3293 | // Where a note about Daimond is written, where the proposals made from notes |
| 3294 | // are read and voted on, and where messages and the people they come from |
| 3295 | // will live. Both halves of the note/proposal pair go through the Oregami |
| 3296 | // forge. See js/improve.js and dev/IMPROVE_CONTRACT.md. |
| 3297 | // |
| 3298 | // TRANSLATORS — `panel.social` names a whole panel and rides in a chip row |
| 3299 | // and a phone tab bar, so keep it to one short word. |
| 3300 | 'panel.social': 'Social', |
| 3301 | 'social.messages': 'Messages', |
| 3302 | 'social.people': 'People', |
| 3303 | 'social.notes': 'Notes', |
| 3304 | 'social.proposals': 'Proposals', |
| 3305 | 'social.settings': 'Settings', |
| 3306 | // The compose box, merged into Proposals: two ways to post, both auto-send. |
| 3307 | 'social.post': 'Post', |
| 3308 | 'social.post_help': 'Post exactly what is above to the forge as a proposal. Public, under your voice name — no account needed to read it.', |
| 3309 | 'social.polish': 'Polish & post', |
| 3310 | 'social.polish_help': 'Let the model rewrite it as a proposal, then post that.', |
| 3311 | 'social.compose_public': 'Posted publicly, under your voice name — anyone can read it.', |
| 3312 | // Shown after a note is raised (posted, or queued to send). The hub label is a |
| 3313 | // press that opens the Improve panel, where it is read and settled. |
| 3314 | 'social.raised_lead': 'Raised — see it in', |
| 3315 | 'social.raised_hub': 'Improve', |
| 3316 | 'social.novoice_set': 'No voice yet — set one in Settings to post.', |
| 3317 | 'social.polish_wait': 'The model could not draft it just now; it is still waiting to send.', |
| 3318 | 'social.waiting_here': 'Waiting to send; it will go when the forge is reachable.', |
| 3319 | // The queue: notes that could not be sent yet. |
| 3320 | 'social.queue': 'Waiting to send ({n})', |
| 3321 | 'social.q_verbatim': 'Waiting to post', |
| 3322 | 'social.q_polish': 'Waiting to polish and post', |
| 3323 | 'social.send_now': 'Send now', |
| 3324 | 'social.send_now_help': 'Try to send this one now, in the way it was written.', |
| 3325 | // The Settings view: the voice, and the drafts the model prepared. |
| 3326 | 'social.set_voice': 'Your voice', |
| 3327 | 'social.set_drafts': 'Prepared drafts', |
| 3328 | 'social.set_drafts_note': 'Forgets any proposals the model drafted from your notes that you have not sent. Your notes waiting to send are not touched.', |
| 3329 | // What each chip's list says while it is empty, and the two are NOT the same |
| 3330 | // sentence. Messages is not switched on in this build, so it says that -- an |
| 3331 | // empty list there would be claiming "nothing has arrived", which is a |
| 3332 | // different claim and is not true. People IS switched on, so its line is an |
| 3333 | // ordinary empty state and tells you how to end it. |
| 3334 | 'social.messages_off': 'Messages are not switched on in this build.', |
| 3335 | 'social.people_off': 'Nobody yet. Show your code to somebody in the room, or read theirs.', |
| 3336 | // The fifth chip. Its module was complete and unreachable before it existed. |
| 3337 | 'social.share': 'Share', |
| 3338 | 'social.share_off': 'Sharing is not available in this build.', |
| 3339 | // ── A private message ────────────────────────────────────── |
| 3340 | // |
| 3341 | // The Social panel's Messages chip. Written from the English at the call |
| 3342 | // sites in js/post.js, VERBATIM: a second wording here would be a second |
| 3343 | // wording on the screen, and nobody would find out which one a reader saw. |
| 3344 | // |
| 3345 | // TRANSLATORS — "private" here is a claim about who can read the words, and |
| 3346 | // it is exact: the message is sealed on this device to the recipient's key, |
| 3347 | // and the relay that carries it holds ciphertext it cannot open. Do not |
| 3348 | // soften it to "confidential" or strengthen it to anything about the |
| 3349 | // recipient's identity, which is a different claim and lives under trust.*. |
| 3350 | // "relay" is the post box in the middle; it is not a server that holds an |
| 3351 | // account, and a word suggesting a mailbox provider would be wrong. |
| 3352 | |
| 3353 | 'post.err_no_bridge': 'This build cannot compose a message: its message format is not loaded.', |
| 3354 | 'post.err_no_draft': 'This build cannot compose a message: its message encoder is not loaded.', |
| 3355 | 'post.err_no_recipient': 'A sealed message needs at least one recipient key.', |
| 3356 | 'post.err_too_many': 'A message can be sealed to at most {n} people at once.', |
| 3357 | 'post.err_bad_key': 'One of the recipients has no usable key, so nothing was sent.', |
| 3358 | 'post.err_short': 'That message is too short to be one.', |
| 3359 | 'post.err_not_sealed': 'That is not a sealed Daimond message.', |
| 3360 | 'post.err_no_sealing_key': 'This device has no sealing key, so it cannot open a sealed message. Unlock Daimond once and one will be made.', |
| 3361 | 'post.err_not_for_you': 'This message was not sealed to any key this device holds.', |
| 3362 | 'post.err_locked': 'Unlock Daimond to send a message: it is signed with your own key.', |
| 3363 | 'post.err_empty': 'There is nothing to send.', |
| 3364 | 'post.err_long': 'That message is longer than {n} characters of text and was not sent. It is refused rather than cut: half a message is not a shorter message.', |
| 3365 | 'post.err_no_card': 'There is no sealing key for that person yet, so nothing can be sealed to them. Scan their code, or ask them to send you theirs.', |
| 3366 | // Sending to a group rather than to one person. `post.err_no_groups` is the |
| 3367 | // build saying it has no group module at all, which is not the same as a |
| 3368 | // group being empty; `post.err_group_none` is a real group the message |
| 3369 | // reached nobody in. |
| 3370 | 'post.err_no_groups': 'This build cannot send to a group.', |
| 3371 | 'post.err_group_none': 'The message reached nobody in that group, so nothing was sent.', |
| 3372 | // The one line a person may not write, because group.js reads it as a |
| 3373 | // membership list. Refused rather than escaped, and the refusal has to say |
| 3374 | // what to do about it. |
| 3375 | 'post.err_reserved_line': 'A message cannot begin with that line: Daimond uses it to carry a group\'s membership list. Put something before it.', |
| 3376 | 'post.err_bad_ref': 'That is not a kind of reference a message can carry.', |
| 3377 | 'post.err_not_a_post': 'That is not a message; it is a {kind}.', |
| 3378 | 'post.err_not_addressed': 'That message is addressed to a different key from this one.', |
| 3379 | 'post.err_addr_mismatch': 'The message the relay named is not the message it carried.', |
| 3380 | 'post.err_offline': 'Daimond could not reach the relay, so the message has not been sent.', |
| 3381 | 'post.err_box_full': 'That mailbox is full, so the message did not arrive. They have to collect what is already in it before another will fit.', |
| 3382 | 'post.err_no_account': 'No account holds that key, so the message has not been sent.', |
| 3383 | 'post.err_too_big': 'That message is too large for the relay to carry.', |
| 3384 | 'post.err_refused': 'The relay would not take that message, so it has not been sent.', |
| 3385 | 'post.err_bad_put': 'A post needs a recipient, an address and a sealed body.', |
| 3386 | 'post.locked': 'Unlock Daimond to read your messages: they are kept encrypted on this device.', |
| 3387 | 'post.tray_head': 'Waiting for your answer', |
| 3388 | 'post.none': 'No messages yet.', |
| 3389 | 'post.you': 'You', |
| 3390 | 'post.someone': 'Someone new', |
| 3391 | 'post.unreadable': 'A message arrived that this device could not open.', |
| 3392 | 'post.accept': 'Accept', |
| 3393 | 'post.ignore': 'Ignore', |
| 3394 | 'post.block': 'Block', |
| 3395 | 'post.expired': 'A message you sent was never collected and the relay has let it go.', |
| 3396 | // The same, for several copies of one group message. {n} is the SENDER'S |
| 3397 | // own count of copies the relay let go, and it is not a read receipt and |
| 3398 | // cannot become one: it is a fact about the relay letting go, never about |
| 3399 | // anybody opening anything. Do not translate it into "{n} people did not |
| 3400 | // read it". |
| 3401 | 'post.expired_group': 'A message you sent to a group was never collected by {n} of the people it went to, and the relay has let those copies go.', |
| 3402 | 'post.notice': 'The relay left a notice here.', |
| 3403 | 'post.nobody': 'There is nobody to write to yet. Exchange codes with somebody in People, and they will be here.', |
| 3404 | 'post.to_label': 'Who this goes to', |
| 3405 | 'post.audience': 'Private. Only you and the person you are writing to can read this.', |
| 3406 | // WHO CAN READ THIS, when the answer is a group, and it is a different |
| 3407 | // sentence with a different set of people behind it. Both halves of the |
| 3408 | // second clause are load-bearing and neither may be dropped for brevity: a |
| 3409 | // later joiner CANNOT read this, and somebody taken out afterwards KEEPS |
| 3410 | // it. That is what having no shared key means, and it is the opposite of |
| 3411 | // what a reader assumes a group chat does. |
| 3412 | 'post.audience_group': 'Sealed once for each of the {n} people in this group. There is no shared key: anybody who joins later cannot read this, and anybody taken out afterwards keeps it.', |
| 3413 | // How many people a group in the picker holds. |
| 3414 | 'post.group_count': '{n} people', |
| 3415 | 'post.box_label': 'Write a private message', |
| 3416 | 'post.box_ph': 'What you want to say, and to whom.', |
| 3417 | 'post.send': 'Send privately', |
| 3418 | 'post.err_no_to': 'Choose who this is going to first.', |
| 3419 | 'post.sending': 'Sending…', |
| 3420 | 'post.sent': 'Sent.', |
| 3421 | // SENT TO, never DELIVERED TO. The relay answers a blocked delivery exactly |
| 3422 | // as it answers an accepted one, so {n} is the number this device wrote to |
| 3423 | // and nothing more. A translation saying "delivered to {n} people" would |
| 3424 | // make a claim the transport was built not to be able to make. |
| 3425 | 'post.sent_group': 'Sent to {n} people.', |
| 3426 | // Who a group message was NOT sealed to, appended to the line above. Never |
| 3427 | // a log line: the sender believes the message went to the whole group, and |
| 3428 | // their own screen at the moment they press is the only place that can be |
| 3429 | // corrected. TWO SENTENCES, because the two lists are fixable by different |
| 3430 | // people -- `group.refused` is what this device would not seal, and this one |
| 3431 | // is what the relay would not take. `post.group_skipped` used to be the first |
| 3432 | // of them and was retired on 2026-08-17: `group.refused` says it, and the only |
| 3433 | // caller left naming the old key was a second copy of the sentence in |
| 3434 | // `js/share.js`. |
| 3435 | 'post.refused_offline': 'the relay could not be reached', |
| 3436 | 'post.refused_full': 'their mailbox is full', |
| 3437 | 'post.refused_no_account': 'no account holds their key', |
| 3438 | 'post.refused_too_big': 'too large for the relay to carry', |
| 3439 | 'post.refused_other': 'the relay refused it', |
| 3440 | 'post.group_refused': 'The relay would not take it for: {who}.', |
| 3441 | // ── A group, and who is in it ────────────────────────────── |
| 3442 | // |
| 3443 | // A group is a LIST OF PEOPLE a message is sealed to one by one. There is no |
| 3444 | // shared key, no key agreement and nothing kept on the relay, and that one |
| 3445 | // fact decides every sentence in this section: what a new member cannot be |
| 3446 | // shown, what somebody taken out keeps, and why a count is always people |
| 3447 | // WRITTEN TO rather than people who received anything. |
| 3448 | // |
| 3449 | // Written from the English at the call sites in js/group.js, VERBATIM, for |
| 3450 | // the reason the private message block above gives: a second wording here |
| 3451 | // would be a second wording on the screen and nobody would find out which |
| 3452 | // one a reader saw. |
| 3453 | // |
| 3454 | // TRANSLATORS — "group" here is not a group chat. Nothing is shared between |
| 3455 | // members except the roster, so a word carrying "room", "channel" or |
| 3456 | // "shared conversation" would promise the architecture. Prefer your |
| 3457 | // language's plain word for a list of people. "roster" is that list; |
| 3458 | // "relay" is the post box in the middle, the same word post.* uses. |
| 3459 | |
| 3460 | 'group.err_unknown': 'This device does not know that group.', |
| 3461 | 'group.err_left': 'You are no longer in this group, so nothing can be sent to it. The messages already here stay where they are.', |
| 3462 | // NOT `err_left`, AND THE DIFFERENCE IS THE POINT. Closing leaves everybody |
| 3463 | // including the creator out of the group, so the record says "left" -- but |
| 3464 | // telling somebody they are no longer in a group they closed themselves reads |
| 3465 | // as something done to them. This one is about the group; that one is about |
| 3466 | // them. |
| 3467 | 'group.err_closed': 'This group has been closed, so nothing more can be sent to it. Every message already here stays where it is.', |
| 3468 | 'group.err_close_not_creator': 'Only the person who made a group can close it.', |
| 3469 | 'group.err_not_joined': 'Join this group before writing to it.', |
| 3470 | 'group.err_nobody': 'There is nobody in this group this device can seal to.', |
| 3471 | 'group.err_locked': 'Unlock Daimond to make a group: its roster is signed with your own key.', |
| 3472 | 'group.err_no_id': 'This device could not name a group.', |
| 3473 | 'group.err_not_creator': 'Only the person who made a group can change who is in it.', |
| 3474 | 'group.err_no_sealing_key': 'This device has no sealing key yet, so it cannot make a group.', |
| 3475 | 'group.err_too_many': 'A group can hold at most {n} people.', |
| 3476 | // TWO SENTENCES AND NOT ONE, because a mis-typed key and somebody whose code |
| 3477 | // has not been scanned take different repairs and a roster can hit both at |
| 3478 | // once. `err_no_card`'s {n} counts the SECOND kind only: it counted both, and |
| 3479 | // one typing mistake plus one uncarded person read as "no sealing key for 2 of |
| 3480 | // the people chosen", which sent the reader to scan a code for a typo. |
| 3481 | // |
| 3482 | // TRANSLATORS — `err_bad_keys` is PLURAL and its {who} is a comma-joined list |
| 3483 | // of the spellings the caller gave, one or several. It replaced a singular key |
| 3484 | // with one {k}, which could not describe the two-entry case it was written |
| 3485 | // for. Keep it in the register of post.group_refused, which carries a list the |
| 3486 | // same way. It names SPELLINGS, because a spelling is what can be corrected. |
| 3487 | 'group.err_no_card': 'There is no sealing key for {n} of the people chosen, so they cannot be added. Scan their code first.', |
| 3488 | 'group.err_bad_keys': 'These are not keys: {who}.', |
| 3489 | 'group.err_pick': 'Choose at least one person.', |
| 3490 | |
| 3491 | // Why a member was left out of a send, each one a phrase that finishes |
| 3492 | // `group.refused`, so they are lower case and carry no full stop. |
| 3493 | // `skip_changed` says MATCHED, the same word trust.key_* uses, because it |
| 3494 | // is the same act being named -- see the note on that family below. |
| 3495 | 'group.skip_blocked': 'you blocked this key', |
| 3496 | 'group.refused': 'Not sealed to: {who}.', |
| 3497 | 'group.skip_changed': 'their key changed and you have not matched the new one', |
| 3498 | 'group.skip_disagree': 'the group\'s key for them is not the one you hold', |
| 3499 | |
| 3500 | // THE THREE SENTENCES, and they are the reason this section reads the way it |
| 3501 | // does. Each says what a group CANNOT do, on the screen before the press |
| 3502 | // rather than after it, where it would be an excuse instead of a fact. |
| 3503 | // |
| 3504 | // TRANSLATORS — translate the LIMIT, not the reassurance. `joining` says |
| 3505 | // the earlier messages cannot be shown to a new member BY ANY DEVICE, |
| 3506 | // because they were never sealed to that key: it is not a policy and not |
| 3507 | // a setting, and "you will not see older messages" loses exactly that. |
| 3508 | // `removing` says nothing is taken back, and both halves are needed -- |
| 3509 | // what they keep, and what stops. `close_note` says the same about a whole |
| 3510 | // group and adds the one thing no other sentence here has to: it cannot be |
| 3511 | // undone. If a qualification will not sit naturally in your language, keep |
| 3512 | // the sentence long rather than lose it. |
| 3513 | 'group.joining_shows_nothing': 'Joining shows you nothing that was sent before you join. Those messages were never sealed to your key, so no device can open them for you.', |
| 3514 | 'group.removing_retracts_nothing': 'Taking somebody out takes nothing back. They keep every message already sent to them; they will not receive anything sent from now on.', |
| 3515 | 'group.close_note': 'Closing a group closes it for everybody. Nobody can write to it again, you included; every message already sent stays where it is. It cannot be undone.', |
| 3516 | |
| 3517 | // A group's name is the CREATOR'S claim, so it is drawn beside the first |
| 3518 | // eight characters of the id, which nobody chose. |
| 3519 | 'group.unnamed': 'A group', |
| 3520 | 'group.invites_head': 'Group invitations', |
| 3521 | 'group.head': 'Groups', |
| 3522 | |
| 3523 | // Two empty states rather than one, because "no groups yet" printed over a |
| 3524 | // pending invitation is a screen arguing with the row above it. The second |
| 3525 | // says what a group IS, and "no shared key" is the whole of it. |
| 3526 | 'group.none_joined': 'None joined yet. Answer the invitation above, or make one below.', |
| 3527 | 'group.none': 'No groups yet. A group is a list of people a message is sealed to one by one — there is no shared key, and nothing is kept on the relay.', |
| 3528 | |
| 3529 | 'group.invited_by': '{n} people, invited by the person who made it.', |
| 3530 | 'group.join': 'Join', |
| 3531 | 'group.decline': 'Not now', |
| 3532 | 'group.count': '{n} people', |
| 3533 | |
| 3534 | // STOP SENDING TO, never "remove". "Remove" reads as though something is |
| 3535 | // taken back, and nothing is; the sentence above this control says so. |
| 3536 | 'group.stop_sending': 'Stop sending to {who}', |
| 3537 | 'group.leave': 'Leave this group', |
| 3538 | |
| 3539 | // A group this device has left keeps its place on the list, with its |
| 3540 | // messages. That IS the second sentence: leaving and being taken out both |
| 3541 | // retract exactly nothing. |
| 3542 | 'group.gone': 'You are no longer in this group. Nothing has been taken away: every message already here stays, and nothing new will arrive.', |
| 3543 | |
| 3544 | // CLOSING ONE. The creator's act and the only one in this feature that cannot |
| 3545 | // be undone, so it is the only one behind a confirmation dialogue. |
| 3546 | // |
| 3547 | // TRANSLATORS — the control says CLOSE and never "delete", "disband" or |
| 3548 | // "remove". Nothing is destroyed by it: every member keeps every message they |
| 3549 | // hold, and the group stays on their list saying it is closed. A word carrying |
| 3550 | // "delete" would promise something this design does not do, in the same way |
| 3551 | // `stop_sending` refuses the word "remove". `close_ask` names the group, so |
| 3552 | // {name} is the creator's own label for it and the quotation marks belong to |
| 3553 | // your language. |
| 3554 | 'group.close': 'Close this group', |
| 3555 | 'group.close_title': 'Close this group for everybody?', |
| 3556 | 'group.close_ask': 'This closes “{name}” for everybody in it.', |
| 3557 | 'group.close_ok': 'Close it for everybody', |
| 3558 | 'group.closed_said': 'Closed, and {n} people have been told.', |
| 3559 | 'group.closed': 'This group has been closed by the person who made it. Nothing has been taken away: every message already here stays, and nobody can write to it again.', |
| 3560 | |
| 3561 | // Making one. A member with no sealing key is a member nothing can be |
| 3562 | // sealed to, so People is where this sends somebody, in the same words |
| 3563 | // `post.nobody` uses. |
| 3564 | 'group.nobody': 'There is nobody to put in a group yet. Exchange codes with somebody in People, and they will be here.', |
| 3565 | 'group.name_ph': 'What to call this group', |
| 3566 | 'group.name_label': 'The group\'s name', |
| 3567 | 'group.members_label': 'Who is in this group', |
| 3568 | 'group.make': 'Make this group', |
| 3569 | |
| 3570 | // The status line while a roster goes out. `group.made` counts people TOLD, |
| 3571 | // which is what this device wrote to, and never people who received it. |
| 3572 | 'group.sending_roster': 'Telling everybody…', |
| 3573 | 'group.roster_sent': 'Done.', |
| 3574 | 'group.making': 'Making the group…', |
| 3575 | 'group.made': 'Made, and {n} people have been told.', |
| 3576 | |
| 3577 | // ── People: a key, and whether it is the one you matched ──── |
| 3578 | // |
| 3579 | // TRANSLATORS — THE ONE RULE HERE IS NOT A STYLE PREFERENCE, and it is worth |
| 3580 | // the paragraph. Daimond makes TWO different claims about somebody and must |
| 3581 | // never let them read as one: |
| 3582 | // |
| 3583 | // what a key IS matched, or new. Matched means the reader compared |
| 3584 | // this key themselves, in person or by reading the |
| 3585 | // safety number aloud. New means they have not. |
| 3586 | // who somebody IS the name they gave. Daimond knows nothing about it |
| 3587 | // and says nothing about it. |
| 3588 | // |
| 3589 | // So a `trust.key_*` string may NEVER carry this language's word for |
| 3590 | // "verified" or "trusted". Those words say the second thing while claiming to |
| 3591 | // say the first, and a reader who believes a key was "verified" believes |
| 3592 | // somebody checked WHO the person is. Nobody did. Use the ordinary word your |
| 3593 | // language has for two things being compared and found the same. |
| 3594 | // |
| 3595 | // This is enforced twice -- `dev/verify_trust.mjs` reads all eight files and |
| 3596 | // fails on the forbidden words, and the drawing code refuses a table that |
| 3597 | // hands it one and draws the built-in English instead -- but a translator who |
| 3598 | // knows why will write a better sentence than a guard that only says no. |
| 3599 | // |
| 3600 | // The key state is drawn as a line UNDER the name, never as a badge beside |
| 3601 | // it, so keep these as sentences rather than as labels. |
| 3602 | |
| 3603 | 'trust.key_blocked': 'Blocked key', |
| 3604 | 'trust.key_matched_qr': 'Key matched in person, {when}', |
| 3605 | 'trust.key_matched_number': 'Key matched by safety number, {when}', |
| 3606 | 'trust.key_changed': 'Different key — the one you matched was last seen {when}', |
| 3607 | 'trust.key_new': 'New key — you have not matched this one', |
| 3608 | 'trust.no_name': '(no name given)', |
| 3609 | 'trust.calls_themselves': 'calls themselves “{name}”', |
| 3610 | 'trust.lookalike': 'This name looks like one you have already matched ({fp}). Names are not identities — check the key.', |
| 3611 | 'trust.held': 'Messages from this key are held until you decide.', |
| 3612 | 'trust.compare_numbers': 'Compare safety numbers', |
| 3613 | 'trust.unblock': 'Unblock', |
| 3614 | 'trust.block': 'Block', |
| 3615 | 'trust.show_mine': 'Show my code', |
| 3616 | 'trust.show_lead': 'Let them point their camera at this. Reading it in person is the only way either of you can mark the other matched without a phone call.', |
| 3617 | 'trust.no_card': 'This device has no card yet. Unlock it and try again.', |
| 3618 | 'trust.paste_lead': 'No camera? Send them this instead. A code that arrives this way is a new key and stays one until you compare numbers.', |
| 3619 | 'trust.add': 'Add somebody', |
| 3620 | 'trust.add_lead': 'Point this device at their code, or paste what they sent you.', |
| 3621 | 'trust.read_paste': 'Read this', |
| 3622 | 'trust.bad_card': 'That is not a Daimond code, or it did not verify.', |
| 3623 | 'trust.arrived': 'A code arrived', |
| 3624 | 'trust.qr_lead': 'You read this off their screen, so there was no channel for anybody to get between you. Mark it matched only if that is what happened.', |
| 3625 | 'trust.mark_matched': 'Mark matched now', |
| 3626 | 'trust.async_lead': 'This came through something in the middle, so it stays a new key. Read the safety number aloud on a call to change that.', |
| 3627 | 'trust.numbers': 'Safety numbers', |
| 3628 | 'trust.numbers_lead': 'Read these sixty digits to each other on a call, or in person. Both of you must see the same twelve groups. Reading them in a message proves nothing — whatever could swap the keys could swap the message.', |
| 3629 | 'trust.numbers_differ': 'They are different', |
| 3630 | 'trust.numbers_match': 'The numbers match', |
| 3631 | 'trust.no_number': 'This device cannot compute the number yet.', |
| 3632 | 'trust.numbers_differ_note': 'Then somebody is between you. Do not use this key. Meet, or start again from a code you read in person.', |
| 3633 | 'social.info': 'What this panel is, in the guide', |
| 3634 | 'social.box_label': 'Write a note about Daimond', |
| 3635 | 'social.box_ph': 'Where it is, what you expected, and what happened instead.', |
| 3636 | 'social.with': 'What goes with it', |
| 3637 | 'social.with_off': 'Take the details off this note', |
| 3638 | 'social.keep': 'Keep', |
| 3639 | 'social.keep_help': 'Store this note on this device. Nothing is sent.', |
| 3640 | 'social.send': 'Send', |
| 3641 | 'social.send_help': 'Sends exactly what is above. Nothing else goes with it.', |
| 3642 | // A note goes under the writer's VOICE. No handle and no name travels with |
| 3643 | // it, so nothing here may name the writer. |
| 3644 | 'social.as_novoice': 'No voice yet — a note can only be kept here.', |
| 3645 | // Above Send, and on the screen exactly when Send is. The repository this |
| 3646 | // panel reads is public — the forge draws no other kind — so a note that is |
| 3647 | // sent is readable by ANYBODY, with NO account, under the name of the voice |
| 3648 | // it was written with. All three of those facts must survive translation. |
| 3649 | // `{host}` is the forge's address, filled in by js/improve.js: keep it as it |
| 3650 | // is rather than typing the address out, and do not name the repository, |
| 3651 | // which is a constant in the code and would strand eight files if it moved. |
| 3652 | 'social.publish_body': 'Daimond will publish this in your name, for anyone to read. It cannot be taken back.\n\n{what}\n\nDecline if you did not expect it.', |
| 3653 | 'social.publish_ok': 'Publish it', |
| 3654 | 'social.publish_title': 'Publish this?', |
| 3655 | 'social.public_note': 'Sending publishes this at {host} with your voice name on it — anyone can read it, no account needed. A kept note stays on this device.', |
| 3656 | 'social.title_hint': 'First line is the title; what happened goes below.', |
| 3657 | 'social.no_title': 'First line is the title — write one, then what happened.', |
| 3658 | 'social.nothing': 'Write something first.', |
| 3659 | // Said after a refusal. Nothing is queued and nothing is tried again, so a |
| 3660 | // translation must not promise a retry. |
| 3661 | 'social.kept_here': 'Kept here; nothing tried again.', |
| 3662 | 'social.copied': 'Copied.', |
| 3663 | 'social.state_kept': 'Kept here', |
| 3664 | 'social.state_sent': 'Sent {date}', |
| 3665 | 'social.state_sent_n': 'Sent {date}, and is proposal {n}', |
| 3666 | 'social.drop': 'Delete this note', |
| 3667 | 'social.drop_ask': 'Delete this note? It is only here — no other copy.', |
| 3668 | 'social.drop_ok': 'Delete', |
| 3669 | 'social.no_notes': 'No notes yet.', |
| 3670 | // The one line that goes with a note, in the characters it travels as. |
| 3671 | 'social.ctx_build': 'Build {id}', |
| 3672 | 'social.ctx_touch': 'touch', |
| 3673 | 'social.ctx_pointer': 'pointer', |
| 3674 | 'social.ctx_palette': 'palette {name}', |
| 3675 | 'social.ctx_panels': 'panels open: {list}', |
| 3676 | |
| 3677 | // The voice: a per-person secret the forge looks the writer up by. It is |
| 3678 | // held here encrypted under the passphrase, and it never goes in an address. |
| 3679 | 'social.voice_held': 'Voice held on this device, encrypted under your passphrase.', |
| 3680 | 'social.voice_none': 'No voice yet: you can read proposals, but only Keep a note to this device.', |
| 3681 | // \u2500\u2500 ONE TAP, NOT A PASTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 |
| 3682 | // `Get my voice` posts to `/api/voice/provision` and the forge mints one; the |
| 3683 | // honest instruction is "tap the button". The old paste-first strings |
| 3684 | // (`voice_how`, `voice_set`, `voice_help_none`) were dropped in the terseness |
| 3685 | // pass -- nothing referenced them, and they carried the stale invitation flow. |
| 3686 | // The paste FORM survives as a fallback for a voice made elsewhere, so |
| 3687 | // `voice_ph`, `voice_help`, `voice_replace` and `voice_save` stay. Detail lives |
| 3688 | // in guide/social.html, not here. |
| 3689 | 'social.voice_intro': 'A voice lets you post, reply and vote on the forge; reading needs none.', |
| 3690 | 'social.voice_get': 'Get my voice', |
| 3691 | 'social.voice_get_help': 'Makes your voice on the forge. One tap.', |
| 3692 | 'social.voice_getting': 'Making your voice on the forge\u2026', |
| 3693 | 'social.voice_get_failed': 'Could not make your voice. Try again shortly.', |
| 3694 | 'social.voice_pro': 'A voice is part of Daimond Pro.', |
| 3695 | 'social.voice_pitch': 'A voice on the forge is part of Daimond Pro.', |
| 3696 | // The forge already holds a voice for this account, on another device. |
| 3697 | 'social.voice_already': 'Your voice is set on another device and will sync here shortly.', |
| 3698 | 'social.voice_reissue': 'I lost my voice \u2014 re-issue', |
| 3699 | 'social.voice_reissue_help': 'Makes a new voice; the old one stops working everywhere. Cannot be undone.', |
| 3700 | 'social.voice_reissue_do': 'Re-issue', |
| 3701 | 'social.voice_reissue_title': 'Re-issue your voice', |
| 3702 | 'social.voice_reissue_ask': 'Re-issue your voice? The old one stops working on every device, and this cannot be undone.', |
| 3703 | // The unobtrusive fallback: a voice the forge made elsewhere, pasted in. |
| 3704 | 'social.voice_have': 'I already have a voice', |
| 3705 | 'social.voice_have_help': 'Paste a voice the forge already gave you.', |
| 3706 | // The paste fallback form (see the note above). |
| 3707 | 'social.voice_replace': 'Replace the voice', |
| 3708 | 'social.voice_help': 'The line the forge showed you. Kept encrypted on this device.', |
| 3709 | 'social.voice_ph': 'Paste the line the forge showed you', |
| 3710 | 'social.voice_save': 'Save the voice', |
| 3711 | 'social.voice_saved': 'Your voice is held here, encrypted.', |
| 3712 | 'social.voice_failed': 'That voice could not be stored.', |
| 3713 | 'social.voice_forget': 'Forget it', |
| 3714 | 'social.voice_forget_help': 'Remove the copy on this device.', |
| 3715 | 'social.voice_forgotten': 'The copy on this device is gone.', |
| 3716 | 'social.voice_ask_forget': 'Forget your voice here? It was shown once and will not be shown again.', |
| 3717 | |
| 3718 | // ── What js/voice.js says when a voice will not do ────────── |
| 3719 | // The secret itself, and every refusal about it. These were in NO catalogue |
| 3720 | // at all -- not even this one -- from the day voice.js was written until |
| 3721 | // 2026-08-14: every one of them is reached through `tOr(key, english)`, which |
| 3722 | // paints correct English and reports nothing, and both existing checks |
| 3723 | // compare the seven locales AGAINST this file, so a key absent HERE was |
| 3724 | // invisible to both by construction. `dev/i18ncheck.mjs` now reads the call |
| 3725 | // sites as well as the tables, which is what closes that. |
| 3726 | // |
| 3727 | // NONE of these may quote the secret: an error message carrying a credential |
| 3728 | // is a credential in a screenshot. |
| 3729 | 'voice.err.empty': 'A voice is needed to write on the forge.', |
| 3730 | 'voice.err.shape': 'That does not look like a voice. Copy the whole line the forge printed.', |
| 3731 | 'voice.err.short': 'That is shorter than any voice the forge issues. Copy the whole line.', |
| 3732 | 'voice.err.long': 'That is longer than a voice can be.', |
| 3733 | 'voice.err.locked': 'Unlock Daimond first: your voice is kept encrypted under your passphrase.', |
| 3734 | 'voice.err.locked_send': 'Unlock Daimond to write on the forge: your voice is encrypted under your passphrase.', |
| 3735 | 'voice.err.unreadable': 'Your voice cannot be read with this passphrase. Set it again from the line the forge printed for you.', |
| 3736 | 'voice.err.inurl': 'A voice goes in a header, never in an address.', |
| 3737 | // What the voice is CALLED in a list of what did not survive a passphrase |
| 3738 | // change. Never shown on its own; `changepass.voice_not_resealed` is the |
| 3739 | // sentence a person reads. |
| 3740 | 'voice.the_voice': 'your forge voice', |
| 3741 | |
| 3742 | 'tracker.the_settle_voice': 'your settle voice', |
| 3743 | // Proposals, read from the forge as the panel is looked at. Nothing tells a |
| 3744 | // reader when one is answered, and no string here may suggest otherwise. |
| 3745 | 'social.live_note': 'These are read from the forge as you look at them. Nothing tells you when a proposal is answered; look again to find out.', |
| 3746 | 'social.loading': 'Reading the proposals…', |
| 3747 | 'social.none_shown': 'Nothing could be read just now.', |
| 3748 | 'social.none_yet': 'No proposals here yet. Yours would be the first.', |
| 3749 | 'social.reading': 'Reading it…', |
| 3750 | 'social.more': 'Show older', |
| 3751 | 'social.count.one': '{n} proposal', |
| 3752 | 'social.count.other': '{n} proposals', |
| 3753 | 'social.by': 'from {who}', |
| 3754 | 'social.said_n.one': '{n} reply', |
| 3755 | 'social.said_n.other': '{n} replies', |
| 3756 | 'social.built_on': 'written on build {build}', |
| 3757 | 'social.closed_by': 'closed by mark {mark}', |
| 3758 | 'social.move_floor': 'A note follows its content across files only when the change counts as a move — the floor is 64 bytes. Cut less, and the history holds a delete and an insert, so the note reports its content deleted.', |
| 3759 | 'social.state_open': 'Open', |
| 3760 | // The forge calls this state `accepted`; the panel has always called it |
| 3761 | // Being done, and the eight locales already hold that phrase. |
| 3762 | 'social.state_taken': 'Being done', |
| 3763 | 'social.state_done': 'Done', |
| 3764 | 'social.state_declined': 'Declined', |
| 3765 | 'social.tally': '{yes} for, {no} against', |
| 3766 | 'social.do': 'Do this', |
| 3767 | 'social.not': 'Not this', |
| 3768 | // Revising one's own proposal. Dark until the forge answers the per-asker flag; |
| 3769 | // see `drawAmendControl` in improve.js. |
| 3770 | 'social.amend': 'Revise this', |
| 3771 | 'social.amend_help': 'Replace what this proposal says; everyone sees the new words.', |
| 3772 | 'social.amend_title_ph': 'The one line this proposal is about', |
| 3773 | 'social.amend_body_ph': 'What happened, and what was expected instead', |
| 3774 | 'social.amend_save': 'Publish the revision', |
| 3775 | 'social.amend_save_help': 'Sends exactly these two boxes. Nothing else.', |
| 3776 | 'social.revised_n.one': 'revised once', |
| 3777 | 'social.revised_n.other': 'revised {n} times', |
| 3778 | // Drafting from the whole list of kept notes at once (www/js/triage.js). |
| 3779 | 'social.triage_run': 'Draft from all {n} notes', |
| 3780 | 'social.triage_run_help': 'Reads your kept notes and the forge, and drafts a plan. Nothing is sent until you Send a draft.', |
| 3781 | 'social.triage_running': 'Reading your notes…', |
| 3782 | 'social.triage_cost': 'All {n} notes on {model}, your key: ~{in} tokens in, up to {out} out — at most {usd}.', |
| 3783 | 'social.triage_cost_unknown': 'All {n} notes on {model}, your key: ~{in} tokens in, up to {out} out. This model is not priced, so the cost is not known first.', |
| 3784 | 'social.triage_cost_stop': 'Nothing is sent until you Send a draft.', |
| 3785 | 'social.triage_nomodel': 'Set a model in AI before drafting from your notes.', |
| 3786 | 'social.triage_nonotes': 'There are no kept notes to draft from.', |
| 3787 | 'social.triage_unread': 'The model did not return a readable plan. Nothing was sent; your notes are untouched.', |
| 3788 | 'social.triage_failed': 'The drafting did not finish: {why}', |
| 3789 | 'social.triage_plan': '{n} drafts from your notes, by {model}. Edit any, send the ones you want. Nothing has left this device.', |
| 3790 | 'social.triage_dropped': '{n} more came back in a shape this panel could not read, and are not shown.', |
| 3791 | 'social.triage_left': '{n} notes are in no draft:', |
| 3792 | 'social.triage_clear': 'Forget this plan', |
| 3793 | 'social.triage_clear_help': 'Clear the drafts. Nothing is sent.', |
| 3794 | 'social.triage_drop': 'Not this one', |
| 3795 | 'social.triage_drop_help': 'Drop this draft. Nothing is sent.', |
| 3796 | 'social.triage_send_help': 'Sends exactly what is in the box. Nothing else.', |
| 3797 | 'social.triage_kept': 'Nothing sent, nothing retried. Your notes are untouched.', |
| 3798 | 'social.triage_from': 'from {n} notes', |
| 3799 | 'social.triage_from_one': 'from 1 note', |
| 3800 | 'social.triage_kind_new': 'New proposal', |
| 3801 | 'social.triage_kind_comment': 'Comment on #{n}', |
| 3802 | 'social.triage_kind_revision': 'Revision of #{n}', |
| 3803 | 'social.triage_opened': 'Opened as proposal #{n}.', |
| 3804 | 'social.triage_said': 'Said on proposal #{n}.', |
| 3805 | 'social.triage_revised': 'Proposal #{n} is revised.', |
| 3806 | |
| 3807 | // The approve-list (js/approvelist.js): a local review queue of drafts, batch- |
| 3808 | // sent through the forge door. Terse — a label, not a paragraph. |
| 3809 | 'approve.count': '{n} drafts waiting, {sel} ticked. Edit any, tick the ones to send.', |
| 3810 | 'approve.send': 'Send selected', |
| 3811 | 'approve.sending': 'Sending…', |
| 3812 | 'approve.send_help': 'Sends exactly the ticked drafts, each as it is in its box.', |
| 3813 | 'approve.all': 'Tick all', |
| 3814 | 'approve.all_help': 'Tick every draft that can be sent.', |
| 3815 | 'approve.clear_sel': 'Untick all', |
| 3816 | 'approve.clear': 'Empty the queue', |
| 3817 | 'approve.clear_help': 'Remove every draft. Nothing is sent.', |
| 3818 | 'approve.drop': 'Not this one', |
| 3819 | 'approve.drop_help': 'Take this draft off the queue. Nothing is sent.', |
| 3820 | 'approve.tick': 'Send this one', |
| 3821 | 'approve.kind_new': 'New proposal', |
| 3822 | 'approve.kind_comment': 'Comment on #{n}', |
| 3823 | 'approve.kind_revision': 'Revision of #{n}', |
| 3824 | 'approve.target_new': 'opens a new proposal', |
| 3825 | 'approve.target_on': 'on proposal #{n}', |
| 3826 | 'approve.not_yours': 'Only the proposal’s author can revise it.', |
| 3827 | 'approve.novoice': 'No voice yet — a draft can only wait here.', |
| 3828 | 'approve.none_ticked': 'Tick a draft first.', |
| 3829 | 'approve.empty': 'Nothing to send.', |
| 3830 | 'approve.no_title': 'First line is the title — write one, then what happened.', |
| 3831 | 'approve.kept': 'Kept here; nothing tried again.', |
| 3832 | 'approve.sent_batch': 'Sent {sent}. {failed} still waiting.', |
| 3833 | |
| 3834 | 'social.vote_novoice': 'Set a voice to vote on this.', |
| 3835 | 'social.vote_off': 'Press again to take your vote back off.', |
| 3836 | 'social.reply': 'Say it', |
| 3837 | 'social.reply_ph': 'Say something about this proposal.', |
| 3838 | 'social.reply_help': 'Sends exactly this box. Nothing else.', |
| 3839 | |
| 3840 | // What the forge refused, said in words. None of them names which allowance |
| 3841 | // ran out: a limit that reports its own state is one somebody can pace |
| 3842 | // against, and a vote and a proposal draw on different budgets. |
| 3843 | // |
| 3844 | // TRANSLATORS — `social.err_absent` is a privacy rule, not a wording |
| 3845 | // preference. The forge answers `absent` for BOTH "no such repository" and |
| 3846 | // "this repository is private", deliberately and permanently, because any |
| 3847 | // token, status or timing that separated them would republish exactly what a |
| 3848 | // private repository is withholding. The sentence must therefore be true in |
| 3849 | // both cases: "there is no such repository" is false when it is private, and |
| 3850 | // "this repository is private" leaks. Say only that it is not available to |
| 3851 | // you. See dev/IMPROVE_CONTRACT.md §7 (and the forge contract §3.1). |
| 3852 | 'social.err_absent': 'This repository is not available to you.', |
| 3853 | 'social.err_unvoiced': 'The forge was given no voice, so it refused.', |
| 3854 | 'social.err_unknown': 'The forge does not recognise your voice. Set it again from the line the forge printed for you.', |
| 3855 | 'social.err_unpermitted': 'Your voice may not do that here.', |
| 3856 | 'social.err_amend_unpermitted': 'Only the person who opened this proposal may revise it.', |
| 3857 | 'social.err_throttled': 'Too many requests just now. Wait a little, then try again.', |
| 3858 | 'social.err_throttled_address': 'Too many requests from this address just now. Wait a little, then try again.', |
| 3859 | 'social.err_throttled_failing': 'Too many failing requests just now. Wait a little, then try again.', |
| 3860 | 'social.err_malformed': 'The forge could not read what Daimond asked it. That is a fault in Daimond, not in what you wrote.', |
| 3861 | 'social.err_no_proposal': 'There is no such proposal here.', |
| 3862 | 'social.err_unsupported': 'The forge does not answer that.', |
| 3863 | 'social.err_internal': 'Something went wrong at the forge. This is not your fault.', |
| 3864 | 'social.err_gateway': 'Daimond could not reach the forge just now.', |
| 3865 | 'social.err_session': 'Daimond is not signed in just now, so it could not reach the forge.', |
| 3866 | 'social.err_toolong': 'That is longer than the forge accepts. Shorten it, or send it in two.', |
| 3867 | 'social.err_offline': 'Nothing could be sent just now.', |
| 3868 | |
| 3869 | // ── A reference, drawn as a chip ─────────────────────────── |
| 3870 | // |
| 3871 | // Four things a message may point at: a proposal, a build, a panel and a |
| 3872 | // guide page. THE READER RESOLVES IT. What travels is a kind, an id and the |
| 3873 | // sender's own description, and that description is drawn only when the |
| 3874 | // resolution fails -- as plain text, said to be the sender's words. A title |
| 3875 | // taken from the sender and drawn as though it were a forge record is a lie |
| 3876 | // waiting to happen and a place to inject markup. |
| 3877 | // |
| 3878 | // i18n-family: ref.kind_ = proposal build panel guide |
| 3879 | 'ref.kind_proposal': 'Proposal', |
| 3880 | 'ref.kind_build': 'Build', |
| 3881 | 'ref.kind_panel': 'Panel', |
| 3882 | 'ref.kind_guide': 'Guide', |
| 3883 | 'ref.proposal': 'Proposal #{n}', |
| 3884 | 'ref.build': 'Build {id}', |
| 3885 | 'ref.panel': 'The {name} panel', |
| 3886 | 'ref.guide': 'Guide: {page}', |
| 3887 | 'ref.open_proposal': 'Open the proposal', |
| 3888 | 'ref.open_build': 'Open the release note', |
| 3889 | 'ref.open_panel': 'Open it', |
| 3890 | 'ref.open_guide': 'Open the page', |
| 3891 | 'ref.expand': 'Show what this is', |
| 3892 | 'ref.reading': 'Reading it…', |
| 3893 | // Not "not found". A signed-out reader is refused before the forge is asked, |
| 3894 | // so nothing is known about whether the thing exists -- and saying it does |
| 3895 | // not would be the panel inventing an answer it was never given. |
| 3896 | 'ref.signin': 'Sign in to open this.', |
| 3897 | // The sender's own words, said to be the sender's own words. |
| 3898 | 'ref.said': 'Described as: {text}', |
| 3899 | 'ref.unopenable': 'There is no opening this here.', |
| 3900 | 'ref.said_n.one': '{n} comment, public', |
| 3901 | 'ref.said_n.other': '{n} comments, public', |
| 3902 | 'ref.build_here': 'This is the build you are on.', |
| 3903 | 'ref.build_other': 'You are on build {id}.', |
| 3904 | 'ref.build_update': 'Update to it', |
| 3905 | |
| 3906 | // ── The dock count badge ─────────────────────────────────── |
| 3907 | // One number on a dock chip, and the whole of what this app does to say |
| 3908 | // something arrived while you were elsewhere. Never a zero: a badge showing |
| 3909 | // 0 is a mark that has to be read before it can be ignored. |
| 3910 | 'dock.unseen.one': '{n} unread', |
| 3911 | 'dock.unseen.other': '{n} unread', |
| 3912 | |
| 3913 | // ── The sealing key, and the identity that holds it ──────── |
| 3914 | // js/identity.js has named this key since the sealing key landed and this |
| 3915 | // table never had it, so every reader in every language got the English by |
| 3916 | // fallback and nothing said so. |
| 3917 | 'identity.err_no_sealing_key': 'This device has no sealing key, so it cannot open a sealed message. Unlock the identity once and one will be made.', |
| 3918 | |
| 3919 | 'trash.nothing': 'Nothing has been deleted.', |
| 3920 | 'trash.kept_days': 'Kept for {days} days, then destroyed.', |
| 3921 | 'trash.holding.one': '{n} thing, {bytes}', |
| 3922 | 'trash.holding.other': '{n} things, {bytes}', |
| 3923 | 'trash.until': 'Until {date}', |
| 3924 | 'trash.deleted_why': 'Deleted. It is only here.', |
| 3925 | // The app did this, not the user. It must not read as a reproach. |
| 3926 | 'trash.expired_why': 'Its time ran out. It is only here.', |
| 3927 | 'trash.kind_chat': 'chat', |
| 3928 | 'trash.kind_diamond': 'diamond', |
| 3929 | 'trash.unnamed_chat': 'Unnamed chat', |
| 3930 | // A trashed chat can still be made into a Diamond, which is the useful act |
| 3931 | // on a conversation somebody has just found again. |
| 3932 | 'trash.keep': 'Keep', |
| 3933 | 'trash.keep_help': 'Make a Diamond of this chat, with the whole conversation as its first artefact.', |
| 3934 | 'trash.keep_named': 'Keep {name} as a Diamond', |
| 3935 | 'trash.restore': 'Restore', |
| 3936 | 'trash.restore_named': 'Restore {name}', |
| 3937 | 'trash.restore_all': 'Restore everything', |
| 3938 | 'trash.purge': 'Delete permanently', |
| 3939 | 'trash.purge_named': 'Delete {name} permanently', |
| 3940 | 'trash.purge_ask': 'Delete “{name}” for good? It goes from every device, and there is no way back.', |
| 3941 | 'trash.purge_ok': 'Delete permanently', |
| 3942 | 'trash.empty': 'Empty trash', |
| 3943 | 'trash.empty_ask.one': 'Destroy the {n} thing in the trash for good? It goes from every device, and there is no way back.', |
| 3944 | 'trash.empty_ask.other': 'Destroy all {n} things in the trash for good? They go from every device, and there is no way back.', |
| 3945 | 'trash.empty_ok': 'Empty trash', |
| 3946 | 'trash.moved': 'Moved “{name}” to the trash.', |
| 3947 | 'trash.moved_n.one': 'Moved {n} thing to the trash.', |
| 3948 | 'trash.moved_n.other': 'Moved {n} things to the trash.', |
| 3949 | // The retention sweep, reported once at boot. Destroyed, and not coming |
| 3950 | // back: the word has to carry that. |
| 3951 | 'trash.swept.one': 'One thing had been in the trash for {days} days and has been destroyed.', |
| 3952 | 'trash.swept.other': '{n} things had been in the trash for {days} days and have been destroyed.', |
| 3953 | |
| 3954 | // ── The Terms and the Privacy Policy, in the app ─────────── |
| 3955 | // js/legal.js puts both documents in Daimond's own Web panel rather than |
| 3956 | // handing the reader to another website. See www/guide/legal/. |
| 3957 | // i18n-indirect: legal.js d.key = legal.terms legal.privacy |
| 3958 | 'legal.terms': 'Terms of Service', |
| 3959 | 'legal.privacy': 'Privacy Policy', |
| 3960 | 'legal.draft_note': 'In force from 13 August 2026. Points marked [TO CONFIRM] are still being settled.', |
| 3961 | |
| 3962 | // ── Lapse notices ────────────────────────────────────────── |
| 3963 | // Two clauses of the Terms promise a notice on this screen before something |
| 3964 | // of the user's lapses: §13 and Privacy §9 on stored file data, §7 on the |
| 3965 | // five-year Pro term. Every sentence traces to one in landing/terms.html, and |
| 3966 | // none of them may be more generous than the clause it quotes. See |
| 3967 | // js/lapse.js and dev/verify_legalreach.mjs. |
| 3968 | 'lapse.hide': 'Hide until tomorrow', |
| 3969 | 'lapse.read_clause': 'What the Terms say', |
| 3970 | 'lapse.storage_head': 'Stored files above the free allowance will be deleted on {date}.', |
| 3971 | 'lapse.storage_head_undated': 'Stored files above the free allowance will be deleted when the grace period ends.', |
| 3972 | 'lapse.storage_why': 'Your credits will not cover the cloud storage you are holding, so the metering has paused. Nothing is being back-charged, and you can still read everything you have stored.', |
| 3973 | 'lapse.storage_size': 'About {size} is held above the free allowance.', |
| 3974 | 'lapse.storage_what': 'If the balance is not restored by then, the stored data above the free allowance is deleted. Files on this device are untouched.', |
| 3975 | 'lapse.top_up': 'Top up credits', |
| 3976 | 'lapse.credits_pitch': 'Topping up stops the stored data above the free allowance being deleted.', |
| 3977 | 'lapse.lic_head': 'Your Pro licence ends on {date}.', |
| 3978 | 'lapse.lic_head_past': 'Your Pro licence ended on {date}.', |
| 3979 | 'lapse.lic_off': 'Cross-device sync, cloud storage and Daimond Email switch off then, because each of those is a service we run on our side.', |
| 3980 | 'lapse.lic_off_past': 'Cross-device sync, cloud storage and Daimond Email are off, because each of those is a service we run on our side.', |
| 3981 | 'lapse.lic_keep': 'Everything on this device carries on exactly as before: your files, your chats, your Diamonds, your identity and your own provider key. Nothing is deleted, nothing is locked, and nothing you have made becomes unreadable.', |
| 3982 | 'lapse.lic_pull': 'Pulling down what you have already stored never stops, and your credits are unaffected.', |
| 3983 | |
| 3984 | // ── The Tracker view (js/tracker.js) ─────────────────────── |
| 3985 | // A decision-queue BOARD onto Daimond's own development, tracked as proposals |
| 3986 | // on the Oregami forge: four columns, the life of a proposal left to right. |
| 3987 | // Votes are shown as a tally only; the settle controls are the owner's and |
| 3988 | // operators'. Terse throughout -- the detail lives one press away. |
| 3989 | 'tracker.title': 'Development', |
| 3990 | 'tracker.count': '{n} proposals', |
| 3991 | // The All / Mine filter on the hub. "Mine" is proposals THIS DEVICE raised, |
| 3992 | // matched by the locally-tracked numbers -- the local voice has no name, so it |
| 3993 | // cannot be matched by author. Defaults to All. |
| 3994 | 'tracker.filter_all': 'All', |
| 3995 | 'tracker.filter_mine': 'Mine', |
| 3996 | 'tracker.mine_empty': 'Nothing raised from this device yet.', |
| 3997 | 'tracker.loading': 'Reading…', |
| 3998 | 'tracker.empty': 'No proposals yet.', |
| 3999 | 'tracker.comments': '{n} comments', |
| 4000 | 'tracker.tally': '{yes} for, {no} against', |
| 4001 | 'tracker.back': 'Back', |
| 4002 | 'tracker.revisions': 'Revisions', |
| 4003 | 'tracker.discussion': 'Comments', |
| 4004 | 'tracker.no_comments': 'No comments.', |
| 4005 | // Vote and comment, cast with the pull voice through the Social panel. The |
| 4006 | // upvote shows the for-count; the reply box lives in the opened card. Without a |
| 4007 | // voice the "set a voice" affordance shows in place of a control that would fail. |
| 4008 | 'tracker.upvotes': '▲ {n}', |
| 4009 | 'tracker.upvote_help': 'Upvote this. Press again to take it back.', |
| 4010 | 'tracker.reply_ph': 'Say something about this proposal.', |
| 4011 | 'tracker.say': 'Say it', |
| 4012 | 'tracker.say_help': 'Sends exactly this box. Nothing else.', |
| 4013 | 'tracker.say_empty': 'Write something first.', |
| 4014 | 'tracker.say_novoice': 'Set a voice in Social to comment.', |
| 4015 | 'tracker.vote_novoice': 'Set a voice in Social to vote.', |
| 4016 | 'tracker.set_voice': 'Set a voice', |
| 4017 | 'tracker.set_voice_help': 'A voice lets you vote and comment. Set it in Social.', |
| 4018 | // The four board columns, and the empty note under one that holds nothing. |
| 4019 | // tracker.js:drawColumn builds `tracker.` + col.key for the four column labels; |
| 4020 | // the prefix is the board's whole namespace (title, columns, state, admin, errors) |
| 4021 | // and only these four are formed by concatenation, so the sweep is one-way. |
| 4022 | // i18n-family: tracker. = one-way col_open col_green col_ship col_drop -- the prefix is the board's whole namespace, not a set; only the four column labels are built by concatenation in tracker.js. |
| 4023 | 'tracker.col_open': 'Awaiting you', |
| 4024 | 'tracker.col_green': 'Greenlit', |
| 4025 | 'tracker.col_ship': 'Shipped', |
| 4026 | 'tracker.col_drop': 'Dropped', |
| 4027 | 'tracker.col_empty': 'Nothing here.', |
| 4028 | // A Greenlit card's latest activity, when it is drawn from the listing rather |
| 4029 | // than the last comment: "{n} comments · active {when}". |
| 4030 | 'tracker.active': 'active {when}', |
| 4031 | // A Shipped card's build stamp: the real deployed id, or a wait for one. |
| 4032 | 'tracker.shipped_in': 'Shipped in', |
| 4033 | 'tracker.shipped_await': 'Shipped — awaiting build stamp.', |
| 4034 | 'tracker.ship_help': 'Open the transparency log.', |
| 4035 | // The state words a reader is shown on the detail head for the forge's |
| 4036 | // open/accepted/declined/done. |
| 4037 | 'tracker.state_open': 'Open', |
| 4038 | 'tracker.state_taken': 'Being done', |
| 4039 | 'tracker.state_done': 'Done', |
| 4040 | 'tracker.state_declined': 'Declined', |
| 4041 | // The settle controls, on a board card and on the detail. |
| 4042 | 'tracker.accept': 'Accept', |
| 4043 | 'tracker.decline': 'Decline', |
| 4044 | 'tracker.done': 'Mark done', |
| 4045 | 'tracker.reopen': 'Reopen', |
| 4046 | // Refusals, said not swallowed. `absent` is true whether the repository is |
| 4047 | // missing or private, and leaks neither. |
| 4048 | 'tracker.err_offline': 'Nothing could be read just now.', |
| 4049 | 'tracker.err_absent': 'This repository is not available.', |
| 4050 | 'tracker.err_unvoiced': 'The forge was given no voice.', |
| 4051 | 'tracker.err_unknown': 'The forge does not recognise that voice.', |
| 4052 | 'tracker.err_unpermitted': 'That voice may not settle proposals here.', |
| 4053 | 'tracker.err_throttled': 'Too many requests just now. Wait, then try again.', |
| 4054 | 'tracker.err_throttled_address': 'Too many requests from this address. Wait, then try again.', |
| 4055 | 'tracker.err_malformed': 'The forge could not read that request.', |
| 4056 | 'tracker.err_no_proposal': 'There is no such proposal.', |
| 4057 | 'tracker.err_unsupported': 'The forge does not answer that.', |
| 4058 | 'tracker.err_internal': 'Something went wrong at the forge.', |
| 4059 | 'tracker.err_session': 'Not signed in, so the forge could not be reached.', |
| 4060 | 'tracker.err_gateway': 'The forge could not be reached just now.', |
| 4061 | // The nav/panel label. Kept in this block, not beside the other `panel.*` |
| 4062 | // keys, so this lane's addition sits at one anchor. |
| 4063 | 'panel.tracker': 'Improve', |
| 4064 | // The settle voice: a hand-minted admin voice, pasted once and wrapped under |
| 4065 | // the passphrase. When none is held the board shows the "add your settle voice" |
| 4066 | // affordance in place of settle buttons that would fail. |
| 4067 | 'tracker.admin_none': 'Reading only. Settling needs your admin voice.', |
| 4068 | 'tracker.admin_add': 'Add your settle voice', |
| 4069 | 'tracker.admin_held': 'Settle voice held, encrypted.', |
| 4070 | 'tracker.admin_forget': 'Forget it', |
| 4071 | 'tracker.admin_forget_ask': 'Forget your settle voice here? It was shown once.', |
| 4072 | 'tracker.admin_forgotten': 'The copy on this device is gone.', |
| 4073 | 'tracker.admin_ph': 'Paste your settle voice', |
| 4074 | 'tracker.admin_save': 'Save', |
| 4075 | 'tracker.admin_saved': 'Settle voice held, encrypted.', |
| 4076 | 'tracker.admin_need': 'Add your settle voice first.', |
| 4077 | 'tracker.admin_empty': 'Paste your settle voice.', |
| 4078 | 'tracker.admin_short': 'That looks too short to be a whole voice.', |
| 4079 | 'tracker.admin_locked': 'Unlock Daimond first: your settle voice is kept encrypted under your passphrase.', |
| 4080 | 'tracker.admin_locked_send': 'Unlock Daimond to settle: your settle voice is encrypted under your passphrase.', |
| 4081 | 'tracker.admin_unreadable': 'Your settle voice cannot be read with this passphrase. Set it again.', |
| 4082 | 'tracker.admin_store': 'That voice could not be stored on this device.', |
| 4083 | }); |
| 4084 | })(); |