Skip to content
Snippets Groups Projects
Commit 4f9f226a authored by Rob Swindell's avatar Rob Swindell :speech_balloon:
Browse files

Start of localization support for JS modules/shells/etc.

Similar to GNU gettext(), by loading gettext.js and enclosing all user-visible
strings in gettext(), a script can get easy/easier support for non-default
(i.e. US-English) locales by loading translated strings from the [JS] section
of ctrl/text.<locale>.ini (if it exists), or customized strings from the [JS]
section of ctrl/text.ini (if it exists).

Results are cached for fast subsequent uses of the same string.

What's *not* supported is different translations (e.g. colorization or
whatever) for the same string used in a different scripts. That could be
added (e.g. use a different section name for script-specific translations),
pretty easily, if desired.
parent 79dd2496
No related branches found
No related tags found
1 merge request!463MRC mods by Codefenix (2024-10-20)
...@@ -9,6 +9,7 @@ require("sbbsdefs.js", "K_UPPER"); ...@@ -9,6 +9,7 @@ require("sbbsdefs.js", "K_UPPER");
require("userdefs.js", "UFLAG_T"); require("userdefs.js", "UFLAG_T");
require("nodedefs.js", "NODE_MAIN"); require("nodedefs.js", "NODE_MAIN");
require("key_defs.js", "KEY_UP"); require("key_defs.js", "KEY_UP");
require("gettext.js", "gettext");
require("text.js", "Pause"); require("text.js", "Pause");
bbs.revert_text(Pause); bbs.revert_text(Pause);
load("termsetup.js"); load("termsetup.js");
...@@ -22,7 +23,7 @@ const main_menu = { ...@@ -22,7 +23,7 @@ const main_menu = {
file: "main", file: "main",
eval: 'bbs.main_cmds++', eval: 'bbs.main_cmds++',
node_action: NODE_MAIN, node_action: NODE_MAIN,
prompt: "\x01-\x01c\xfe \x01b\x01hMain \x01n\x01c\xfe \x01h" + time_code + prompt: "\x01-\x01c\xfe \x01b\x01h" + gettext("Main") + " \x01n\x01c\xfe \x01h" + time_code +
" \x01n\x01c[\x01h@GN@\x01n\x01c] @GRP@\x01\\ [\x01h@SN@\x01n\x01c] @SUB@: \x01n", " \x01n\x01c[\x01h@GN@\x01n\x01c] @GRP@\x01\\ [\x01h@SN@\x01n\x01c] @SUB@: \x01n",
num_input: shell.get_sub_num, num_input: shell.get_sub_num,
slash_num_input: shell.get_grp_num, slash_num_input: shell.get_grp_num,
...@@ -30,14 +31,14 @@ const main_menu = { ...@@ -30,14 +31,14 @@ const main_menu = {
'A': { eval: 'bbs.auto_msg()' }, 'A': { eval: 'bbs.auto_msg()' },
'/A': { exec: 'avatar_chooser.js' '/A': { exec: 'avatar_chooser.js'
,ars: 'ANSI and not GUEST' ,ars: 'ANSI and not GUEST'
,err: '\r\nSorry, only regular users with ANSI terminals can do that.\r\n' }, ,err: '\r\n' + gettext("Sorry, only regular users with ANSI terminals can do that.") + '\r\n' },
'B': { eval: 'bbs.scan_subs(SCAN_BACK)' 'B': { eval: 'bbs.scan_subs(SCAN_BACK)'
,msg: '\r\n\x01c\x01hBrowse/New Message Scan\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Browse/New Message Scan") + '\r\n' },
'C': { eval: 'bbs.chat_sec()' }, 'C': { eval: 'bbs.chat_sec()' },
'D': { eval: 'bbs.user_config(); exit()' }, 'D': { eval: 'bbs.user_config(); exit()' },
'E': { exec: 'email_sec.js' }, 'E': { exec: 'email_sec.js' },
'F': { eval: 'bbs.scan_subs(SCAN_FIND)' 'F': { eval: 'bbs.scan_subs(SCAN_FIND)'
,msg: '\r\n\x01c\x01hFind Text in Messages\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Find Text in Messages") + '\r\n' },
'/F': { eval: 'bbs.scan_subs(SCAN_FIND, /* all */true)' }, '/F': { eval: 'bbs.scan_subs(SCAN_FIND, /* all */true)' },
'G': { eval: 'bbs.text_sec()' }, 'G': { eval: 'bbs.text_sec()' },
'I': { eval: 'shell.main_info()' }, 'I': { eval: 'shell.main_info()' },
...@@ -46,7 +47,7 @@ const main_menu = { ...@@ -46,7 +47,7 @@ const main_menu = {
'/L': { eval: 'bbs.list_nodes()' }, '/L': { eval: 'bbs.list_nodes()' },
'M': { eval: 'bbs.time_bank()' }, 'M': { eval: 'bbs.time_bank()' },
'N': { eval: 'bbs.scan_subs(SCAN_NEW)' 'N': { eval: 'bbs.scan_subs(SCAN_NEW)'
,msg: '\r\n\x01c\x01hNew Message Scan\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("New Message Scan") + '\r\n' },
'/N': { eval: 'bbs.scan_subs(SCAN_NEW, /* all */true)' }, '/N': { eval: 'bbs.scan_subs(SCAN_NEW, /* all */true)' },
'O': { eval: 'shell.logoff(/* fast: */false)' }, 'O': { eval: 'shell.logoff(/* fast: */false)' },
'/O': { eval: 'shell.logoff(/* fast: */true)' }, '/O': { eval: 'shell.logoff(/* fast: */true)' },
...@@ -55,7 +56,7 @@ const main_menu = { ...@@ -55,7 +56,7 @@ const main_menu = {
'Q': { eval: 'bbs.qwk_sec()' }, 'Q': { eval: 'bbs.qwk_sec()' },
'R': { eval: 'bbs.scan_msgs()' }, 'R': { eval: 'bbs.scan_msgs()' },
'S': { eval: 'bbs.scan_subs(SCAN_TOYOU)' 'S': { eval: 'bbs.scan_subs(SCAN_TOYOU)'
,msg: '\r\n\x01c\x01hScan for Messages Posted to You\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Scan for Messages Posted to You") + '\r\n' },
'/S': { eval: 'bbs.scan_subs(SCAN_TOYOU, /* all */true)' }, '/S': { eval: 'bbs.scan_subs(SCAN_TOYOU, /* all */true)' },
'U': { eval: 'shell.list_users()' }, 'U': { eval: 'shell.list_users()' },
'/U': { eval: 'bbs.list_users(UL_ALL)' }, '/U': { eval: 'bbs.list_users(UL_ALL)' },
...@@ -64,15 +65,15 @@ const main_menu = { ...@@ -64,15 +65,15 @@ const main_menu = {
'W': { eval: 'bbs.whos_online()' }, 'W': { eval: 'bbs.whos_online()' },
'X': { eval: 'bbs.xtrn_sec()' }, 'X': { eval: 'bbs.xtrn_sec()' },
'Z': { eval: 'bbs.scan_subs(SCAN_NEW | SCAN_CONT)' 'Z': { eval: 'bbs.scan_subs(SCAN_NEW | SCAN_CONT)'
,msg: '\r\n\x01c\x01hContinuous New Message Scan\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Continuous New Message Scan") + '\r\n' },
'/Z': { eval: 'bbs.scan_subs(SCAN_NEW | SCAN_CONT, /* all */true)' }, '/Z': { eval: 'bbs.scan_subs(SCAN_NEW | SCAN_CONT, /* all */true)' },
'*': { eval: 'shell.show_subs(bbs.curgrp)' }, '*': { eval: 'shell.show_subs(bbs.curgrp)' },
'/*': { eval: 'shell.show_grps()' }, '/*': { eval: 'shell.show_grps()' },
'&': { exec: 'msgscancfg.js' }, '&': { exec: 'msgscancfg.js' },
'!': { eval: 'bbs.menu("sysmain")' '!': { eval: 'bbs.menu("sysmain")'
,ars: 'SYSOP or EXEMPT Q or I or N' }, ,ars: 'SYSOP or EXEMPT Q or I or N' },
'#': { msg: '\r\n\x01c\x01hType the actual number, not the symbol.\r\n' }, '#': { msg: '\r\n\x01c\x01h' + gettext("Type the actual number, not the symbol.") + '\r\n' },
'/#': { msg: '\r\n\x01c\x01hType the actual number, not the symbol.\r\n' }, '/#': { msg: '\r\n\x01c\x01h' + gettext("Type the actual number, not the symbol.") + '\r\n' },
}, },
nav: { nav: {
'\r': { }, '\r': { },
...@@ -101,7 +102,7 @@ const file_menu = { ...@@ -101,7 +102,7 @@ const file_menu = {
file: "transfer", file: "transfer",
eval: 'bbs.file_cmds++', eval: 'bbs.file_cmds++',
node_action: NODE_XFER, node_action: NODE_XFER,
prompt: "\x01-\x01c\xfe \x01b\x01hFile \x01n\x01c\xfe \x01h" + time_code + prompt: "\x01-\x01c\xfe \x01b\x01h" + gettext("File") + " \x01n\x01c\xfe \x01h" + time_code +
" \x01n\x01c(\x01h@LN@\x01n\x01c) @LIB@\x01\\ (\x01h@DN@\x01n\x01c) @DIR@: \x01n", " \x01n\x01c(\x01h@LN@\x01n\x01c) @LIB@\x01\\ (\x01h@DN@\x01n\x01c) @DIR@: \x01n",
num_input: shell.get_dir_num, num_input: shell.get_dir_num,
slash_num_input: shell.get_lib_num, slash_num_input: shell.get_lib_num,
...@@ -109,46 +110,46 @@ const file_menu = { ...@@ -109,46 +110,46 @@ const file_menu = {
'B': { eval: 'bbs.batch_menu()' }, 'B': { eval: 'bbs.batch_menu()' },
'C': { eval: 'bbs.chat_sec()' }, 'C': { eval: 'bbs.chat_sec()' },
'D': { eval: 'shell.download_files()' 'D': { eval: 'shell.download_files()'
,msg: '\r\n\x01c\x01hDownload File(s)\r\n' ,msg: '\r\n\x01c\x01h' + gettext("Download File(s)") + '\r\n'
,ars: 'REST NOT D' }, ,ars: 'REST NOT D' },
'/D': { eval: 'shell.download_user_files()' '/D': { eval: 'shell.download_user_files()'
,msg: '\r\n\x01c\x01hDownload File(s) from User(s)\r\n' ,msg: '\r\n\x01c\x01h' + gettext("Download File(s) from User(s)") + '\r\n'
,ars: 'REST NOT D' }, ,ars: 'REST NOT D' },
'E': { eval: 'shell.view_file_info(FI_INFO)' 'E': { eval: 'shell.view_file_info(FI_INFO)'
,msg: '\r\n\x01c\x01hList Extended File Information\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("List Extended File Information") + '\r\n' },
'F': { eval: 'bbs.scan_dirs(FL_FINDDESC);' 'F': { eval: 'bbs.scan_dirs(FL_FINDDESC);'
,msg: '\r\n\x01c\x01hFind Text in File Descriptions (no wildcards)\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Find Text in File Descriptions (no wildcards)") + '\r\n' },
'/F': { eval: 'bbs.scan_dirs(FL_FINDDESC, /* all: */true);' }, '/F': { eval: 'bbs.scan_dirs(FL_FINDDESC, /* all: */true);' },
'I': { eval: 'shell.file_info()' }, 'I': { eval: 'shell.file_info()' },
'J': { eval: 'shell.select_file_area()' }, 'J': { eval: 'shell.select_file_area()' },
'L': { eval: 'shell.list_files()' }, 'L': { eval: 'shell.list_files()' },
'/L': { eval: 'bbs.list_nodes()' }, '/L': { eval: 'bbs.list_nodes()' },
'N': { eval: 'bbs.scan_dirs(FL_ULTIME)' 'N': { eval: 'bbs.scan_dirs(FL_ULTIME)'
,msg: '\r\n\x01c\x01hNew File Scan\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("New File Scan") + '\r\n' },
'/N': { eval: 'bbs.scan_dirs(FL_ULTIME, /* all */true)' }, '/N': { eval: 'bbs.scan_dirs(FL_ULTIME, /* all */true)' },
'O': { eval: 'shell.logoff(/* fast: */false)' }, 'O': { eval: 'shell.logoff(/* fast: */false)' },
'/O': { eval: 'shell.logoff(/* fast: */true)' }, '/O': { eval: 'shell.logoff(/* fast: */true)' },
'R': { eval: 'shell.view_file_info(FI_REMOVE)' 'R': { eval: 'shell.view_file_info(FI_REMOVE)'
,msg: '\r\n\x01c\x01hRemove/Edit File(s)\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Remove/Edit File(s)") + '\r\n' },
'S': { eval: 'bbs.scan_dirs(FL_NO_HDR)' 'S': { eval: 'bbs.scan_dirs(FL_NO_HDR)'
,msg: '\r\n\x01c\x01hSearch for Filename(s)\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Search for Filename(s)") + '\r\n' },
'/S': { eval: 'bbs.scan_dirs(FL_NO_HDR, /* all */true) ' }, '/S': { eval: 'bbs.scan_dirs(FL_NO_HDR, /* all */true) ' },
'T': { eval: 'bbs.temp_xfer()' }, 'T': { eval: 'bbs.temp_xfer()' },
'U': { eval: 'shell.upload_file()' 'U': { eval: 'shell.upload_file()'
,msg: '\r\n\x01c\x01hUpload File\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Upload File") + '\r\n' },
'/U': { eval: 'shell.upload_user_file()' '/U': { eval: 'shell.upload_user_file()'
,msg: '\r\n\x01c\x01hUpload File to User\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Upload File to User") + '\r\n' },
'V': { eval: 'shell.view_files()' 'V': { eval: 'shell.view_files()'
,msg: '\r\n\x01c\x01hView File(s)\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("View File(s)") + '\r\n' },
'W': { eval: 'bbs.whos_online()' }, 'W': { eval: 'bbs.whos_online()' },
'Z': { eval: 'shell.upload_sysop_file()' 'Z': { eval: 'shell.upload_sysop_file()'
,msg: '\r\n\x01c\x01hUpload File to Sysop\r\n' }, ,msg: '\r\n\x01c\x01h' + gettext("Upload File to Sysop") + '\r\n' },
'*': { eval: 'shell.show_dirs(bbs.curlib)' }, '*': { eval: 'shell.show_dirs(bbs.curlib)' },
'/*': { eval: 'shell.show_libs()' }, '/*': { eval: 'shell.show_libs()' },
'&': { exec: 'filescancfg.js' }, '&': { exec: 'filescancfg.js' },
'!': { eval: 'bbs.menu("sysxfer")' }, '!': { eval: 'bbs.menu("sysxfer")' },
'#': { msg: '\r\n\x01c\x01hType the actual number, not the symbol.\r\n' }, '#': { msg: '\r\n\x01c\x01h' + gettext("Type the actual number, not the symbol.") + '\r\n' },
'/#': { msg: '\r\n\x01c\x01hType the actual number, not the symbol.\r\n' }, '/#': { msg: '\r\n\x01c\x01h' + gettext("Type the actual number, not the symbol.") + '\r\n' },
}, },
nav: { nav: {
'\r': { }, '\r': { },
...@@ -232,9 +233,9 @@ while(bbs.online && !js.terminated) { ...@@ -232,9 +233,9 @@ while(bbs.online && !js.terminated) {
} }
var menu_cmd = menu.command[cmd]; var menu_cmd = menu.command[cmd];
if(!menu_cmd) { if(!menu_cmd) {
console.print("\r\n\x01c\x01hUnrecognized command."); console.print("\r\n\x01c\x01h" + gettext("Unrecognized command."));
if(user.settings & USER_EXPERT) if(user.settings & USER_EXPERT)
console.print(" Hit '\x01i" + help_key + "\x01n\x01c\x01h' for a menu."); console.print(" " + gettext("Hit") + " '\x01i" + help_key + "\x01n\x01c\x01h' " + gettext("for a menu."));
console.crlf(); console.crlf();
continue; continue;
} }
......
// Localized/customized support for JavaScript user-visible text (i.e. strings not contained in text.dat)
// Customized text strings go in the [JS] section of ctrl/text.ini
// Localized (translated to non-default locale) text strings go in the [JS] section of ctrl/text.<lang>.ini
"use strict";
var gettext_cache = {};
function gettext(orig) {
function get_text_from_ini(ini_fname, orig) {
var f = new File(ini_fname);
if(!f.open("r"))
return undefined;
var text = f.iniGetValue("js", orig);
f.close();
return text;
}
if (gettext_cache[orig] !== undefined)
return gettext_cache[orig];
var text;
if (user.lang)
text = get_text_from_ini("text." + user.lang + ".ini", orig);
if (text === undefined)
text = get_text_from_ini("text.ini", orig);
if (text === undefined)
text = orig;
return gettext_cache[orig] = text;
}
this;
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
require("text.js", "DownloadBatchQ"); require("text.js", "DownloadBatchQ");
require("sbbsdefs.js", "USER_EXPERT"); require("sbbsdefs.js", "USER_EXPERT");
require("gettext.js", "gettext");
// Build list of current subs/dirs in each group/library // Build list of current subs/dirs in each group/library
// This hack is required because the 'bbs' object doesn't expose the current // This hack is required because the 'bbs' object doesn't expose the current
...@@ -244,7 +245,7 @@ function main_info() ...@@ -244,7 +245,7 @@ function main_info()
if(!(user.settings & USER_EXPERT)) if(!(user.settings & USER_EXPERT))
bbs.menu("maininfo"); bbs.menu("maininfo");
bbs.nodesync(); bbs.nodesync();
console.print("\r\n\x01y\x01hInfo: \x01n"); console.print("\r\n\x01y\x01h"+ gettext("Info") + ": \x01n");
var key = console.getkeys("?QISVY\r"); var key = console.getkeys("?QISVY\r");
bbs.log_key(key); bbs.log_key(key);
switch(key) { switch(key) {
...@@ -290,7 +291,7 @@ function file_info() ...@@ -290,7 +291,7 @@ function file_info()
if(!(user.settings & USER_EXPERT)) if(!(user.settings & USER_EXPERT))
bbs.menu("xferinfo"); bbs.menu("xferinfo");
bbs.nodesync(); bbs.nodesync();
console.print("\r\n\x01y\x01hInfo: \x01n"); console.print("\r\n\x01y\x01h" + gettext("Info") + ": \x01n");
key=console.getkeys("?TYDUQ\r"); key=console.getkeys("?TYDUQ\r");
bbs.log_key(key); bbs.log_key(key);
...@@ -325,9 +326,9 @@ function file_info() ...@@ -325,9 +326,9 @@ function file_info()
function list_users() function list_users()
{ {
console.print("\r\n\x01c\x01hList Users\r\n"); console.print("\r\n\x01c\x01h" + "List Users" + "\r\n");
console.mnemonics("\r\n~Logons Today, ~Yesterday, ~Sub-board, or ~All: "); console.mnemonics("\r\n~" + gettext("Logons Today") + ", ~" + gettext("Yesterday") + ", ~" + gettext("Sub-board") + ", " + gettext("or") + " ~@All@: ");
switch(console.getkeys("LSAY\r")) { switch(console.getkeys("LSY\r" + console.all_key)) {
case 'L': case 'L':
bbs.list_logons(); bbs.list_logons();
break; break;
...@@ -337,7 +338,7 @@ function list_users() ...@@ -337,7 +338,7 @@ function list_users()
case 'S': case 'S':
bbs.list_users(UL_SUB); bbs.list_users(UL_SUB);
break; break;
case 'A': case console.all_key:
bbs.list_users(UL_ALL); bbs.list_users(UL_ALL);
break; break;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment