summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po373
-rw-r--r--res/lang/en.binbin2275 -> 6816 bytes
-rw-r--r--src/ui/certimportwidget.c2
-rw-r--r--src/ui/documentwidget.c251
-rw-r--r--src/ui/labelwidget.c2
-rw-r--r--src/ui/sidebarwidget.c177
-rw-r--r--src/ui/translation.c2
-rw-r--r--src/ui/util.c20
-rw-r--r--src/ui/window.c6
9 files changed, 607 insertions, 226 deletions
diff --git a/po/en.po b/po/en.po
index cb203ca8..27948b3c 100644
--- a/po/en.po
+++ b/po/en.po
@@ -10,6 +10,12 @@ msgstr "Version"
10msgid "about.powered" 10msgid "about.powered"
11msgstr "Powered by SDL 2, OpenSSL, and ☕️" 11msgstr "Powered by SDL 2, OpenSSL, and ☕️"
12 12
13msgid "cancel"
14msgstr "Cancel"
15
16msgid "dismiss"
17msgstr "Dismiss"
18
13msgid "menu.title.file" 19msgid "menu.title.file"
14msgstr "File" 20msgstr "File"
15 21
@@ -157,21 +163,36 @@ msgstr "Go to Root"
157msgid "menu.reload" 163msgid "menu.reload"
158msgstr "Reload Page" 164msgstr "Reload Page"
159 165
166msgid "menu.autoreload"
167msgstr "Set Auto-Reload…"
168
169msgid "menu.page.bookmark"
170msgstr "Bookmark Page…"
171
172msgid "menu.page.subscribe"
173msgstr "Subscribe to Page…"
174
160msgid "menu.aboutpages" 175msgid "menu.aboutpages"
161msgstr "About Pages" 176msgstr "About Pages"
162 177
163msgid "menu.debug" 178msgid "menu.debug"
164msgstr "Debug Information" 179msgstr "Debug Information"
165 180
166msgid "menu.bookmark.page"
167msgstr "Bookmark This Page…"
168
169msgid "menu.subscribe.page"
170msgstr "Subscribe to This Page…"
171
172msgid "menu.import.links" 181msgid "menu.import.links"
173msgstr "Import All Links on Page…" 182msgstr "Import All Links on Page…"
174 183
184msgid "menu.page.import"
185msgstr "Import Links as Bookmarks…"
186
187msgid "menu.page.translate"
188msgstr "Translate…"
189
190msgid "menu.page.copyurl"
191msgstr "Copy Page URL"
192
193msgid "menu.page.copysource"
194msgstr "Copy Page Source"
195
175msgid "menu.bookmarks.refresh" 196msgid "menu.bookmarks.refresh"
176msgstr "Refresh Remote Bookmarks" 197msgstr "Refresh Remote Bookmarks"
177 198
@@ -200,6 +221,12 @@ msgid "sidebar.identities"
200msgstr "Identities" 221msgstr "Identities"
201 222
202msgid "sidebar.outline" 223msgid "sidebar.outline"
224msgstr "Outline"
225
226msgid "sidebar.unread"
227msgstr "Unread"
228
229msgid "toolbar.outline"
203msgstr "Page Outline" 230msgstr "Page Outline"
204 231
205msgid "hint.findtext" 232msgid "hint.findtext"
@@ -215,8 +242,342 @@ msgstr "Updating Feeds"
215msgid "mb" 242msgid "mb"
216msgstr "MB" 243msgstr "MB"
217 244
245# kilobytes, used as the unit after a number
246msgid "kb"
247msgstr "KB"
248
249msgid "bytes"
250msgstr "bytes"
251
218# strftime() formatted, split on two lines 252# strftime() formatted, split on two lines
219#, c-format 253#, c-format
220msgid "page.timestamp" 254msgid "page.timestamp"
221msgstr "Received at %I:%M %p\non %b %d, %Y" 255msgstr "Received at %I:%M %p\non %b %d, %Y"
222 256
257# strftime() formatted
258#, c-format
259msgid "sidebar.date.thisyear"
260msgstr "%b. %d"
261
262# strftime() formatted
263#, c-format
264msgid "sidebar.date.otheryear"
265msgstr "%b. %d, %Y"
266
267msgid "feeds.today"
268msgstr "Today"
269
270msgid "feeds.entry.newtab"
271msgstr "Open Entry in New Tab"
272
273msgid "feeds.entry.markread"
274msgstr "Mark as Read"
275
276msgid "feeds.entry.markunread"
277msgstr "Mark as Unread"
278
279msgid "sidebar.entry.bookmark"
280msgstr "Add Bookmark…"
281
282msgid "feeds.entry.bookmark"
283msgstr "Add Bookmark…"
284
285msgid "feeds.entry.openfeed"
286msgstr "Open Feed Page"
287
288msgid "feeds.edit"
289msgstr "Edit Feed…"
290
291msgid "feeds.unsubscribe"
292msgstr "Unsubscribe…"
293
294msgid "feeds.markallread"
295msgstr "Mark All as Read"
296
297msgid "feeds.refresh"
298msgstr "Refresh Feeds"
299
300msgid "menu.opentab"
301msgstr "Open in New Tab"
302
303msgid "menu.opentab.background"
304msgstr "Open in Background Tab"
305
306msgid "menu.edit"
307msgstr "Edit…"
308
309msgid "menu.dup"
310msgstr "Duplicate…"
311
312msgid "menu.copyurl"
313msgstr "Copy URL"
314
315msgid "menu.forgeturl"
316msgstr "Forget URL"
317
318msgid "history.clear"
319msgstr "Clear History…"
320
321msgid "heading.history.clear"
322msgstr "CLEAR HISTORY"
323
324msgid "dlg.confirm.history.clear"
325msgstr "Do you really want to erase the history of all visited pages?"
326
327msgid "dlg.history.clear"
328msgstr "Clear History"
329
330msgid "bookmark.tag.home"
331msgstr "Use as Homepage"
332
333msgid "bookmark.tag.sub"
334msgstr "Subscribe to Feed"
335
336msgid "bookmark.tag.remote"
337msgstr "Use as Bookmark Source"
338
339msgid "bookmark.untag.home"
340msgstr "Remove Homepage"
341
342msgid "bookmark.untag.sub"
343msgstr "Unsubscribe from Feed"
344
345msgid "bookmark.untag.remote"
346msgstr "Remove Bookmark Source"
347
348msgid "bookmark.delete"
349msgstr "Delete Bookmark"
350
351msgid "bookmarks.reload"
352msgstr "Refresh Remote Sources"
353
354msgid "ident.using"
355msgstr "Using on this page"
356
357msgid "ident.notused"
358msgstr "Not used"
359
360#, c-format
361msgid "ident.usedonurls"
362msgstr "Used on %zu URLs"
363
364msgid "ident.temporary"
365msgstr "Temporary"
366
367# strftime() formatted
368#, c-format
369msgid "ident.expiry"
370msgstr "Expires %b %d, %Y"
371
372msgid "ident.use"
373msgstr "Use on This Page"
374
375msgid "ident.stopuse"
376msgstr "Stop Using on This Page"
377
378msgid "ident.stopuse.all"
379msgstr "Stop Using Everywhere"
380
381msgid "ident.showuse"
382msgstr "Show Usage"
383
384msgid "heading.ident.use"
385msgstr "IDENTITY USAGE"
386
387msgid "menu.edit.notes"
388msgstr "Edit Notes…"
389
390msgid "heading.ident.notes"
391msgstr "IDENTITY NOTES"
392
393# %s refers to name of an identity.
394#, c-format
395msgid "dlg.ident.notes"
396msgstr "Notes about %s:"
397
398msgid "ident.fingerprint"
399msgstr "Copy Fingerprint"
400
401msgid "ident.delete"
402msgstr "Delete Identity…"
403
404msgid "heading.ident.delete"
405msgstr "DELETE IDENTITY"
406
407#, c-format
408msgid "dlg.confirm.ident.delete"
409msgstr "Do you really want to delete the identity\n%s%s%s\nincluding its certificate and private key files?"
410
411msgid "dlg.ident.delete"
412msgstr "Delete Identity and Files"
413
414msgid "sidebar.empty.idents"
415msgstr "No Identities"
416
417# The %s format characters are used to highlight the word "Help" and must be used in the translation in same way as here.
418#, c-format
419msgid "ident.gotohelp"
420msgstr "See %sHelp%s for more information about TLS client certificates."
421
422msgid "heading.unsub"
423msgstr "UNSUBSCRIBE"
424
425#, c-format
426msgid "dlg.confirm.unsub"
427msgstr "Really unsubscribe from feed\n\"%s\"?"
428
429msgid "dlg.unsub"
430msgstr "Unsubscribe"
431
432#, c-format
433msgid "error.unsupported.suggestsave"
434msgstr "\n```\n%s\n```\nYou can save it as a file to your Downloads folder, though. Press %s or select \"%s\" from the menu."
435
436msgid "heading.pageinfo"
437msgstr "PAGE INFORMATION"
438
439msgid "pageinfo.header.cached"
440msgstr "(cached content)"
441
442msgid "pageinfo.cert.status"
443msgstr "Certificate Status:"
444
445msgid "pageinfo.cert.ca.verified"
446msgstr "Verified by CA"
447
448msgid "pageinfo.cert.ca.unverified"
449msgstr "Not verified by CA"
450
451msgid "pageinfo.cert.notexpired"
452msgstr "Not expired"
453
454msgid "pageinfo.cert.expired"
455msgstr "Expired"
456
457msgid "pageinfo.cert.trusted"
458msgstr "Trusted"
459
460msgid "pageinfo.cert.untrusted"
461msgstr "Not trusted"
462
463msgid "pageinfo.domain.match"
464msgstr "Domain name matches"
465
466msgid "pageinfo.domain.mismatch"
467msgstr "Domain name mismatch"
468
469msgid "dlg.cert.trust"
470msgstr "Trust"
471
472msgid "dlg.cert.fingerprint"
473msgstr "Copy Fingerprint"
474
475#, c-format
476msgid "dlg.input.prompt"
477msgstr "Please enter input for %s:"
478
479msgid "dlg.input.send"
480msgstr "Send"
481
482msgid "heading.save"
483msgstr "FILE SAVED"
484
485msgid "heading.save.incomplete"
486msgstr "PAGE INCOMPLETE"
487
488msgid "dlg.save.incomplete"
489msgstr "The page contents are still being downloaded."
490
491msgid "dlg.save.size"
492msgstr "Size:"
493
494msgid "heading.save.error"
495msgstr "ERROR SAVING FILE"
496
497msgid "heading.import.bookmarks"
498msgstr "IMPORT BOOKMARKS"
499
500msgid "dlg.import.found"
501msgstr "Found one new link on the page."
502
503#, c-format
504msgid "dlg.import.found.many"
505msgstr "Found %d new links on the page."
506
507#, c-format
508msgid "dlg.import.add"
509msgstr "%sAdd Bookmark"
510
511#, c-format
512msgid "dlg.import.add.many"
513msgstr "%sAdd %d Bookmarks"
514
515msgid "dlg.import.notnew"
516msgstr "All links on this page are already bookmarked."
517
518msgid "heading.autoreload"
519msgstr "AUTO-RELOAD"
520
521msgid "dlg.autoreload"
522msgstr "Select the auto-reload interval for this tab."
523
524msgid "link.newtab"
525msgstr "Open Link in New Tab"
526
527msgid "link.newtab.background"
528msgstr "Open Link in Background Tab"
529
530msgid "link.browser"
531msgstr "Open Link in Default Browser"
532
533msgid "link.noproxy"
534msgstr "Open without Proxy"
535
536msgid "link.copy"
537msgstr "Copy Link"
538
539msgid "link.bookmark"
540msgstr "Bookmark Link…"
541
542msgid "link.download"
543msgstr "Download Linked File"
544
545msgid "heading.openlink"
546msgstr "OPEN LINK"
547
548#, c-format
549msgid "dlg.openlink.confirm"
550msgstr "Open this link in the default browser?\n%s%s"
551
552msgid "dlg.openlink"
553msgstr "Open Link"
554
555msgid "heading.certwarn"
556msgstr "UNTRUSTED CERTIFICATE"
557
558#, c-format
559msgid "dlg.certwarn.mayberenewed"
560msgstr "The received certificate may have been recently renewed — it is for the correct domain and has not expired. The currently trusted certificate will expire on %s, in %d days."
561
562msgid "dlg.certwarn.different"
563msgstr "The received certificate is valid but different than the one we trust."
564
565#, c-format
566msgid "dlg.certwarn.expired"
567msgstr "The received certificate has expired on %s."
568
569msgid "dlg.certwarn.domain"
570msgstr "The received certificate is for the wrong domain (%s). This may be a server configuration problem."
571
572msgid "dlg.certwarn.domain.expired"
573msgstr "The received certificate is expired AND for the wrong domain."
574
575msgid "link.hint.audio"
576msgstr "Play Audio"
577
578msgid "link.hint.image"
579msgstr "View Image"
580
581msgid "bookmark.title.blank"
582msgstr "Blank Page"
583
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 0603e6ad..ab178d78 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
index 0c1cf674..d47851f5 100644
--- a/src/ui/certimportwidget.c
+++ b/src/ui/certimportwidget.c
@@ -154,7 +154,7 @@ void init_CertImportWidget(iCertImportWidget *d) {
154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI))); 154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
155 iWidget *buttons = makeDialogButtons_Widget( 155 iWidget *buttons = makeDialogButtons_Widget(
156 (iMenuItem[]){ 156 (iMenuItem[]){
157 { "Cancel", 0, 0, NULL }, 157 { "${cancel}", 0, 0, NULL },
158 { uiTextAction_ColorEscape "Import", SDLK_RETURN, KMOD_PRIMARY, "certimport.accept" } }, 158 { uiTextAction_ColorEscape "Import", SDLK_RETURN, KMOD_PRIMARY, "certimport.accept" } },
159 2); 159 2);
160 addChild_Widget(w, iClob(buttons)); 160 addChild_Widget(w, iClob(buttons));
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 685bafd6..6ce53e44 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -824,9 +824,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
824 iString *key = collectNew_String(); 824 iString *key = collectNew_String();
825 toString_Sym(SDLK_s, KMOD_PRIMARY, key); 825 toString_Sym(SDLK_s, KMOD_PRIMARY, key);
826 appendFormat_String(src, 826 appendFormat_String(src,
827 "\n```\n%s\n```\n" 827 cstr_Lang("error.unsupported.suggestsave"),
828 "You can save it as a file to your Downloads folder, though. "
829 "Press %s or select \"%s\" from the menu.",
830 cstr_String(meta), 828 cstr_String(meta),
831 cstr_String(key), 829 cstr_String(key),
832 saveToDownloads_Label); 830 saveToDownloads_Label);
@@ -1045,7 +1043,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
1045 updateTrust_DocumentWidget_(d, resp); 1043 updateTrust_DocumentWidget_(d, resp);
1046 d->sourceTime = resp->when; 1044 d->sourceTime = resp->when;
1047 d->sourceStatus = success_GmStatusCode; 1045 d->sourceStatus = success_GmStatusCode;
1048 format_String(&d->sourceHeader, "(cached content)"); 1046 format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached"));
1049 updateTimestampBuf_DocumentWidget_(d); 1047 updateTimestampBuf_DocumentWidget_(d);
1050 set_Block(&d->sourceContent, &resp->body); 1048 set_Block(&d->sourceContent, &resp->body);
1051 updateDocument_DocumentWidget_(d, resp, iTrue); 1049 updateDocument_DocumentWidget_(d, resp, iTrue);
@@ -1220,9 +1218,9 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
1220 NULL, 1218 NULL,
1221 format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)), 1219 format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)),
1222 isEmpty_String(&resp->meta) 1220 isEmpty_String(&resp->meta)
1223 ? format_CStr("Please enter input for %s:", cstr_Rangecc(parts.path)) 1221 ? format_CStr(cstr_Lang("dlg.input.prompt"), cstr_Rangecc(parts.path))
1224 : cstr_String(&resp->meta), 1222 : cstr_String(&resp->meta),
1225 uiTextCaution_ColorEscape "Send \u21d2", 1223 uiTextCaution_ColorEscape "${dlg.input.send} \u21d2",
1226 format_CStr("!document.input.submit doc:%p", d)); 1224 format_CStr("!document.input.submit doc:%p", d));
1227 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), 1225 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"),
1228 statusCode == sensitiveInput_GmStatusCode); 1226 statusCode == sensitiveInput_GmStatusCode);
@@ -1469,14 +1467,14 @@ static void saveToDownloads_(const iString *url, const iString *mime, const iBlo
1469#if defined (iPlatformAppleMobile) 1467#if defined (iPlatformAppleMobile)
1470 exportDownloadedFile_iOS(savePath); 1468 exportDownloadedFile_iOS(savePath);
1471#else 1469#else
1472 makeMessage_Widget(uiHeading_ColorEscape "FILE SAVED", 1470 makeMessage_Widget(uiHeading_ColorEscape "${heading.save}",
1473 format_CStr("%s\nSize: %.3f %s", cstr_String(path_File(f)), 1471 format_CStr("%s\n${dlg.save.size} %.3f %s", cstr_String(path_File(f)),
1474 isMega ? size / 1.0e6f : (size / 1.0e3f), 1472 isMega ? size / 1.0e6f : (size / 1.0e3f),
1475 isMega ? "MB" : "KB")); 1473 isMega ? "${mb}" : "${kb}"));
1476#endif 1474#endif
1477 } 1475 }
1478 else { 1476 else {
1479 makeMessage_Widget(uiTextCaution_ColorEscape "ERROR SAVING FILE", 1477 makeMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}",
1480 strerror(errno)); 1478 strerror(errno));
1481 } 1479 }
1482 iRelease(f); 1480 iRelease(f);
@@ -1625,57 +1623,62 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1625 } 1623 }
1626 iString *msg = collectNew_String(); 1624 iString *msg = collectNew_String();
1627 if (isEmpty_String(&d->sourceHeader)) { 1625 if (isEmpty_String(&d->sourceHeader)) {
1628 appendFormat_String(msg, "%s\n%zu bytes\n", cstr_String(meta), size_Block(&d->sourceContent)); 1626 appendFormat_String(msg, "%s\n%zu ${bytes}\n", cstr_String(meta), size_Block(&d->sourceContent));
1629 } 1627 }
1630 else { 1628 else {
1631 appendFormat_String(msg, "%s\n", cstr_String(&d->sourceHeader)); 1629 appendFormat_String(msg, "%s\n", cstr_String(&d->sourceHeader));
1632 if (size_Block(&d->sourceContent)) { 1630 if (size_Block(&d->sourceContent)) {
1633 appendFormat_String(msg, "%zu bytes\n", size_Block(&d->sourceContent)); 1631 appendFormat_String(msg, "%zu ${bytes}\n", size_Block(&d->sourceContent));
1634 } 1632 }
1635 } 1633 }
1636 appendFormat_String(msg, 1634 appendFormat_String(
1637 "\n%sCertificate Status:\n" 1635 msg,
1638 "%s%s %s by CA\n" 1636 "\n%s${pageinfo.cert.status}\n"
1639 "%s%s Domain name %s%s\n" 1637 "%s%s %s\n"
1640 "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n" 1638 "%s%s %s%s\n"
1641 "%s%s %s", 1639 "%s%s %s (%04d-%02d-%02d %02d:%02d:%02d)\n"
1642 uiHeading_ColorEscape, 1640 "%s%s %s",
1643 d->certFlags & authorityVerified_GmCertFlag ? 1641 uiHeading_ColorEscape,
1644 checked : uiTextAction_ColorEscape "\u2610", 1642 d->certFlags & authorityVerified_GmCertFlag ? checked
1645 uiText_ColorEscape, 1643 : uiTextAction_ColorEscape "\u2610",
1646 d->certFlags & authorityVerified_GmCertFlag ? "Verified" : "Not verified", 1644 uiText_ColorEscape,
1647 d->certFlags & domainVerified_GmCertFlag ? checked : unchecked, 1645 d->certFlags & authorityVerified_GmCertFlag ? "${pageinfo.cert.ca.verified}"
1648 uiText_ColorEscape, 1646 : "${pageinfo.cert.ca.unverified}",
1649 d->certFlags & domainVerified_GmCertFlag ? "matches" : "mismatch", 1647 d->certFlags & domainVerified_GmCertFlag ? checked : unchecked,
1650 ~d->certFlags & domainVerified_GmCertFlag 1648 uiText_ColorEscape,
1651 ? format_CStr(" (%s)", cstr_String(d->certSubject)) 1649 d->certFlags & domainVerified_GmCertFlag ? "${pageinfo.domain.match}"
1652 : "", 1650 : "${pageinfo.domain.mismatch}",
1653 d->certFlags & timeVerified_GmCertFlag ? checked : unchecked, 1651 ~d->certFlags & domainVerified_GmCertFlag
1654 uiText_ColorEscape, 1652 ? format_CStr(" (%s)", cstr_String(d->certSubject))
1655 d->certFlags & timeVerified_GmCertFlag ? "Not expired" : "Expired", 1653 : "",
1656 d->certExpiry.year, 1654 d->certFlags & timeVerified_GmCertFlag ? checked : unchecked,
1657 d->certExpiry.month, 1655 uiText_ColorEscape,
1658 d->certExpiry.day, 1656 d->certFlags & timeVerified_GmCertFlag ? "${pageinfo.cert.notexpired}"
1659 d->certExpiry.hour, 1657 : "${pageinfo.cert.expired}",
1660 d->certExpiry.minute, 1658 d->certExpiry.year,
1661 d->certExpiry.second, 1659 d->certExpiry.month,
1662 d->certFlags & trusted_GmCertFlag ? checked : unchecked, 1660 d->certExpiry.day,
1663 uiText_ColorEscape, 1661 d->certExpiry.hour,
1664 d->certFlags & trusted_GmCertFlag ? "Trusted" : "Not trusted"); 1662 d->certExpiry.minute,
1663 d->certExpiry.second,
1664 d->certFlags & trusted_GmCertFlag ? checked : unchecked,
1665 uiText_ColorEscape,
1666 d->certFlags & trusted_GmCertFlag ? "${pageinfo.cert.trusted}"
1667 : "${pageinfo.cert.untrusted}");
1665 setFocus_Widget(NULL); 1668 setFocus_Widget(NULL);
1666 iArray *items = new_Array(sizeof(iMenuItem)); 1669 iArray *items = new_Array(sizeof(iMenuItem));
1667 if (canTrust) { 1670 if (canTrust) {
1668 pushBack_Array( 1671 pushBack_Array(
1669 items, &(iMenuItem){ uiTextCaution_ColorEscape "Trust", 0, 0, "server.trustcert" }); 1672 items, &(iMenuItem){ uiTextCaution_ColorEscape "${dlg.cert.trust}", 0, 0, "server.trustcert" });
1670 } 1673 }
1671 if (haveFingerprint) { 1674 if (haveFingerprint) {
1672 pushBack_Array(items, &(iMenuItem){ "Copy Fingerprint", 0, 0, "server.copycert" }); 1675 pushBack_Array(items, &(iMenuItem){ "${dlg.cert.fingerprint}", 0, 0, "server.copycert" });
1673 } 1676 }
1674 if (!isEmpty_Array(items)) { 1677 if (!isEmpty_Array(items)) {
1675 pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 }); 1678 pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 });
1676 } 1679 }
1677 pushBack_Array(items, &(iMenuItem){ "Dismiss", 0, 0, "message.ok" }); 1680 pushBack_Array(items, &(iMenuItem){ "${dismiss}", 0, 0, "message.ok" });
1678 iWidget *dlg = makeQuestion_Widget(uiHeading_ColorEscape "PAGE INFORMATION", 1681 iWidget *dlg = makeQuestion_Widget(uiHeading_ColorEscape "${heading.pageinfo}",
1679 cstr_String(msg), 1682 cstr_String(msg),
1680 data_Array(items), 1683 data_Array(items),
1681 size_Array(items)); 1684 size_Array(items));
@@ -1871,8 +1874,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1871 } 1874 }
1872 else if (equal_Command(cmd, "document.save") && document_App() == d) { 1875 else if (equal_Command(cmd, "document.save") && document_App() == d) {
1873 if (d->request) { 1876 if (d->request) {
1874 makeMessage_Widget(uiTextCaution_ColorEscape "PAGE INCOMPLETE", 1877 makeMessage_Widget(uiTextCaution_ColorEscape "${heading.save.incomplete}",
1875 "The page contents are still being downloaded."); 1878 "${dlg.save.incomplete}");
1876 } 1879 }
1877 else if (!isEmpty_Block(&d->sourceContent)) { 1880 else if (!isEmpty_Block(&d->sourceContent)) {
1878 saveToDownloads_(d->mod.url, &d->sourceMime, &d->sourceContent); 1881 saveToDownloads_(d->mod.url, &d->sourceMime, &d->sourceContent);
@@ -2077,14 +2080,20 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2077 } 2080 }
2078 if (!isEmpty_PtrArray(links)) { 2081 if (!isEmpty_PtrArray(links)) {
2079 if (argLabel_Command(cmd, "confirm")) { 2082 if (argLabel_Command(cmd, "confirm")) {
2080 const char *plural = size_PtrArray(links) != 1 ? "s" : ""; 2083 //const char *plural = size_PtrArray(links) != 1 ? "s" : "";
2084 const iBool isPlural = size_PtrArray(links) != 1;
2081 makeQuestion_Widget( 2085 makeQuestion_Widget(
2082 uiHeading_ColorEscape "IMPORT BOOKMARKS", 2086 uiHeading_ColorEscape "${heading.import.bookmarks}",
2083 format_CStr("Found %d new link%s on the page.", size_PtrArray(links), plural), 2087 format_CStr(cstr_Lang(isPlural ? "dlg.import.found.many" : "dlg.import.found"),
2084 (iMenuItem[]){ { "Cancel", 0, 0, NULL }, 2088 size_PtrArray(links)),
2085 { format_CStr(uiTextAction_ColorEscape "Add %d Bookmark%s", 2089 (iMenuItem[]){
2086 size_PtrArray(links), 2090 { "${cancel}", 0, 0, NULL },
2087 plural), 0, 0, "bookmark.links" } }, 2091 { format_CStr(isPlural ? "dlg.import.add.many" : "dlg.import.add",
2092 uiTextAction_ColorEscape,
2093 size_PtrArray(links)),
2094 0,
2095 0,
2096 "bookmark.links" } },
2088 2); 2097 2);
2089 } 2098 }
2090 else { 2099 else {
@@ -2100,8 +2109,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2100 } 2109 }
2101 } 2110 }
2102 else { 2111 else {
2103 makeMessage_Widget(uiHeading_ColorEscape "IMPORT BOOKMARKS", 2112 makeMessage_Widget(uiHeading_ColorEscape "${heading.import.bookmarks}",
2104 "All links on this page are already bookmarked."); 2113 "${dlg.import.notnew}");
2105 } 2114 }
2106 return iTrue; 2115 return iTrue;
2107 } 2116 }
@@ -2117,9 +2126,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2117 } 2126 }
2118 } 2127 }
2119 else if (equal_Command(cmd, "document.autoreload.menu") && document_App() == d) { 2128 else if (equal_Command(cmd, "document.autoreload.menu") && document_App() == d) {
2120 iWidget *dlg = makeQuestion_Widget(uiTextAction_ColorEscape "AUTO-RELOAD", 2129 iWidget *dlg = makeQuestion_Widget(uiTextAction_ColorEscape "${heading.autoreload}",
2121 "Select the auto-reload interval for this tab.", 2130 "${dlg.autoreload}",
2122 (iMenuItem[]){ { "Cancel", 0, 0, NULL } }, 2131 (iMenuItem[]){ { "${cancel}", 0, 0, NULL } },
2123 1); 2132 1);
2124 for (int i = 0; i < max_ReloadInterval; ++i) { 2133 for (int i = 0; i < max_ReloadInterval; ++i) {
2125 insertChildAfterFlags_Widget( 2134 insertChildAfterFlags_Widget(
@@ -2454,7 +2463,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2454 init_Array(&items, sizeof(iMenuItem)); 2463 init_Array(&items, sizeof(iMenuItem));
2455 if (d->contextLink) { 2464 if (d->contextLink) {
2456 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId); 2465 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId);
2457 const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); 2466// const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId);
2458 const iRangecc scheme = urlScheme_String(linkUrl); 2467 const iRangecc scheme = urlScheme_String(linkUrl);
2459 const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); 2468 const iBool isGemini = equalCase_Rangecc(scheme, "gemini");
2460 iBool isNative = iFalse; 2469 iBool isNative = iFalse;
@@ -2466,11 +2475,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2466 pushBackN_Array( 2475 pushBackN_Array(
2467 &items, 2476 &items,
2468 (iMenuItem[]){ 2477 (iMenuItem[]){
2469 { openTab_Icon " Open Link in New Tab", 2478 { openTab_Icon " ${link.newtab}",
2470 0, 2479 0,
2471 0, 2480 0,
2472 format_CStr("!open newtab:1 url:%s", cstr_String(linkUrl)) }, 2481 format_CStr("!open newtab:1 url:%s", cstr_String(linkUrl)) },
2473 { openTabBg_Icon " Open Link in Background Tab", 2482 { openTabBg_Icon " ${link.newtab.background}",
2474 0, 2483 0,
2475 0, 2484 0,
2476 format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) } }, 2485 format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) } },
@@ -2479,7 +2488,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2479 else if (!willUseProxy_App(scheme)) { 2488 else if (!willUseProxy_App(scheme)) {
2480 pushBack_Array( 2489 pushBack_Array(
2481 &items, 2490 &items,
2482 &(iMenuItem){ openExt_Icon " Open Link in Default Browser", 2491 &(iMenuItem){ openExt_Icon " ${link.browser}",
2483 0, 2492 0,
2484 0, 2493 0,
2485 format_CStr("!open default:1 url:%s", cstr_String(linkUrl)) }); 2494 format_CStr("!open default:1 url:%s", cstr_String(linkUrl)) });
@@ -2489,7 +2498,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2489 &items, 2498 &items,
2490 (iMenuItem[]){ 2499 (iMenuItem[]){
2491 { "---", 0, 0, NULL }, 2500 { "---", 0, 0, NULL },
2492 { isGemini ? "Open without Proxy" : openExt_Icon " Open Link in Default Browser", 2501 { isGemini ? "${link.noproxy}" : openExt_Icon " ${link.browser}",
2493 0, 2502 0,
2494 0, 2503 0,
2495 format_CStr("!open noproxy:1 url:%s", cstr_String(linkUrl)) } }, 2504 format_CStr("!open noproxy:1 url:%s", cstr_String(linkUrl)) } },
@@ -2500,8 +2509,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2500 urlEncodeSpaces_String(linkLabel); 2509 urlEncodeSpaces_String(linkLabel);
2501 pushBackN_Array(&items, 2510 pushBackN_Array(&items,
2502 (iMenuItem[]){ { "---", 0, 0, NULL }, 2511 (iMenuItem[]){ { "---", 0, 0, NULL },
2503 { "Copy Link", 0, 0, "document.copylink" }, 2512 { "${link.copy}", 0, 0, "document.copylink" },
2504 { pin_Icon " Bookmark Link...", 2513 { pin_Icon " ${link.bookmark}",
2505 0, 2514 0,
2506 0, 2515 0,
2507 format_CStr("!bookmark.add title:%s url:%s", 2516 format_CStr("!bookmark.add title:%s url:%s",
@@ -2512,7 +2521,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2512 if (isNative && d->contextLink->mediaType != download_GmRunMediaType) { 2521 if (isNative && d->contextLink->mediaType != download_GmRunMediaType) {
2513 pushBackN_Array(&items, (iMenuItem[]){ 2522 pushBackN_Array(&items, (iMenuItem[]){
2514 { "---", 0, 0, NULL }, 2523 { "---", 0, 0, NULL },
2515 { download_Icon " Download Linked File", 0, 0, "document.downloadlink" }, 2524 { download_Icon " ${link.download}", 0, 0, "document.downloadlink" },
2516 }, 2); 2525 }, 2);
2517 } 2526 }
2518 iMediaRequest *mediaReq; 2527 iMediaRequest *mediaReq;
@@ -2532,39 +2541,39 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2532 if (!isEmpty_Range(&d->selectMark)) { 2541 if (!isEmpty_Range(&d->selectMark)) {
2533 pushBackN_Array( 2542 pushBackN_Array(
2534 &items, 2543 &items,
2535 (iMenuItem[]){ { "Copy", 0, 0, "copy" }, { "---", 0, 0, NULL } }, 2544 (iMenuItem[]){ { "${menu.copy}", 0, 0, "copy" }, { "---", 0, 0, NULL } },
2536 2); 2545 2);
2537 } 2546 }
2538 if (deviceType_App() == desktop_AppDeviceType) { 2547 if (deviceType_App() == desktop_AppDeviceType) {
2539 pushBackN_Array( 2548 pushBackN_Array(
2540 &items, 2549 &items,
2541 (iMenuItem[]){ 2550 (iMenuItem[]){
2542 { "Go Back", navigateBack_KeyShortcut, "navigate.back" }, 2551 { "${menu.back}", navigateBack_KeyShortcut, "navigate.back" },
2543 { "Go Forward", navigateForward_KeyShortcut, "navigate.forward" } }, 2552 { "${menu.forward}", navigateForward_KeyShortcut, "navigate.forward" } },
2544 2); 2553 2);
2545 } 2554 }
2546 pushBackN_Array( 2555 pushBackN_Array(
2547 &items, 2556 &items,
2548 (iMenuItem[]){ 2557 (iMenuItem[]){
2549 { upArrow_Icon " Go to Parent", navigateParent_KeyShortcut, "navigate.parent" }, 2558 { upArrow_Icon " ${menu.parent}", navigateParent_KeyShortcut, "navigate.parent" },
2550 { upArrowBar_Icon " Go to Root", navigateRoot_KeyShortcut, "navigate.root" }, 2559 { upArrowBar_Icon " ${menu.root}", navigateRoot_KeyShortcut, "navigate.root" },
2551 { "---", 0, 0, NULL }, 2560 { "---", 0, 0, NULL },
2552 { reload_Icon " Reload Page", reload_KeyShortcut, "navigate.reload" }, 2561 { reload_Icon " ${menu.reload}", reload_KeyShortcut, "navigate.reload" },
2553 { timer_Icon " Set Auto-Reload...", 0, 0, "document.autoreload.menu" }, 2562 { timer_Icon " ${menu.autoreload}", 0, 0, "document.autoreload.menu" },
2554 { "---", 0, 0, NULL }, 2563 { "---", 0, 0, NULL },
2555 { pin_Icon " Bookmark Page...", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, 2564 { pin_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" },
2556 { star_Icon " Subscribe to Page...", subscribeToPage_KeyModifier, "feeds.subscribe" }, 2565 { star_Icon " ${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" },
2557 { "---", 0, 0, NULL }, 2566 { "---", 0, 0, NULL },
2558 { book_Icon " Import Links as Bookmarks...", 0, 0, "bookmark.links confirm:1" }, 2567 { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" },
2559 { globe_Icon " Translate...", 0, 0, "document.translate" }, 2568 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
2560 { "---", 0, 0, NULL }, 2569 { "---", 0, 0, NULL },
2561 { "Copy Page URL", 0, 0, "document.copylink" } }, 2570 { "${menu.page.copyurl}", 0, 0, "document.copylink" } },
2562 12); 2571 12);
2563 if (isEmpty_Range(&d->selectMark)) { 2572 if (isEmpty_Range(&d->selectMark)) {
2564 pushBackN_Array( 2573 pushBackN_Array(
2565 &items, 2574 &items,
2566 (iMenuItem[]){ 2575 (iMenuItem[]){
2567 { "Copy Page Source", 'c', KMOD_PRIMARY, "copy" }, 2576 { "${menu.page.copysource}", 'c', KMOD_PRIMARY, "copy" },
2568 { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" } }, 2577 { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" } },
2569 2); 2578 2);
2570 } 2579 }
@@ -2692,14 +2701,14 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2692 const iString *url = absoluteUrl_String( 2701 const iString *url = absoluteUrl_String(
2693 d->mod.url, linkUrl_GmDocument(d->doc, linkId)); 2702 d->mod.url, linkUrl_GmDocument(d->doc, linkId));
2694 makeQuestion_Widget( 2703 makeQuestion_Widget(
2695 uiTextCaution_ColorEscape "OPEN LINK", 2704 uiTextCaution_ColorEscape "${heading.openlink}",
2696 format_CStr( 2705 format_CStr(
2697 "Open this link in the default browser?\n" uiTextAction_ColorEscape 2706 cstr_Lang("dlg.openlink.confirm"),
2698 "%s", 2707 uiTextAction_ColorEscape,
2699 cstr_String(url)), 2708 cstr_String(url)),
2700 (iMenuItem[]){ 2709 (iMenuItem[]){
2701 { "Cancel", 0, 0, NULL }, 2710 { "${cancel}", 0, 0, NULL },
2702 { uiTextCaution_ColorEscape "Open Link", 2711 { uiTextCaution_ColorEscape "${dlg.openlink}",
2703 0, 0, format_CStr("!open default:1 url:%s", cstr_String(url)) } }, 2712 0, 0, format_CStr("!open default:1 url:%s", cstr_String(url)) } },
2704 2); 2713 2);
2705 } 2714 }
@@ -2823,7 +2832,7 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
2823 const int domainHeight = lineHeight_Text(banner_FontId) * 2; 2832 const int domainHeight = lineHeight_Text(banner_FontId) * 2;
2824 iRect rect = { add_I2(visPos, init_I2(0, domainHeight)), 2833 iRect rect = { add_I2(visPos, init_I2(0, domainHeight)),
2825 addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) }; 2834 addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) };
2826 format_String(&str, "UNTRUSTED CERTIFICATE"); 2835 format_String(&str, "${heading.certwarn}");
2827 const int certFlags = d->widget->certFlags; 2836 const int certFlags = d->widget->certFlags;
2828 if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) { 2837 if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) {
2829 iUrl parts; 2838 iUrl parts;
@@ -2834,32 +2843,30 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
2834 iTime now; 2843 iTime now;
2835 initCurrent_Time(&now); 2844 initCurrent_Time(&now);
2836 const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24; 2845 const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24;
2846 appendCStr_String(&str, "\n");
2837 if (days <= 30) { 2847 if (days <= 30) {
2838 appendFormat_String(&str, 2848 appendFormat_String(&str,
2839 "\nThe received certificate may have been recently renewed " 2849 cstr_Lang("dlg.certwarn.mayberenewed"),
2840 "\u2014 it is for the correct domain and has not expired. "
2841 "The currently trusted certificate will expire on %s, "
2842 "in %d days.",
2843 cstrCollect_String(format_Date(&exp, "%Y-%m-%d")), 2850 cstrCollect_String(format_Date(&exp, "%Y-%m-%d")),
2844 days); 2851 days);
2845 } 2852 }
2846 else { 2853 else {
2847 appendFormat_String(&str, "\nThe received certificate is valid but different than " 2854 appendCStr_String(&str, cstr_Lang("dlg.certwarn.different"));
2848 "the one we trust.");
2849 } 2855 }
2850 } 2856 }
2851 else if (certFlags & domainVerified_GmCertFlag) { 2857 else if (certFlags & domainVerified_GmCertFlag) {
2852 appendFormat_String(&str, "\nThe received certificate has expired on %s.", 2858 appendCStr_String(&str, "\n");
2859 appendFormat_String(&str, cstr_Lang("dlg.certwarn.expired"),
2853 cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d"))); 2860 cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d")));
2854 } 2861 }
2855 else if (certFlags & timeVerified_GmCertFlag) { 2862 else if (certFlags & timeVerified_GmCertFlag) {
2856 appendFormat_String(&str, "\nThe received certificate is for the wrong domain (%s). " 2863 appendCStr_String(&str, "\n");
2857 "This may be a server configuration problem.", 2864 appendFormat_String(&str, cstr_Lang("dlg.certwarn.domain"),
2858 cstr_String(d->widget->certSubject)); 2865 cstr_String(d->widget->certSubject));
2859 } 2866 }
2860 else { 2867 else {
2861 appendFormat_String(&str, "\nThe received certificate is expired AND for the " 2868 appendCStr_String(&str, "\n");
2862 "wrong domain."); 2869 appendCStr_String(&str, cstr_Lang("dlg.certwarn.domain.expired"));
2863 } 2870 }
2864 const iInt2 dims = advanceWrapRange_Text( 2871 const iInt2 dims = advanceWrapRange_Text(
2865 uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str)); 2872 uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str));
@@ -2880,6 +2887,7 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
2880 bpos = topLeft_Rect(rect); 2887 bpos = topLeft_Rect(rect);
2881 draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0"); 2888 draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0");
2882 adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI); 2889 adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI);
2890 translate_Lang(&str);
2883 drawWrapRange_Text(uiContent_FontId, 2891 drawWrapRange_Text(uiContent_FontId,
2884 addY_I2(topLeft_Rect(rect), yOff), 2892 addY_I2(topLeft_Rect(rect), yOff),
2885 width_Rect(rect), 2893 width_Rect(rect),
@@ -2990,8 +2998,9 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
2990 iGmMediaInfo info; 2998 iGmMediaInfo info;
2991 imageInfo_Media(constMedia_GmDocument(doc), imageId, &info); 2999 imageInfo_Media(constMedia_GmDocument(doc), imageId, &info);
2992 const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId); 3000 const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId);
2993 format_String(&text, "%s \u2014 %d x %d \u2014 %.1fMB", 3001 format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s",
2994 info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f); 3002 info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f,
3003 cstr_Lang("mb"));
2995 } 3004 }
2996 else if (audioId) { 3005 else if (audioId) {
2997 iGmMediaInfo info; 3006 iGmMediaInfo info;
@@ -3047,24 +3056,26 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
3047 if (run->flags & endOfLine_GmRunFlag && 3056 if (run->flags & endOfLine_GmRunFlag &&
3048 (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag) || 3057 (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag) ||
3049 showHost)) { 3058 showHost)) {
3050 format_String(&str, 3059 format_String(
3051 " \u2014%s%s%s\r%c%s", 3060 &str,
3052 showHost ? " " : "", 3061 " \u2014%s%s%s\r%c%s",
3053 showHost ? (flags & mailto_GmLinkFlag 3062 showHost ? " " : "",
3054 ? cstr_String(url) 3063 showHost
3055 : ~flags & gemini_GmLinkFlag 3064 ? (flags & mailto_GmLinkFlag ? cstr_String(url)
3056 ? format_CStr("%s://%s", 3065 : ~flags & gemini_GmLinkFlag ? format_CStr("%s://%s",
3057 cstr_Rangecc(parts.scheme), 3066 cstr_Rangecc(parts.scheme),
3058 cstr_Rangecc(parts.host)) 3067 cstr_Rangecc(parts.host))
3059 : cstr_Rangecc(parts.host)) 3068 : cstr_Rangecc(parts.host))
3060 : "", 3069 : "",
3061 showHost && (showImage || showAudio) ? " \u2014" : "", 3070 showHost && (showImage || showAudio) ? " \u2014" : "",
3062 showImage || showAudio 3071 showImage || showAudio
3063 ? asciiBase_ColorEscape + fg 3072 ? asciiBase_ColorEscape + fg
3064 : (asciiBase_ColorEscape + 3073 : (asciiBase_ColorEscape +
3065 linkColor_GmDocument(doc, run->linkId, domain_GmLinkPart)), 3074 linkColor_GmDocument(doc, run->linkId, domain_GmLinkPart)),
3066 showImage ? " View Image \U0001f5bb" 3075 showImage || showAudio
3067 : showAudio ? " Play Audio \U0001f3b5" : ""); 3076 ? format_CStr(showImage ? " %s \U0001f5bb" : " %s \U0001f3b5",
3077 cstr_Lang(showImage ? "link.hint.image" : "link.hint.audio"))
3078 : "");
3068 } 3079 }
3069 if (run->flags & endOfLine_GmRunFlag && flags & visited_GmLinkFlag) { 3080 if (run->flags & endOfLine_GmRunFlag && flags & visited_GmLinkFlag) {
3070 iDate date; 3081 iDate date;
@@ -3398,7 +3409,7 @@ const iString *bookmarkTitle_DocumentWidget(const iDocumentWidget *d) {
3398 } 3409 }
3399 } 3410 }
3400 if (isEmpty_StringArray(title)) { 3411 if (isEmpty_StringArray(title)) {
3401 pushBackCStr_StringArray(title, "Blank Page"); 3412 pushBackCStr_StringArray(title, cstr_Lang("bookmark.title.blank"));
3402 } 3413 }
3403 return collect_String(joinCStr_StringArray(title, " \u2014 ")); 3414 return collect_String(joinCStr_StringArray(title, " \u2014 "));
3404} 3415}
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 0becd419..2a1eb06a 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -429,6 +429,8 @@ void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) {
429 429
430void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) { 430void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) {
431 setCStr_String(&d->label, text); 431 setCStr_String(&d->label, text);
432 set_String(&d->srcLabel, &d->label);
433 replaceVariables_LabelWidget_(d);
432 updateSize_LabelWidget(d); 434 updateSize_LabelWidget(d);
433} 435}
434 436
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 2cc1d899..7a704de7 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -168,11 +168,14 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
168 on = entryDate; 168 on = entryDate;
169 iSidebarItem *sep = new_SidebarItem(); 169 iSidebarItem *sep = new_SidebarItem();
170 sep->listItem.isSeparator = iTrue; 170 sep->listItem.isSeparator = iTrue;
171 iString *text = format_Date(&on, on.year == today.year ? "%b. %d" : "%b. %d, %Y"); 171 iString *text = format_Date(&on,
172 cstr_Lang(on.year == today.year
173 ? "sidebar.date.thisyear"
174 : "sidebar.date.otheryear"));
172 if (today.year == on.year && 175 if (today.year == on.year &&
173 today.month == on.month && 176 today.month == on.month &&
174 today.day == on.day) { 177 today.day == on.day) {
175 appendCStr_String(text, " \u2014 Today"); 178 appendCStr_String(text, " \u2014 ${feeds.today}");
176 } 179 }
177 set_String(&sep->meta, text); 180 set_String(&sep->meta, text);
178 delete_String(text); 181 delete_String(text);
@@ -203,16 +206,16 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
203 } 206 }
204 d->menu = makeMenu_Widget( 207 d->menu = makeMenu_Widget(
205 as_Widget(d), 208 as_Widget(d),
206 (iMenuItem[]){ { openTab_Icon " Open Entry in New Tab", 0, 0, "feed.entry.opentab" }, 209 (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" },
207 { circle_Icon " Mark as Read", 0, 0, "feed.entry.toggleread" }, 210 { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" },
208 { pin_Icon " Add Bookmark...", 0, 0, "feed.entry.bookmark" }, 211 { pin_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" },
209 { "---", 0, 0, NULL }, 212 { "---", 0, 0, NULL },
210 { page_Icon " Open Feed Page", 0, 0, "feed.entry.openfeed" }, 213 { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" },
211 { edit_Icon " Edit Feed...", 0, 0, "feed.entry.edit" }, 214 { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" },
212 { whiteStar_Icon " " uiTextCaution_ColorEscape "Unsubscribe...", 0, 0, "feed.entry.unsubscribe" }, 215 { whiteStar_Icon " " uiTextCaution_ColorEscape "${feeds.unsubscribe}", 0, 0, "feed.entry.unsubscribe" },
213 { "---", 0, 0, NULL }, 216 { "---", 0, 0, NULL },
214 { check_Icon " Mark All as Read", SDLK_a, KMOD_SHIFT, "feeds.markallread" }, 217 { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" },
215 { reload_Icon " Refresh Feeds", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } }, 218 { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } },
216 10); 219 10);
217 break; 220 break;
218 } 221 }
@@ -262,20 +265,20 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
262 } 265 }
263 d->menu = makeMenu_Widget( 266 d->menu = makeMenu_Widget(
264 as_Widget(d), 267 as_Widget(d),
265 (iMenuItem[]){ { openTab_Icon " Open in New Tab", 0, 0, "bookmark.open newtab:1" }, 268 (iMenuItem[]){ { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" },
266 { openTabBg_Icon " Open in Background Tab", 0, 0, "bookmark.open newtab:2" }, 269 { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" },
267 { "---", 0, 0, NULL }, 270 { "---", 0, 0, NULL },
268 { edit_Icon " Edit...", 0, 0, "bookmark.edit" }, 271 { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" },
269 { copy_Icon " Duplicate...", 0, 0, "bookmark.dup" }, 272 { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" },
270 { "Copy URL", 0, 0, "bookmark.copy" }, 273 { "${menu.copyurl}", 0, 0, "bookmark.copy" },
271 { "---", 0, 0, NULL }, 274 { "---", 0, 0, NULL },
272 { "?", 0, 0, "bookmark.tag tag:subscribed" }, 275 { "?", 0, 0, "bookmark.tag tag:subscribed" },
273 { "?", 0, 0, "bookmark.tag tag:homepage" }, 276 { "?", 0, 0, "bookmark.tag tag:homepage" },
274 { "?", 0, 0, "bookmark.tag tag:remotesource" }, 277 { "?", 0, 0, "bookmark.tag tag:remotesource" },
275 { "---", 0, 0, NULL }, 278 { "---", 0, 0, NULL },
276 { delete_Icon " " uiTextCaution_ColorEscape "Delete Bookmark", 0, 0, "bookmark.delete" }, 279 { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" },
277 { "---", 0, 0, NULL }, 280 { "---", 0, 0, NULL },
278 { reload_Icon " Refresh Remote Sources", 0, 0, "bookmarks.reload.remote" } }, 281 { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } },
279 14); 282 14);
280 break; 283 break;
281 } 284 }
@@ -301,8 +304,10 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
301 /* Date separator. */ 304 /* Date separator. */
302 iSidebarItem *sep = new_SidebarItem(); 305 iSidebarItem *sep = new_SidebarItem();
303 sep->listItem.isSeparator = iTrue; 306 sep->listItem.isSeparator = iTrue;
304 const iString *text = collect_String(format_Date( 307 const iString *text = collect_String(
305 &date, date.year != thisYear ? "%b. %d, %Y" : "%b. %d")); 308 format_Date(&date,
309 cstr_Lang(date.year != thisYear ? "sidebar.date.otheryear"
310 : "sidebar.date.thisyear")));
306 set_String(&sep->meta, text); 311 set_String(&sep->meta, text);
307 const int yOffset = itemHeight_ListWidget(d->list) * 2 / 3; 312 const int yOffset = itemHeight_ListWidget(d->list) * 2 / 3;
308 sep->id = yOffset; 313 sep->id = yOffset;
@@ -322,12 +327,12 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
322 d->menu = makeMenu_Widget( 327 d->menu = makeMenu_Widget(
323 as_Widget(d), 328 as_Widget(d),
324 (iMenuItem[]){ 329 (iMenuItem[]){
325 { "Copy URL", 0, 0, "history.copy" }, 330 { "${menu.copyurl}", 0, 0, "history.copy" },
326 { pin_Icon " Add Bookmark...", 0, 0, "history.addbookmark" }, 331 { pin_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" },
327 { "---", 0, 0, NULL }, 332 { "---", 0, 0, NULL },
328 { close_Icon " Forget URL", 0, 0, "history.delete" }, 333 { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" },
329 { "---", 0, 0, NULL }, 334 { "---", 0, 0, NULL },
330 { delete_Icon " " uiTextCaution_ColorEscape "Clear History...", 0, 0, "history.clear confirm:1" }, 335 { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" },
331 }, 6); 336 }, 6);
332 break; 337 break;
333 } 338 }
@@ -342,17 +347,16 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
342 iDate until; 347 iDate until;
343 validUntil_TlsCertificate(ident->cert, &until); 348 validUntil_TlsCertificate(ident->cert, &until);
344 const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); 349 const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl);
345 format_String( 350 format_String(&item->meta,
346 &item->meta, 351 "%s",
347 "%s", 352 isActive ? cstr_Lang("ident.using")
348 isActive ? "Using on this page" 353 : isUsed_GmIdentity(ident)
349 : isUsed_GmIdentity(ident) 354 ? format_CStr(cstr_Lang("ident.usedonurls"), size_StringSet(ident->useUrls))
350 ? format_CStr("Used on %zu URLs", size_StringSet(ident->useUrls)) 355 : cstr_Lang("ident.notused"));
351 : "Not used");
352 const char *expiry = 356 const char *expiry =
353 ident->flags & temporary_GmIdentityFlag 357 ident->flags & temporary_GmIdentityFlag
354 ? "Temporary" 358 ? cstr_Lang("ident.temporary")
355 : cstrCollect_String(format_Date(&until, "Expires %b %d, %Y")); 359 : cstrCollect_String(format_Date(&until, cstr_Lang("ident.expiry")));
356 if (isEmpty_String(&ident->notes)) { 360 if (isEmpty_String(&ident->notes)) {
357 appendFormat_String(&item->meta, "\n%s", expiry); 361 appendFormat_String(&item->meta, "\n%s", expiry);
358 } 362 }
@@ -368,17 +372,17 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
368 iRelease(item); 372 iRelease(item);
369 } 373 }
370 const iMenuItem menuItems[] = { 374 const iMenuItem menuItems[] = {
371 { person_Icon " Use on This Page", 0, 0, "ident.use arg:1" }, 375 { person_Icon " ${ident.use}", 0, 0, "ident.use arg:1" },
372 { close_Icon " Stop Using on This Page", 0, 0, "ident.use arg:0" }, 376 { close_Icon " ${ident.stopuse}", 0, 0, "ident.use arg:0" },
373 { close_Icon " Stop Using Everywhere", 0, 0, "ident.use arg:0 clear:1" }, 377 { close_Icon " ${ident.stopuse.all}", 0, 0, "ident.use arg:0 clear:1" },
374 { "Show Usage", 0, 0, "ident.showuse" }, 378 { "${ident.showuse}", 0, 0, "ident.showuse" },
375 { "---", 0, 0, NULL }, 379 { "---", 0, 0, NULL },
376 { edit_Icon " Edit Notes...", 0, 0, "ident.edit" }, 380 { edit_Icon " ${menu.edit.notes}", 0, 0, "ident.edit" },
377 { "Copy Fingerprint", 0, 0, "ident.fingerprint" }, 381 { "${ident.fingerprint}", 0, 0, "ident.fingerprint" },
378// { "Pick Icon...", 0, 0, "ident.pickicon" }, 382// { "Pick Icon...", 0, 0, "ident.pickicon" },
379 { "---", 0, 0, NULL }, 383 { "---", 0, 0, NULL },
380 //{ "Reveal Files", 0, 0, "ident.reveal" }, 384 //{ "Reveal Files", 0, 0, "ident.reveal" },
381 { delete_Icon " " uiTextCaution_ColorEscape "Delete Identity...", 0, 0, "ident.delete confirm:1" }, 385 { delete_Icon " " uiTextCaution_ColorEscape "${ident.delete}", 0, 0, "ident.delete confirm:1" },
382 }; 386 };
383 d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); 387 d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems));
384 break; 388 break;
@@ -394,7 +398,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
394 iWidget *div = makeVDiv_Widget(); 398 iWidget *div = makeVDiv_Widget();
395 setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI); 399 setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI);
396 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ 400 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */
397 addChild_Widget(div, iClob(new_LabelWidget("Refresh Feeds", "feeds.refresh"))); 401 addChild_Widget(div, iClob(new_LabelWidget("${menu.feeds.refresh}", "feeds.refresh")));
398 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ 402 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */
399 addChild_Widget(d->blank, iClob(div)); 403 addChild_Widget(d->blank, iClob(div));
400 } 404 }
@@ -402,20 +406,22 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
402 iWidget *div = makeVDiv_Widget(); 406 iWidget *div = makeVDiv_Widget();
403 setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI); 407 setPadding_Widget(div, 3 * gap_UI, 0, 3 * gap_UI, 2 * gap_UI);
404 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ 408 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */
405 iLabelWidget *msg = new_LabelWidget("No Identities", NULL); 409 iLabelWidget *msg = new_LabelWidget("${sidebar.empty.idents}", NULL);
406 setFont_LabelWidget(msg, uiLabelLarge_FontId); 410 setFont_LabelWidget(msg, uiLabelLarge_FontId);
407 addChildFlags_Widget(div, iClob(msg), frameless_WidgetFlag); 411 addChildFlags_Widget(div, iClob(msg), frameless_WidgetFlag);
408 addChild_Widget(div, iClob(makePadding_Widget(3 * gap_UI))); 412 addChild_Widget(div, iClob(makePadding_Widget(3 * gap_UI)));
409 addChild_Widget(div, iClob(new_LabelWidget("New Identity...", "ident.new"))); 413 addChild_Widget(div, iClob(new_LabelWidget("${menu.identity.new}", "ident.new")));
410 addChild_Widget(div, iClob(makePadding_Widget(gap_UI))); 414 addChild_Widget(div, iClob(makePadding_Widget(gap_UI)));
411 addChild_Widget(div, iClob(new_LabelWidget("Import...", "ident.import"))); 415 addChild_Widget(div, iClob(new_LabelWidget("${menu.identity.import}", "ident.import")));
412 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ 416 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */
413 setBackgroundColor_Widget(addChildFlags_Widget( 417 setBackgroundColor_Widget(
414 div, 418 addChildFlags_Widget(
415 iClob(new_LabelWidget("See " uiTextStrong_ColorEscape "Help" restore_ColorEscape 419 div,
416 " for more information about TLS client certificates.", 420 iClob(new_LabelWidget(format_CStr(cstr_Lang("ident.gotohelp"),
417 "!open newtab:1 gotoheading:1.6 url:about:help")), 421 uiTextStrong_ColorEscape,
418 frameless_WidgetFlag | fixedHeight_WidgetFlag | wrapText_WidgetFlag), 422 restore_ColorEscape),
423 "!open newtab:1 gotoheading:1.6 url:about:help")),
424 frameless_WidgetFlag | fixedHeight_WidgetFlag | wrapText_WidgetFlag),
419 uiBackgroundSidebar_ColorId); 425 uiBackgroundSidebar_ColorId);
420 addChild_Widget(d->blank, iClob(div)); 426 addChild_Widget(d->blank, iClob(div));
421 } 427 }
@@ -459,11 +465,11 @@ int width_SidebarWidget(const iSidebarWidget *d) {
459} 465}
460 466
461static const char *normalModeLabels_[max_SidebarMode] = { 467static const char *normalModeLabels_[max_SidebarMode] = {
462 pin_Icon " Bookmarks", 468 pin_Icon " ${sidebar.bookmarks}",
463 star_Icon " Feeds", 469 star_Icon " ${sidebar.feeds}",
464 clock_Icon " History", 470 clock_Icon " ${sidebar.history}",
465 person_Icon " Identities", 471 person_Icon " ${sidebar.identities}",
466 page_Icon " Outline", 472 page_Icon " ${sidebar.outline}",
467}; 473};
468 474
469static const char *tightModeLabels_[max_SidebarMode] = { 475static const char *tightModeLabels_[max_SidebarMode] = {
@@ -554,7 +560,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
554 // } 560 // }
555 } 561 }
556 else { 562 else {
557 iLabelWidget *heading = new_LabelWidget(person_Icon " Identities", NULL); 563 iLabelWidget *heading = new_LabelWidget(person_Icon " ${sidebar.identities}", NULL);
558 checkIcon_LabelWidget(heading); 564 checkIcon_LabelWidget(heading);
559 setBackgroundColor_Widget(as_Widget(heading), uiBackgroundSidebar_ColorId); 565 setBackgroundColor_Widget(as_Widget(heading), uiBackgroundSidebar_ColorId);
560 setTextColor_LabelWidget(heading, uiTextSelected_ColorId); 566 setTextColor_LabelWidget(heading, uiTextSelected_ColorId);
@@ -699,7 +705,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) {
699 collectNewFormat_String("%s " uiTextAction_ColorEscape "%zu%s", 705 collectNewFormat_String("%s " uiTextAction_ColorEscape "%zu%s",
700 tightModeLabels_[i], 706 tightModeLabels_[i],
701 d->numUnreadEntries, 707 d->numUnreadEntries,
702 !isTight ? " Unread" : "")); 708 !isTight ? " ${sidebar.unread}" : ""));
703 } 709 }
704 else { 710 else {
705 updateTextCStr_LabelWidget(button, 711 updateTextCStr_LabelWidget(button,
@@ -1008,7 +1014,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1008 postCommand_App("visited.changed"); 1014 postCommand_App("visited.changed");
1009 return iTrue; 1015 return iTrue;
1010 } 1016 }
1011 if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { 1017 if (isCommand_Widget(w, ev, "sidebar.entry.bookmark")) {
1012 makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); 1018 makeBookmarkCreation_Widget(&item->url, &item->label, item->icon);
1013 postCommand_App("focus.set id:bmed.title"); 1019 postCommand_App("focus.set id:bmed.title");
1014 return iTrue; 1020 return iTrue;
@@ -1032,12 +1038,12 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1032 } 1038 }
1033 else { 1039 else {
1034 makeQuestion_Widget( 1040 makeQuestion_Widget(
1035 uiTextCaution_ColorEscape "UNSUBSCRIBE", 1041 uiTextCaution_ColorEscape "${heading.unsub}",
1036 format_CStr("Really unsubscribe from feed\n\"%s\"?", 1042 format_CStr(cstr_Lang("dlg.confirm.unsub"),
1037 cstr_String(&feedBookmark->title)), 1043 cstr_String(&feedBookmark->title)),
1038 (iMenuItem[]){ 1044 (iMenuItem[]){
1039 { "Cancel", 0, 0, NULL }, 1045 { "${cancel}", 0, 0, NULL },
1040 { uiTextCaution_ColorEscape "Unsubscribe", 1046 { uiTextCaution_ColorEscape "${dlg.unsub}",
1041 0, 1047 0,
1042 0, 1048 0,
1043 format_CStr("!feed.entry.unsubscribe arg:1 ptr:%p", d) } }, 1049 format_CStr("!feed.entry.unsubscribe arg:1 ptr:%p", d) } },
@@ -1069,7 +1075,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1069 else if (isCommand_Widget(w, ev, "ident.showuse")) { 1075 else if (isCommand_Widget(w, ev, "ident.showuse")) {
1070 const iGmIdentity *ident = menuIdentity_SidebarWidget_(d); 1076 const iGmIdentity *ident = menuIdentity_SidebarWidget_(d);
1071 if (ident) { 1077 if (ident) {
1072 makeMessage_Widget(uiHeading_ColorEscape "IDENTITY USAGE", 1078 makeMessage_Widget(uiHeading_ColorEscape "${heading.ident.use}",
1073 cstrCollect_String(joinCStr_StringSet(ident->useUrls, "\n"))); 1079 cstrCollect_String(joinCStr_StringSet(ident->useUrls, "\n")));
1074 } 1080 }
1075 return iTrue; 1081 return iTrue;
@@ -1079,8 +1085,8 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1079 if (ident) { 1085 if (ident) {
1080 makeValueInput_Widget(get_Window()->root, 1086 makeValueInput_Widget(get_Window()->root,
1081 &ident->notes, 1087 &ident->notes,
1082 uiHeading_ColorEscape "IDENTITY NOTES", 1088 uiHeading_ColorEscape "${heading.ident.notes}",
1083 format_CStr("Notes about %s:", cstr_String(name_GmIdentity(ident))), 1089 format_CStr(cstr_Lang("dlg.ident.notes"), cstr_String(name_GmIdentity(ident))),
1084 uiTextAction_ColorEscape "OK", 1090 uiTextAction_ColorEscape "OK",
1085 format_CStr("!ident.setnotes ident:%p ptr:%p", ident, d)); 1091 format_CStr("!ident.setnotes ident:%p ptr:%p", ident, d));
1086 } 1092 }
@@ -1120,15 +1126,16 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1120 iSidebarItem *item = d->contextItem; 1126 iSidebarItem *item = d->contextItem;
1121 if (argLabel_Command(cmd, "confirm")) { 1127 if (argLabel_Command(cmd, "confirm")) {
1122 makeQuestion_Widget( 1128 makeQuestion_Widget(
1123 uiTextCaution_ColorEscape "DELETE IDENTITY", 1129 uiTextCaution_ColorEscape "${heading.ident.delete}",
1124 format_CStr( 1130 format_CStr(cstr_Lang("dlg.confirm.ident.delete"),
1125 "Do you really want to delete the identity\n" uiTextAction_ColorEscape 1131 uiTextAction_ColorEscape,
1126 "%s\n" uiText_ColorEscape 1132 cstr_String(&item->label),
1127 "including its certificate and private key files?", 1133 uiText_ColorEscape),
1128 cstr_String(&item->label)), 1134 (iMenuItem[]){ { "${cancel}", 0, 0, NULL },
1129 (iMenuItem[]){ { "Cancel", 0, 0, NULL }, 1135 { uiTextCaution_ColorEscape "${dlg.ident.delete}",
1130 { uiTextCaution_ColorEscape "Delete Identity and Files", 1136 0,
1131 0, 0, format_CStr("!ident.delete confirm:0 ptr:%p", d) } }, 1137 0,
1138 format_CStr("!ident.delete confirm:0 ptr:%p", d) } },
1132 2); 1139 2);
1133 return iTrue; 1140 return iTrue;
1134 } 1141 }
@@ -1163,10 +1170,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1163 } 1170 }
1164 else if (equal_Command(cmd, "history.clear")) { 1171 else if (equal_Command(cmd, "history.clear")) {
1165 if (argLabel_Command(cmd, "confirm")) { 1172 if (argLabel_Command(cmd, "confirm")) {
1166 makeQuestion_Widget(uiTextCaution_ColorEscape "CLEAR HISTORY", 1173 makeQuestion_Widget(uiTextCaution_ColorEscape "${heading.history.clear}",
1167 "Do you really want to erase the history of all visited pages?", 1174 "${dlg.confirm.history.clear}",
1168 (iMenuItem[]){ { "Cancel", 0, 0, NULL }, 1175 (iMenuItem[]){ { "${cancel}", 0, 0, NULL },
1169 { uiTextCaution_ColorEscape "Clear History", 1176 { uiTextCaution_ColorEscape "${dlg.history.clear}",
1170 0, 0, "history.clear confirm:0" } }, 1177 0, 0, "history.clear confirm:0" } },
1171 2); 1178 2);
1172 } 1179 }
@@ -1215,24 +1222,24 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1215 if (menuItem) { 1222 if (menuItem) {
1216 setTextCStr_LabelWidget(menuItem, 1223 setTextCStr_LabelWidget(menuItem,
1217 hasTag_Bookmark(bm, "homepage") 1224 hasTag_Bookmark(bm, "homepage")
1218 ? home_Icon " Remove Homepage" 1225 ? home_Icon " ${bookmark.untag.home}"
1219 : home_Icon " Use as Homepage"); 1226 : home_Icon " ${bookmark.tag.home}");
1220 checkIcon_LabelWidget(menuItem); 1227 checkIcon_LabelWidget(menuItem);
1221 } 1228 }
1222 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:subscribed"); 1229 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:subscribed");
1223 if (menuItem) { 1230 if (menuItem) {
1224 setTextCStr_LabelWidget(menuItem, 1231 setTextCStr_LabelWidget(menuItem,
1225 hasTag_Bookmark(bm, "subscribed") 1232 hasTag_Bookmark(bm, "subscribed")
1226 ? star_Icon " Unsubscribe from Feed" 1233 ? star_Icon " ${bookmark.untag.sub}"
1227 : star_Icon " Subscribe to Feed"); 1234 : star_Icon " ${bookmark.tag.sub}");
1228 checkIcon_LabelWidget(menuItem); 1235 checkIcon_LabelWidget(menuItem);
1229 } 1236 }
1230 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource"); 1237 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource");
1231 if (menuItem) { 1238 if (menuItem) {
1232 setTextCStr_LabelWidget(menuItem, 1239 setTextCStr_LabelWidget(menuItem,
1233 hasTag_Bookmark(bm, "remotesource") 1240 hasTag_Bookmark(bm, "remotesource")
1234 ? downArrowBar_Icon " Remove Bookmark Source" 1241 ? downArrowBar_Icon " ${bookmark.untag.remote}"
1235 : downArrowBar_Icon " Use as Bookmark Source"); 1242 : downArrowBar_Icon " ${bookmark.tag.remote}");
1236 checkIcon_LabelWidget(menuItem); 1243 checkIcon_LabelWidget(menuItem);
1237 } 1244 }
1238 } 1245 }
@@ -1241,8 +1248,8 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1241 iLabelWidget *menuItem = findMenuItem_Widget(d->menu, "feed.entry.toggleread"); 1248 iLabelWidget *menuItem = findMenuItem_Widget(d->menu, "feed.entry.toggleread");
1242 const iBool isRead = d->contextItem->indent == 0; 1249 const iBool isRead = d->contextItem->indent == 0;
1243 setTextCStr_LabelWidget(menuItem, 1250 setTextCStr_LabelWidget(menuItem,
1244 isRead ? circle_Icon " Mark as Unread" 1251 isRead ? circle_Icon " ${feeds.entry.markunread}"
1245 : circleWhite_Icon " Mark as Read"); 1252 : circleWhite_Icon " ${feeds.entry.markread}");
1246 checkIcon_LabelWidget(menuItem); 1253 checkIcon_LabelWidget(menuItem);
1247 } 1254 }
1248 else if (d->mode == identities_SidebarMode) { 1255 else if (d->mode == identities_SidebarMode) {
diff --git a/src/ui/translation.c b/src/ui/translation.c
index d3ac5bba..aaedfa4f 100644
--- a/src/ui/translation.c
+++ b/src/ui/translation.c
@@ -471,7 +471,7 @@ iBool handleCommand_Translation(iTranslation *d, const char *cmd) {
471 updateTextCStr_LabelWidget( 471 updateTextCStr_LabelWidget(
472 findMenuItem_Widget(findChild_Widget(d->dlg, "dialogbuttons"), 472 findMenuItem_Widget(findChild_Widget(d->dlg, "dialogbuttons"),
473 "translation.cancel"), 473 "translation.cancel"),
474 "Dismiss"); 474 "${dismiss}");
475 cancel_TlsRequest(d->request); 475 cancel_TlsRequest(d->request);
476 } 476 }
477 else { 477 else {
diff --git a/src/ui/util.c b/src/ui/util.c
index d68ae738..5a08ec20 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -498,7 +498,7 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
498 if (deviceType_App() == phone_AppDeviceType) { 498 if (deviceType_App() == phone_AppDeviceType) {
499 addChild_Widget(menu, iClob(makeMenuSeparator_())); 499 addChild_Widget(menu, iClob(makeMenuSeparator_()));
500 addChildFlags_Widget(menu, 500 addChildFlags_Widget(menu,
501 iClob(new_LabelWidget("Cancel", "cancel")), 501 iClob(new_LabelWidget("${cancel}", "cancel")),
502 itemFlags | noBackground_WidgetFlag | frameless_WidgetFlag | 502 itemFlags | noBackground_WidgetFlag | frameless_WidgetFlag |
503 alignLeft_WidgetFlag); 503 alignLeft_WidgetFlag);
504 } 504 }
@@ -937,7 +937,7 @@ static iBool slidePanelHandler_(iWidget *d, const char *cmd) {
937 iWidget *button = pointer_Command(cmd); 937 iWidget *button = pointer_Command(cmd);
938 iWidget *panel = userData_Object(button); 938 iWidget *panel = userData_Object(button);
939 openMenu_Widget(panel, zero_I2()); 939 openMenu_Widget(panel, zero_I2());
940// updateTextCStr_LabelWidget(findWidget_App("panel.back"), ""); 940// updateTextCStr_LabelWidget(findWidget_App("panel.back"), );
941 return iTrue; 941 return iTrue;
942 } 942 }
943 if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd) && 943 if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd) &&
@@ -1462,7 +1462,7 @@ void makeFilePath_Widget(iWidget * parent,
1462 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 1462 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
1463 iWidget *div = new_Widget(); { 1463 iWidget *div = new_Widget(); {
1464 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); 1464 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
1465 addChild_Widget(div, iClob(newKeyMods_LabelWidget("Cancel", SDLK_ESCAPE, 0, "filepath.cancel"))); 1465 addChild_Widget(div, iClob(newKeyMods_LabelWidget("${cancel}", SDLK_ESCAPE, 0, "filepath.cancel")));
1466 addChild_Widget(div, iClob(newKeyMods_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept"))); 1466 addChild_Widget(div, iClob(newKeyMods_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept")));
1467 } 1467 }
1468 addChild_Widget(dlg, iClob(div)); 1468 addChild_Widget(dlg, iClob(div));
@@ -1566,7 +1566,7 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) {
1566 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); 1566 addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag);
1567 continue; 1567 continue;
1568 } 1568 }
1569 if (!iCmpStr(label, "Cancel") && !cmd) { 1569 if (!iCmpStr(label, "${cancel}") && !cmd) {
1570 cmd = "cancel"; 1570 cmd = "cancel";
1571 key = SDLK_ESCAPE; 1571 key = SDLK_ESCAPE;
1572 kmods = 0; 1572 kmods = 0;
@@ -1621,7 +1621,7 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
1621 addChild_Widget( 1621 addChild_Widget(
1622 dlg, 1622 dlg,
1623 iClob(makeDialogButtons_Widget( 1623 iClob(makeDialogButtons_Widget(
1624 (iMenuItem[]){ { "Cancel", 0, 0, NULL }, { acceptLabel, 0, 0, "valueinput.accept" } }, 1624 (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, { acceptLabel, 0, 0, "valueinput.accept" } },
1625 2))); 1625 2)));
1626 finalizeSheet_Widget(dlg); 1626 finalizeSheet_Widget(dlg);
1627 if (parent) { 1627 if (parent) {
@@ -1970,7 +1970,7 @@ iWidget *makePreferences_Widget(void) {
1970 } 1970 }
1971 addChild_Widget(dlg, 1971 addChild_Widget(dlg,
1972 iClob(makeDialogButtons_Widget( 1972 iClob(makeDialogButtons_Widget(
1973 (iMenuItem[]){ { "Dismiss", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1))); 1973 (iMenuItem[]){ { "${dismiss}", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1)));
1974 addChild_Widget(get_Window()->root, iClob(dlg)); 1974 addChild_Widget(get_Window()->root, iClob(dlg));
1975 finalizeSheet_Widget(dlg); 1975 finalizeSheet_Widget(dlg);
1976 return dlg; 1976 return dlg;
@@ -2001,7 +2001,7 @@ iWidget *makeBookmarkEditor_Widget(void) {
2001 } 2001 }
2002 addChild_Widget( 2002 addChild_Widget(
2003 dlg, 2003 dlg,
2004 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "Cancel", 0, 0, NULL }, 2004 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "${cancel}", 0, 0, NULL },
2005 { uiTextCaution_ColorEscape "Save Bookmark", 2005 { uiTextCaution_ColorEscape "Save Bookmark",
2006 SDLK_RETURN, 2006 SDLK_RETURN,
2007 KMOD_PRIMARY, 2007 KMOD_PRIMARY,
@@ -2131,7 +2131,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
2131 iWidget *buttons = 2131 iWidget *buttons =
2132 addChild_Widget(dlg, 2132 addChild_Widget(dlg,
2133 iClob(makeDialogButtons_Widget( 2133 iClob(makeDialogButtons_Widget(
2134 (iMenuItem[]){ { "Cancel", 0, 0, NULL }, 2134 (iMenuItem[]){ { "${cancel}", 0, 0, NULL },
2135 { bookmarkId ? uiTextCaution_ColorEscape "Save Settings" 2135 { bookmarkId ? uiTextCaution_ColorEscape "Save Settings"
2136 : uiTextCaution_ColorEscape "Subscribe", 2136 : uiTextCaution_ColorEscape "Subscribe",
2137 SDLK_RETURN, 2137 SDLK_RETURN,
@@ -2212,7 +2212,7 @@ iWidget *makeIdentityCreation_Widget(void) {
2212 } 2212 }
2213 addChild_Widget( 2213 addChild_Widget(
2214 dlg, 2214 dlg,
2215 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "Cancel", 0, 0, NULL }, 2215 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "${cancel}", 0, 0, NULL },
2216 { uiTextAction_ColorEscape "Create Identity", 2216 { uiTextAction_ColorEscape "Create Identity",
2217 SDLK_RETURN, 2217 SDLK_RETURN,
2218 KMOD_PRIMARY, 2218 KMOD_PRIMARY,
@@ -2310,7 +2310,7 @@ iWidget *makeTranslation_Widget(iWidget *parent) {
2310 dlg, 2310 dlg,
2311 iClob(makeDialogButtons_Widget( 2311 iClob(makeDialogButtons_Widget(
2312 (iMenuItem[]){ 2312 (iMenuItem[]){
2313 { "Cancel", SDLK_ESCAPE, 0, "translation.cancel" }, 2313 { "${cancel}", SDLK_ESCAPE, 0, "translation.cancel" },
2314 { uiTextAction_ColorEscape "Translate", SDLK_RETURN, 0, "translation.submit" } }, 2314 { uiTextAction_ColorEscape "Translate", SDLK_RETURN, 0, "translation.submit" } },
2315 2))); 2315 2)));
2316 addChild_Widget(parent, iClob(dlg)); 2316 addChild_Widget(parent, iClob(dlg));
diff --git a/src/ui/window.c b/src/ui/window.c
index d27d252b..6949e245 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -293,8 +293,8 @@ static const iMenuItem viewMenuItems_[] = {
293}; 293};
294 294
295static iMenuItem bookmarksMenuItems_[] = { 295static iMenuItem bookmarksMenuItems_[] = {
296 { "${menu.bookmark.page}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, 296 { "${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" },
297 { "${menu.subscribe.page}", subscribeToPage_KeyModifier, "feeds.subscribe" }, 297 { "${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" },
298 { "---", 0, 0, NULL }, 298 { "---", 0, 0, NULL },
299 { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" }, 299 { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" },
300 { "---", 0, 0, NULL }, 300 { "---", 0, 0, NULL },
@@ -1162,7 +1162,7 @@ static void setupUserInterface_Window(iWindow *d) {
1162 { pin_Icon " ${sidebar.bookmarks}", 0, 0, "toolbar.showview arg:0" }, 1162 { pin_Icon " ${sidebar.bookmarks}", 0, 0, "toolbar.showview arg:0" },
1163 { star_Icon " ${sidebar.feeds}", 0, 0, "toolbar.showview arg:1" }, 1163 { star_Icon " ${sidebar.feeds}", 0, 0, "toolbar.showview arg:1" },
1164 { clock_Icon " ${sidebar.history}", 0, 0, "toolbar.showview arg:2" }, 1164 { clock_Icon " ${sidebar.history}", 0, 0, "toolbar.showview arg:2" },
1165 { page_Icon " ${sidebar.outline}", 0, 0, "toolbar.showview arg:4" }, 1165 { page_Icon " ${toolbar.outline}", 0, 0, "toolbar.showview arg:4" },
1166 }; 1166 };
1167 iWidget *menu = makeMenu_Widget(findChild_Widget(toolBar, "toolbar.view"), 1167 iWidget *menu = makeMenu_Widget(findChild_Widget(toolBar, "toolbar.view"),
1168 items, iElemCount(items)); 1168 items, iElemCount(items));