summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rwxr-xr-xpo/compile.py55
-rw-r--r--res/about/lagrange.gmi2
-rw-r--r--res/lang/en.binbin0 -> 2131 bytes
-rw-r--r--src/app.c2
-rw-r--r--src/lang.c11
-rw-r--r--src/ui/labelwidget.c36
-rw-r--r--src/ui/window.c4
8 files changed, 101 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2166acc5..19cc6653 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,6 +73,7 @@ set (EMBED_RESOURCES
73 res/fonts/SourceSansPro-Regular.ttf 73 res/fonts/SourceSansPro-Regular.ttf
74 res/fonts/SourceSansPro-Bold.ttf 74 res/fonts/SourceSansPro-Bold.ttf
75 res/fonts/Symbola.ttf 75 res/fonts/Symbola.ttf
76 res/lang/en.bin
76 res/shadow.png 77 res/shadow.png
77) 78)
78if ((UNIX AND NOT APPLE) OR MSYS) 79if ((UNIX AND NOT APPLE) OR MSYS)
diff --git a/po/compile.py b/po/compile.py
new file mode 100755
index 00000000..6e565733
--- /dev/null
+++ b/po/compile.py
@@ -0,0 +1,55 @@
1#!/usr/bin/env python3
2# Parses all the .po files and generates binary language strings to be loaded
3# at runtime via embedded data.
4
5import os
6
7ESCAPES = {
8 '\\': '\\',
9 '"': '"',
10 'n': '\n',
11 'r': '\r',
12 't': '\t'
13}
14
15
16def unquote(string):
17 txt = string.strip()
18 if txt[0] != '"' or txt[-1] != '"':
19 raise Exception("invalid quoted string: " + string)
20 txt = txt[1:-1]
21 out = ''
22 is_escape = False
23 for c in txt:
24 if is_escape:
25 out += ESCAPES[c]
26 is_escape = False
27 continue
28 if c == '\\':
29 is_escape = True
30 else:
31 out += c
32 return out
33
34
35messages = []
36for src in os.listdir('.'):
37 if not src.endswith('.po'):
38 continue
39 msg_id, msg_str = None, None
40 for line in open(src, 'rt', encoding='utf-8').readlines():
41 line = line.strip()
42 if line.startswith('msgid'):
43 msg_id = unquote(line[6:])
44 elif line.startswith('msgstr'):
45 msg_str = unquote(line[7:])
46 messages.append((msg_id, msg_str))
47 # Make a binary blob with strings sorted by ID.
48 compiled = bytes()
49 for msg in sorted(messages):
50 compiled += msg[0].encode('utf-8') + bytes([0])
51 compiled += msg[1].encode('utf-8') + bytes([0])
52 #print(compiled)
53 open(f'../res/lang/{src[:-3]}.bin', 'wb').write(compiled)
54
55
diff --git a/res/about/lagrange.gmi b/res/about/lagrange.gmi
index c043d26b..7544c26d 100644
--- a/res/about/lagrange.gmi
+++ b/res/about/lagrange.gmi
@@ -10,6 +10,6 @@ o888ooooood8 `Y888""8o `8oooooo. d888b `Y888""8o o888o o888o `8oooooo. `Y8b
10 "Y88888P' "Y88888P' 10 "Y88888P' "Y88888P'
11``` 11```
12# ${about.tagline} 12# ${about.tagline}
13## ${version} ${APP_VERSION} 13## ${about.version} ${APP_VERSION}
14=> https://skyjake.fi/@jk by @jk@skyjake.fi 14=> https://skyjake.fi/@jk by @jk@skyjake.fi
15${about.powered} 15${about.powered}
diff --git a/res/lang/en.bin b/res/lang/en.bin
new file mode 100644
index 00000000..d02a2f3d
--- /dev/null
+++ b/res/lang/en.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 4789c62b..95345bda 100644
--- a/src/app.c
+++ b/src/app.c
@@ -507,7 +507,6 @@ static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance,
507 507
508static void init_App_(iApp *d, int argc, char **argv) { 508static void init_App_(iApp *d, int argc, char **argv) {
509 init_CommandLine(&d->args, argc, argv); 509 init_CommandLine(&d->args, argc, argv);
510 init_Lang();
511 /* Where was the app started from? We ask SDL first because the command line alone is 510 /* Where was the app started from? We ask SDL first because the command line alone is
512 not a reliable source of this information, particularly when it comes to different 511 not a reliable source of this information, particularly when it comes to different
513 operating systems. */ { 512 operating systems. */ {
@@ -533,6 +532,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
533 } 532 }
534 } 533 }
535#endif 534#endif
535 init_Lang();
536 /* Configure the valid command line options. */ { 536 /* Configure the valid command line options. */ {
537 defineValues_CommandLine(&d->args, "close-tab", 0); 537 defineValues_CommandLine(&d->args, "close-tab", 0);
538 defineValues_CommandLine(&d->args, "echo;E", 0); 538 defineValues_CommandLine(&d->args, "echo;E", 0);
diff --git a/src/lang.c b/src/lang.c
index 3e99c09c..d0120798 100644
--- a/src/lang.c
+++ b/src/lang.c
@@ -31,15 +31,16 @@ static void clear_Lang_(iLang *d) {
31 31
32static void load_Lang_(iLang *d, const char *id) { 32static void load_Lang_(iLang *d, const char *id) {
33 /* Load compiled language strings from an embedded blob. */ 33 /* Load compiled language strings from an embedded blob. */
34 const iBlock *data = NULL; // &blobLangEn_Embedded; 34 iUnused(id);
35 const iBlock *data = &blobEn_Embedded;
35 iMsgStr msg; 36 iMsgStr msg;
36 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++) {
37 msg.id = ptr; 38 msg.id = ptr;
38 while (*++ptr) {} 39 while (*++ptr) {}
39 msg.str = ++ptr; 40 msg.str = ++ptr;
40 while (*++ptr) {} 41 while (*++ptr) {}
41 /* Allocate the string. */ 42 /* Allocate the string. The data has already been sorted. */
42 insert_SortedArray(d->messages, &msg); 43 pushBack_Array(&d->messages->values, &msg);
43 } 44 }
44} 45}
45 46
@@ -68,8 +69,8 @@ const char *cstr_Lang(const char *msgId) {
68 if (locate_SortedArray(d->messages, &key, &pos)) { 69 if (locate_SortedArray(d->messages, &key, &pos)) {
69 return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str; 70 return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str;
70 } 71 }
71 //iAssert(iFalse); 72 fprintf(stderr, "[Lang] missing: %s\n", msgId); fflush(stderr);
72 fprintf(stderr, "[Lang] missing: %s\n", msgId); 73 iAssert(iFalse);
73 return msgId; 74 return msgId;
74} 75}
75 76
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index ea70977c..c3bc4392 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -40,6 +40,7 @@ iLocalDef iInt2 padding_(int64_t flags) {
40 40
41struct Impl_LabelWidget { 41struct Impl_LabelWidget {
42 iWidget widget; 42 iWidget widget;
43 iString srcLabel;
43 iString label; 44 iString label;
44 int font; 45 int font;
45 int key; 46 int key;
@@ -357,12 +358,40 @@ void updateSize_LabelWidget(iLabelWidget *d) {
357 } 358 }
358} 359}
359 360
361static void replaceVariables_LabelWidget_(iLabelWidget *d) {
362 for (const char *label = cstr_String(&d->label); *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}
386
360void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { 387void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) {
361 init_Widget(&d->widget); 388 init_Widget(&d->widget);
362 d->font = uiLabel_FontId; 389 d->font = uiLabel_FontId;
363 d->forceFg = none_ColorId; 390 d->forceFg = none_ColorId;
364 d->icon = 0; 391 d->icon = 0;
365 initCStr_String(&d->label, label); 392 initCStr_String(&d->srcLabel, label);
393 initCopy_String(&d->label, &d->srcLabel);
394 replaceVariables_LabelWidget_(d);
366 if (cmd) { 395 if (cmd) {
367 initCStr_String(&d->command, cmd); 396 initCStr_String(&d->command, cmd);
368 } 397 }
@@ -381,6 +410,7 @@ void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) {
381 410
382void deinit_LabelWidget(iLabelWidget *d) { 411void deinit_LabelWidget(iLabelWidget *d) {
383 deinit_String(&d->label); 412 deinit_String(&d->label);
413 deinit_String(&d->srcLabel);
384 deinit_String(&d->command); 414 deinit_String(&d->command);
385} 415}
386 416
@@ -407,11 +437,15 @@ void setAlignVisually_LabelWidget(iLabelWidget *d, iBool alignVisual) {
407 437
408void updateText_LabelWidget(iLabelWidget *d, const iString *text) { 438void updateText_LabelWidget(iLabelWidget *d, const iString *text) {
409 set_String(&d->label, text); 439 set_String(&d->label, text);
440 set_String(&d->srcLabel, text);
441 replaceVariables_LabelWidget_(d);
410 refresh_Widget(&d->widget); 442 refresh_Widget(&d->widget);
411} 443}
412 444
413void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) { 445void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) {
414 setCStr_String(&d->label, text); 446 setCStr_String(&d->label, text);
447 set_String(&d->srcLabel, &d->label);
448 replaceVariables_LabelWidget_(d);
415 refresh_Widget(&d->widget); 449 refresh_Widget(&d->widget);
416} 450}
417 451
diff --git a/src/ui/window.c b/src/ui/window.c
index b8b2853e..2d1deb72 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -188,8 +188,8 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
188#if !defined (iPlatformAppleMobile) 188#if !defined (iPlatformAppleMobile)
189/* TODO: Submenus wouldn't hurt here. */ 189/* TODO: Submenus wouldn't hurt here. */
190static const iMenuItem navMenuItems_[] = { 190static const iMenuItem navMenuItems_[] = {
191 { add_Icon " ${menu.nav.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, 191 { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" },
192 { "${menu.nav.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, 192 { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" },
193 { "---", 0, 0, NULL }, 193 { "---", 0, 0, NULL },
194 { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, 194 { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" },
195 { "${menu.copy.source}", SDLK_c, KMOD_PRIMARY, "copy" }, 195 { "${menu.copy.source}", SDLK_c, KMOD_PRIMARY, "copy" },