diff --git a/CHANGELOG.md b/CHANGELOG.md
index 428a60ae..7c3f8045 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
# PrivateBin version history
* **1.4 (not yet released)**
- * ADDED: Translation for Estonian
+ * ADDED: Translations for Estonian and Lojban
* ADDED: new HTTP headers improving security (#765)
* ADDED: Download button for paste text (#774)
* ADDED: Opt-out of federated learning of cohorts (FLoC) (#776)
@@ -14,7 +14,7 @@
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419)
* CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419)
* **1.3.5 (2021-04-05)**
- * ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan
+ * ADDED: Translations for Hebrew, Lithuanian, Indonesian and Catalan
* ADDED: Make the project info configurable (#681)
* CHANGED: Upgrading libraries to: DOMpurify 2.2.7, kjua 0.9.0 & random_compat 2.0.18
* CHANGED: Open all links in new window (#630)
diff --git a/CREDITS.md b/CREDITS.md
index 6c2f647c..de0ebe81 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -54,3 +54,4 @@ Sébastien Sauvage - original idea and main developer
* whenwesober - Indonesian
* retiolus - Catalan
* sarnane - Estonian
+* foxsouns - Lojban
diff --git a/i18n/jbo.json b/i18n/jbo.json
new file mode 100644
index 00000000..cf04d978
--- /dev/null
+++ b/i18n/jbo.json
@@ -0,0 +1,189 @@
+{
+ "PrivateBin": "sivbaktu",
+ "%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "la %s basti lo sorcu lo'e se setca kibro .i ji'a zo'e se zancari gi'e fingubni .i lo samse'u na djuno lo datni selru'e cu .i Data is encrypted/decrypted %sin the browser%s using 256 bits AES.",
+ "More information on the project page.": "More information on the project page.",
+ "Because ignorance is bliss": ".i ki'u le ka na djuno cu ka saxfri",
+ "en": "jb",
+ "Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.",
+ "%s requires php %s or above to work. Sorry.": "%s requires php %s or above to work. Sorry.",
+ "%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.",
+ "Please wait %d seconds between each post.": [
+ "Please wait %d second between each post. (singular)",
+ "Please wait %d seconds between each post. (1st plural)",
+ "Please wait %d seconds between each post. (2nd plural)",
+ "Please wait %d seconds between each post. (3rd plural)"
+ ],
+ "Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.",
+ "Invalid data.": ".i le selru'e cu na drani",
+ "You are unlucky. Try again.": "You are unlucky. Try again.",
+ "Error saving comment. Sorry.": "Error saving comment. Sorry.",
+ "Error saving paste. Sorry.": "Error saving paste. Sorry.",
+ "Invalid paste ID.": "Invalid paste ID.",
+ "Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.",
+ "Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.",
+ "Paste was properly deleted.": "Paste was properly deleted.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
+ "%s requires a modern browser to work.": "%s requires a modern browser to work.",
+ "New": "cnino",
+ "Send": "benji",
+ "Clone": "fukpi",
+ "Raw text": "vlapoi nalselrucyzu'e",
+ "Expires": "Expires",
+ "Burn after reading": "Burn after reading",
+ "Open discussion": "Open discussion",
+ "Password (recommended)": "Password (recommended)",
+ "Discussion": "Discussion",
+ "Toggle navigation": "Toggle navigation",
+ "%d seconds": [
+ "%d second (singular)",
+ "%d seconds (1st plural)",
+ "%d seconds (2nd plural)",
+ "%d seconds (3rd plural)"
+ ],
+ "%d minutes": [
+ "%d minute (singular)",
+ "%d minutes (1st plural)",
+ "%d minutes (2nd plural)",
+ "%d minutes (3rd plural)"
+ ],
+ "%d hours": [
+ "%d hour (singular)",
+ "%d hours (1st plural)",
+ "%d hours (2nd plural)",
+ "%d hours (3rd plural)"
+ ],
+ "%d days": [
+ "%d day (singular)",
+ "%d days (1st plural)",
+ "%d days (2nd plural)",
+ "%d days (3rd plural)"
+ ],
+ "%d weeks": [
+ "%d week (singular)",
+ "%d weeks (1st plural)",
+ "%d weeks (2nd plural)",
+ "%d weeks (3rd plural)"
+ ],
+ "%d months": [
+ "%d month (singular)",
+ "%d months (1st plural)",
+ "%d months (2nd plural)",
+ "%d months (3rd plural)"
+ ],
+ "%d years": [
+ "%d year (singular)",
+ "%d years (1st plural)",
+ "%d years (2nd plural)",
+ "%d years (3rd plural)"
+ ],
+ "Never": "Never",
+ "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.",
+ "This document will expire in %d seconds.": [
+ "This document will expire in %d second. (singular)",
+ "This document will expire in %d seconds. (1st plural)",
+ "This document will expire in %d seconds. (2nd plural)",
+ "This document will expire in %d seconds. (3rd plural)"
+ ],
+ "This document will expire in %d minutes.": [
+ "This document will expire in %d minute. (singular)",
+ "This document will expire in %d minutes. (1st plural)",
+ "This document will expire in %d minutes. (2nd plural)",
+ "This document will expire in %d minutes. (3rd plural)"
+ ],
+ "This document will expire in %d hours.": [
+ "This document will expire in %d hour. (singular)",
+ "This document will expire in %d hours. (1st plural)",
+ "This document will expire in %d hours. (2nd plural)",
+ "This document will expire in %d hours. (3rd plural)"
+ ],
+ "This document will expire in %d days.": [
+ "This document will expire in %d day. (singular)",
+ "This document will expire in %d days. (1st plural)",
+ "This document will expire in %d days. (2nd plural)",
+ "This document will expire in %d days. (3rd plural)"
+ ],
+ "This document will expire in %d months.": [
+ "This document will expire in %d month. (singular)",
+ "This document will expire in %d months. (1st plural)",
+ "This document will expire in %d months. (2nd plural)",
+ "This document will expire in %d months. (3rd plural)"
+ ],
+ "Please enter the password for this paste:": "Please enter the password for this paste:",
+ "Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)",
+ "Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.",
+ "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.",
+ "Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?",
+ "Reply": "Reply",
+ "Anonymous": "Anonymous",
+ "Avatar generated from IP address": "Avatar generated from IP address",
+ "Add comment": "Add comment",
+ "Optional nickname…": "Optional nickname…",
+ "Post comment": "Post comment",
+ "Sending comment…": "Sending comment…",
+ "Comment posted.": "Comment posted.",
+ "Could not refresh display: %s": "Could not refresh display: %s",
+ "unknown status": "unknown status",
+ "server error or not responding": "server error or not responding",
+ "Could not post comment: %s": "Could not post comment: %s",
+ "Sending paste…": "Sending paste…",
+ "Your paste is %s(Hit [Ctrl]+[c] to copy)": "Your paste is %s(Hit [Ctrl]+[c] to copy)",
+ "Delete data": "Delete data",
+ "Could not create paste: %s": "Could not create paste: %s",
+ "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)",
+ "B": "B",
+ "KiB": "KiB",
+ "MiB": "MiB",
+ "GiB": "GiB",
+ "TiB": "TiB",
+ "PiB": "PiB",
+ "EiB": "EiB",
+ "ZiB": "ZiB",
+ "YiB": "YiB",
+ "Format": "Format",
+ "Plain Text": "Plain Text",
+ "Source Code": "Source Code",
+ "Markdown": "Markdown",
+ "Download attachment": "Download attachment",
+ "Cloned: '%s'": "Cloned: '%s'",
+ "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
+ "Attach a file": "Attach a file",
+ "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+ "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
+ "Remove attachment": "Remove attachment",
+ "Your browser does not support uploading encrypted files. Please use a newer browser.": "Your browser does not support uploading encrypted files. Please use a newer browser.",
+ "Invalid attachment.": "Invalid attachment.",
+ "Options": "Options",
+ "Shorten URL": "Shorten URL",
+ "Editor": "Editor",
+ "Preview": "Preview",
+ "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
+ "Decrypt": "Decrypt",
+ "Enter password": "Enter password",
+ "Loading…": "Loading…",
+ "Decrypting paste…": "Decrypting paste…",
+ "Preparing new paste…": "Preparing new paste…",
+ "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "In case this message never disappears please have a look at this FAQ for information to troubleshoot.",
+ "+++ no paste text +++": "+++ no paste text +++",
+ "Could not get paste data: %s": "Could not get paste data: %s",
+ "QR code": "QR code",
+ "This website is using an insecure HTTP connection! Please use it only for testing.": "This website is using an insecure HTTP connection! Please use it only for testing.",
+ "For more information see this FAQ entry.": "For more information see this FAQ entry.",
+ "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.",
+ "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
+ "waiting on user to provide a password": "waiting on user to provide a password",
+ "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
+ "Retry": "Retry",
+ "Showing raw text…": "Showing raw text…",
+ "Notice:": "Notice:",
+ "This link will expire after %s.": "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.": "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:": "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?": "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone": "Use Current Timezone",
+ "Convert To UTC": "Convert To UTC",
+ "Close": "Close",
+ "Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
+ "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
+ "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
+ "Save paste": "Save paste"
+}
diff --git a/i18n/languages.json b/i18n/languages.json
index a2aff4a9..2d7341dc 100644
--- a/i18n/languages.json
+++ b/i18n/languages.json
@@ -89,6 +89,7 @@
"ku": ["Kurdî", "Kurdish"],
"kj": ["Kuanyama", "Kwanyama"],
"la": ["lingua latina", "Latin"],
+ "jbo":["jbobau", "Lojban"],
"lb": ["Lëtzebuergesch", "Luxembourgish"],
"lg": ["Luganda", "Ganda"],
"li": ["Limburgs", "Limburgish"],
diff --git a/js/privatebin.js b/js/privatebin.js
index 6218700a..ef030fb3 100644
--- a/js/privatebin.js
+++ b/js/privatebin.js
@@ -601,7 +601,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @prop {string[]}
* @readonly
*/
- const supportedLanguages = ['bg', 'ca', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
+ const supportedLanguages = ['bg', 'ca', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
/**
* built in language
@@ -785,6 +785,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
case 'he':
return n === 1 ? 0 : (n === 2 ? 1 : ((n < 0 || n > 10) && (n % 10 === 0) ? 2 : 3));
case 'id':
+ case 'jbo':
return 0;
case 'lt':
return n % 10 === 1 && n % 100 !== 11 ? 0 : ((n % 10 >= 2 && n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
@@ -5404,8 +5405,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
node.setAttribute('target', '_blank');
}
// set non-HTML/MathML links to xlink:show=new
- if (!node.hasAttribute('target')
- && (node.hasAttribute('xlink:href')
+ if (!node.hasAttribute('target')
+ && (node.hasAttribute('xlink:href')
|| node.hasAttribute('href'))) {
node.setAttribute('xlink:show', 'new');
}
diff --git a/lib/I18n.php b/lib/I18n.php
index 50bf0ccf..bc8b765c 100644
--- a/lib/I18n.php
+++ b/lib/I18n.php
@@ -195,7 +195,7 @@ class I18n
if (count(self::$_availableLanguages) == 0) {
$i18n = dir(self::_getPath());
while (false !== ($file = $i18n->read())) {
- if (preg_match('/^([a-z]{2}).json$/', $file, $match) === 1) {
+ if (preg_match('/^([a-z]{2,3}).json$/', $file, $match) === 1) {
self::$_availableLanguages[] = $match[1];
}
}
@@ -324,6 +324,7 @@ class I18n
case 'he':
return $n === 1 ? 0 : ($n === 2 ? 1 : (($n < 0 || $n > 10) && ($n % 10 === 0) ? 2 : 3));
case 'id':
+ case 'jbo':
return 0;
case 'lt':
return $n % 10 === 1 && $n % 100 !== 11 ? 0 : (($n % 10 >= 2 && $n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php
index 6f83893a..650504db 100644
--- a/tpl/bootstrap.php
+++ b/tpl/bootstrap.php
@@ -72,7 +72,7 @@ endif;
?>
-
+
diff --git a/tpl/page.php b/tpl/page.php
index bc9641ec..f54df219 100644
--- a/tpl/page.php
+++ b/tpl/page.php
@@ -50,7 +50,7 @@ endif;
?>
-
+
diff --git a/tst/Bootstrap.php b/tst/Bootstrap.php
index 70aafddd..5b6012f0 100644
--- a/tst/Bootstrap.php
+++ b/tst/Bootstrap.php
@@ -304,7 +304,7 @@ class StorageObjectStub extends StorageObject
$this->_generation = $generation;
$this->_info = $info;
$this->_connection = $connection;
- $timeCreated = new Datetime();
+ $timeCreated = new DateTime();
$this->_info['metadata']['timeCreated'] = $timeCreated->format('Y-m-d\TH:i:s.u\Z');
}