diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-22 19:27:44 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-22 19:29:09 +0200 |
commit | de34940c4dd709fa7103c6961cbe8b410638ffdd (patch) | |
tree | 03afd79839273638bc25efbed0d8e70e2ce0896d | |
parent | 0bd8465b33c6e3f6dacf79785a47cbd6abea6f25 (diff) |
Lang: Improvements; macOS menu items
Use char pointer ranges for the array of IDs and strings to avoid allocations.
Translate macOS menu items.
IssueID #192
-rw-r--r-- | po/en.po | 35 | ||||
-rw-r--r-- | res/lang/en.bin | bin | 2131 -> 2275 bytes | |||
-rw-r--r-- | src/lang.c | 53 | ||||
-rw-r--r-- | src/lang.h | 7 | ||||
-rw-r--r-- | src/macos.m | 4 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 24 | ||||
-rw-r--r-- | src/ui/window.c | 12 |
7 files changed, 87 insertions, 48 deletions
@@ -10,6 +10,24 @@ msgstr "Version" | |||
10 | msgid "about.powered" | 10 | msgid "about.powered" |
11 | msgstr "Powered by SDL 2, OpenSSL, and ☕️" | 11 | msgstr "Powered by SDL 2, OpenSSL, and ☕️" |
12 | 12 | ||
13 | msgid "menu.title.file" | ||
14 | msgstr "File" | ||
15 | |||
16 | msgid "menu.title.edit" | ||
17 | msgstr "Edit" | ||
18 | |||
19 | msgid "menu.title.view" | ||
20 | msgstr "View" | ||
21 | |||
22 | msgid "menu.title.bookmarks" | ||
23 | msgstr "Bookmarks" | ||
24 | |||
25 | msgid "menu.title.identity" | ||
26 | msgstr "Identity" | ||
27 | |||
28 | msgid "menu.title.help" | ||
29 | msgstr "Help" | ||
30 | |||
13 | msgid "menu.newtab" | 31 | msgid "menu.newtab" |
14 | msgstr "New Tab" | 32 | msgstr "New Tab" |
15 | 33 | ||
@@ -29,7 +47,7 @@ msgid "menu.duptab" | |||
29 | msgstr "Duplicate Tab" | 47 | msgstr "Duplicate Tab" |
30 | 48 | ||
31 | msgid "menu.openlocation" | 49 | msgid "menu.openlocation" |
32 | msgstr "Open Location..." | 50 | msgstr "Open Location…" |
33 | 51 | ||
34 | msgid "menu.find" | 52 | msgid "menu.find" |
35 | msgstr "Find on Page" | 53 | msgstr "Find on Page" |
@@ -86,7 +104,7 @@ msgid "menu.feeds.entrylist" | |||
86 | msgstr "Show Feed Entries" | 104 | msgstr "Show Feed Entries" |
87 | 105 | ||
88 | msgid "menu.preferences" | 106 | msgid "menu.preferences" |
89 | msgstr "Preferences..." | 107 | msgstr "Preferences…" |
90 | 108 | ||
91 | msgid "menu.help" | 109 | msgid "menu.help" |
92 | msgstr "Help" | 110 | msgstr "Help" |
@@ -146,13 +164,13 @@ msgid "menu.debug" | |||
146 | msgstr "Debug Information" | 164 | msgstr "Debug Information" |
147 | 165 | ||
148 | msgid "menu.bookmark.page" | 166 | msgid "menu.bookmark.page" |
149 | msgstr "Bookmark This Page..." | 167 | msgstr "Bookmark This Page…" |
150 | 168 | ||
151 | msgid "menu.subscribe.page" | 169 | msgid "menu.subscribe.page" |
152 | msgstr "Subscribe to This Page..." | 170 | msgstr "Subscribe to This Page…" |
153 | 171 | ||
154 | msgid "menu.import.links" | 172 | msgid "menu.import.links" |
155 | msgstr "Import All Links on Page..." | 173 | msgstr "Import All Links on Page…" |
156 | 174 | ||
157 | msgid "menu.bookmarks.refresh" | 175 | msgid "menu.bookmarks.refresh" |
158 | msgstr "Refresh Remote Bookmarks" | 176 | msgstr "Refresh Remote Bookmarks" |
@@ -161,10 +179,10 @@ msgid "menu.feeds.refresh" | |||
161 | msgstr "Refresh Feeds" | 179 | msgstr "Refresh Feeds" |
162 | 180 | ||
163 | msgid "menu.identity.new" | 181 | msgid "menu.identity.new" |
164 | msgstr "New Identity..." | 182 | msgstr "New Identity…" |
165 | 183 | ||
166 | msgid "menu.identity.import" | 184 | msgid "menu.identity.import" |
167 | msgstr "Import..." | 185 | msgstr "Import…" |
168 | 186 | ||
169 | msgid "menu.identity.notactive" | 187 | msgid "menu.identity.notactive" |
170 | msgstr "No Active Identity" | 188 | msgstr "No Active Identity" |
@@ -193,10 +211,11 @@ msgstr "Search Query" | |||
193 | msgid "status.feeds" | 211 | msgid "status.feeds" |
194 | msgstr "Updating Feeds" | 212 | msgstr "Updating Feeds" |
195 | 213 | ||
214 | # megabytes, used as the unit after a number | ||
196 | msgid "mb" | 215 | msgid "mb" |
197 | msgstr "MB" | 216 | msgstr "MB" |
198 | 217 | ||
199 | # strftime() formatted, split on two lines | 218 | # strftime() formatted, split on two lines |
200 | #, c-format | 219 | #, c-format |
201 | msgid "page.timestamp" | 220 | msgid "page.timestamp" |
202 | msgstr "Received at %I:%M %p\non %b %d, %Y" | 221 | msgstr "Received at %I:%M %p\non %b %d, %Y" |
diff --git a/res/lang/en.bin b/res/lang/en.bin index d02a2f3d..0603e6ad 100644 --- a/res/lang/en.bin +++ b/res/lang/en.bin | |||
Binary files differ | |||
@@ -8,13 +8,13 @@ iDeclareType(Lang) | |||
8 | iDeclareType(MsgStr) | 8 | iDeclareType(MsgStr) |
9 | 9 | ||
10 | struct Impl_MsgStr { | 10 | struct Impl_MsgStr { |
11 | const char *id; /* these point to null-terminated strings in embedded data */ | 11 | iRangecc id; /* these point to null-terminated strings in embedded data */ |
12 | const char *str; | 12 | iRangecc str; |
13 | }; | 13 | }; |
14 | 14 | ||
15 | int cmp_MsgStr_(const void *e1, const void *e2) { | 15 | int cmp_MsgStr_(const void *e1, const void *e2) { |
16 | const iMsgStr *a = e1, *b = e2; | 16 | const iMsgStr *a = e1, *b = e2; |
17 | return iCmpStr(a->id, b->id); | 17 | return cmpCStrNSc_Rangecc(a->id, b->id.start, size_Range(&b->id), &iCaseSensitive); |
18 | } | 18 | } |
19 | 19 | ||
20 | /*----------------------------------------------------------------------------------------------*/ | 20 | /*----------------------------------------------------------------------------------------------*/ |
@@ -35,10 +35,12 @@ static void load_Lang_(iLang *d, const char *id) { | |||
35 | const iBlock *data = &blobEn_Embedded; | 35 | const iBlock *data = &blobEn_Embedded; |
36 | iMsgStr msg; | 36 | iMsgStr msg; |
37 | for (const char *ptr = constBegin_Block(data); ptr != constEnd_Block(data); ptr++) { | 37 | for (const char *ptr = constBegin_Block(data); ptr != constEnd_Block(data); ptr++) { |
38 | msg.id = ptr; | 38 | msg.id.start = ptr; |
39 | while (*++ptr) {} | 39 | while (*++ptr) {} |
40 | msg.str = ++ptr; | 40 | msg.id.end = ptr; |
41 | msg.str.start = ++ptr; | ||
41 | while (*++ptr) {} | 42 | while (*++ptr) {} |
43 | msg.str.end = ptr; | ||
42 | /* Allocate the string. The data has already been sorted. */ | 44 | /* Allocate the string. The data has already been sorted. */ |
43 | pushBack_Array(&d->messages->values, &msg); | 45 | pushBack_Array(&d->messages->values, &msg); |
44 | } | 46 | } |
@@ -62,18 +64,51 @@ void setCurrent_Lang(const char *language) { | |||
62 | load_Lang_(d, language); | 64 | load_Lang_(d, language); |
63 | } | 65 | } |
64 | 66 | ||
65 | const char *cstr_Lang(const char *msgId) { | 67 | iRangecc range_Lang(iRangecc msgId) { |
66 | const iLang *d = &lang_; | 68 | const iLang *d = &lang_; |
67 | size_t pos; | 69 | size_t pos; |
68 | const iMsgStr key = { .id = iConstCast(char *, msgId) }; | 70 | const iMsgStr key = { .id = msgId }; |
69 | if (locate_SortedArray(d->messages, &key, &pos)) { | 71 | if (locate_SortedArray(d->messages, &key, &pos)) { |
70 | return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str; | 72 | return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str; |
71 | } | 73 | } |
72 | fprintf(stderr, "[Lang] missing: %s\n", msgId); fflush(stderr); | 74 | fprintf(stderr, "[Lang] missing: %s\n", cstr_Rangecc(msgId)); fflush(stderr); |
73 | iAssert(iFalse); | 75 | iAssert(iFalse); |
74 | return msgId; | 76 | return msgId; |
75 | } | 77 | } |
76 | 78 | ||
79 | const char *cstr_Lang(const char *msgId) { | ||
80 | return range_Lang(range_CStr(msgId)).start; /* guaranteed to be NULL-terminated */ | ||
81 | } | ||
82 | |||
77 | const iString *string_Lang(const char *msgId) { | 83 | const iString *string_Lang(const char *msgId) { |
78 | return collectNewCStr_String(cstr_Lang(msgId)); | 84 | return collectNewRange_String(range_Lang(range_CStr(msgId))); |
85 | } | ||
86 | |||
87 | void translate_Lang(iString *textWithIds) { | ||
88 | for (const char *pos = cstr_String(textWithIds); *pos; ) { | ||
89 | iRangecc id; | ||
90 | id.start = strstr(pos, "${"); | ||
91 | if (!id.start) { | ||
92 | break; | ||
93 | } | ||
94 | id.start += 2; | ||
95 | id.end = strchr(id.start, '}'); | ||
96 | iAssert(id.end != NULL); | ||
97 | const size_t idLen = size_Range(&id); | ||
98 | const iRangecc replacement = range_Lang(id); | ||
99 | const size_t startPos = id.start - cstr_String(textWithIds) - 2; | ||
100 | /* Replace it. */ | ||
101 | remove_Block(&textWithIds->chars, startPos, idLen + 3); | ||
102 | insertData_Block(&textWithIds->chars, startPos, replacement.start, size_Range(&replacement)); | ||
103 | pos = cstr_String(textWithIds) + startPos + size_Range(&replacement); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | const char *translateCStr_Lang(const char *textWithIds) { | ||
108 | if (strstr(textWithIds, "${") == NULL) { | ||
109 | return textWithIds; /* nothing to replace */ | ||
110 | } | ||
111 | iString *text = collectNewCStr_String(textWithIds); | ||
112 | translate_Lang(text); | ||
113 | return cstr_String(text); | ||
79 | } | 114 | } |
@@ -6,5 +6,10 @@ void init_Lang (void); | |||
6 | void deinit_Lang (void); | 6 | void deinit_Lang (void); |
7 | 7 | ||
8 | void setCurrent_Lang (const char *language); | 8 | void setCurrent_Lang (const char *language); |
9 | const iString * string_Lang (const char *msgId); | 9 | iRangecc range_Lang (iRangecc msgId); |
10 | |||
10 | const char * cstr_Lang (const char *msgId); | 11 | const char * cstr_Lang (const char *msgId); |
12 | const iString * string_Lang (const char *msgId); | ||
13 | |||
14 | void translate_Lang (iString *textWithIds); | ||
15 | const char * translateCStr_Lang (const char *textWithIds); | ||
diff --git a/src/macos.m b/src/macos.m index c6c30fa5..12575dd5 100644 --- a/src/macos.m +++ b/src/macos.m | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "macos.h" | 23 | #include "macos.h" |
24 | #include "app.h" | 24 | #include "app.h" |
25 | #include "lang.h" | ||
25 | #include "ui/color.h" | 26 | #include "ui/color.h" |
26 | #include "ui/command.h" | 27 | #include "ui/command.h" |
27 | #include "ui/keys.h" | 28 | #include "ui/keys.h" |
@@ -462,6 +463,7 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * | |||
462 | NSApplication *app = [NSApplication sharedApplication]; | 463 | NSApplication *app = [NSApplication sharedApplication]; |
463 | MyDelegate *myDel = (MyDelegate *) app.delegate; | 464 | MyDelegate *myDel = (MyDelegate *) app.delegate; |
464 | NSMenu *appMenu = [app mainMenu]; | 465 | NSMenu *appMenu = [app mainMenu]; |
466 | menuLabel = translateCStr_Lang(menuLabel); | ||
465 | NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] | 467 | NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] |
466 | action:nil | 468 | action:nil |
467 | keyEquivalent:@"" | 469 | keyEquivalent:@"" |
@@ -469,7 +471,7 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * | |||
469 | NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; | 471 | NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; |
470 | [menu setAutoenablesItems:NO]; | 472 | [menu setAutoenablesItems:NO]; |
471 | for (size_t i = 0; i < count; ++i) { | 473 | for (size_t i = 0; i < count; ++i) { |
472 | const char *label = items[i].label; | 474 | const char *label = translateCStr_Lang(items[i].label); |
473 | if (label[0] == '\r') { | 475 | if (label[0] == '\r') { |
474 | /* Skip the formatting escape. */ | 476 | /* Skip the formatting escape. */ |
475 | label += 2; | 477 | label += 2; |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index c3bc4392..0becd419 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -359,29 +359,7 @@ void updateSize_LabelWidget(iLabelWidget *d) { | |||
359 | } | 359 | } |
360 | 360 | ||
361 | static void replaceVariables_LabelWidget_(iLabelWidget *d) { | 361 | static void replaceVariables_LabelWidget_(iLabelWidget *d) { |
362 | for (const char *label = cstr_String(&d->label); *label; ) { | 362 | translate_Lang(&d->label); |
363 | iRangecc id; | ||
364 | id.start = strstr(label, "${"); | ||
365 | if (!id.start) { | ||
366 | break; | ||
367 | } | ||
368 | id.start += 2; | ||
369 | id.end = strchr(id.start, '}'); | ||
370 | iAssert(id.end != NULL); | ||
371 | /* TODO: Add a lookup that doesn't allocate anything; Lang can handle it. */ | ||
372 | const size_t len = size_Range(&id); | ||
373 | char *key = malloc(len + 1); | ||
374 | memcpy(key, id.start, len); | ||
375 | key[len] = 0; | ||
376 | const char *text = cstr_Lang(key); | ||
377 | const size_t textLen = strlen(text); | ||
378 | free(key); | ||
379 | /* Replace it. */ | ||
380 | size_t startPos = id.start - cstr_String(&d->label) - 2; | ||
381 | remove_Block(&d->label.chars, startPos, len + 3); | ||
382 | insertData_Block(&d->label.chars, startPos, text, textLen); | ||
383 | label = cstr_String(&d->label) + startPos + textLen; | ||
384 | } | ||
385 | } | 363 | } |
386 | 364 | ||
387 | void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { | 365 | void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { |
diff --git a/src/ui/window.c b/src/ui/window.c index 2d1deb72..d27d252b 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1055,12 +1055,12 @@ static void setupUserInterface_Window(iWindow *d) { | |||
1055 | setAlignVisually_LabelWidget(navMenu, iTrue); | 1055 | setAlignVisually_LabelWidget(navMenu, iTrue); |
1056 | setId_Widget(addChildFlags_Widget(navBar, iClob(navMenu), collapse_WidgetFlag), "navbar.menu"); | 1056 | setId_Widget(addChildFlags_Widget(navBar, iClob(navMenu), collapse_WidgetFlag), "navbar.menu"); |
1057 | #else | 1057 | #else |
1058 | insertMenuItems_MacOS("File", 1, fileMenuItems_, iElemCount(fileMenuItems_)); | 1058 | insertMenuItems_MacOS("${menu.title.file}", 1, fileMenuItems_, iElemCount(fileMenuItems_)); |
1059 | insertMenuItems_MacOS("Edit", 2, editMenuItems_, iElemCount(editMenuItems_)); | 1059 | insertMenuItems_MacOS("${menu.title.edit}", 2, editMenuItems_, iElemCount(editMenuItems_)); |
1060 | insertMenuItems_MacOS("View", 3, viewMenuItems_, iElemCount(viewMenuItems_)); | 1060 | insertMenuItems_MacOS("${menu.title.view}", 3, viewMenuItems_, iElemCount(viewMenuItems_)); |
1061 | insertMenuItems_MacOS("Bookmarks", 4, bookmarksMenuItems_, iElemCount(bookmarksMenuItems_)); | 1061 | insertMenuItems_MacOS("${menu.title.bookmarks}", 4, bookmarksMenuItems_, iElemCount(bookmarksMenuItems_)); |
1062 | insertMenuItems_MacOS("Identity", 5, identityMenuItems_, iElemCount(identityMenuItems_)); | 1062 | insertMenuItems_MacOS("${menu.title.identity}", 5, identityMenuItems_, iElemCount(identityMenuItems_)); |
1063 | insertMenuItems_MacOS("Help", 7, helpMenuItems_, iElemCount(helpMenuItems_)); | 1063 | insertMenuItems_MacOS("${menu.title.help}", 7, helpMenuItems_, iElemCount(helpMenuItems_)); |
1064 | #endif | 1064 | #endif |
1065 | } | 1065 | } |
1066 | /* Tab bar. */ { | 1066 | /* Tab bar. */ { |