2021-02-10 17:05:00 +01:00
|
|
|
import Handlebars from "handlebars/runtime";
|
2020-08-01 03:43:15 +02:00
|
|
|
|
2021-04-10 05:53:13 +02:00
|
|
|
import {default_html_elements, intl} from "./i18n";
|
2021-02-10 17:05:00 +01:00
|
|
|
import * as util from "./util";
|
2020-02-28 23:59:07 +01:00
|
|
|
|
2019-06-25 11:39:03 +02:00
|
|
|
// Below, we register Zulip-specific extensions to the handlebars API.
|
|
|
|
//
|
|
|
|
// IMPORTANT: When adding a new handlebars helper, update the
|
|
|
|
// knownHelpers array in the webpack config so that webpack knows your
|
|
|
|
// helper is registered at runtime and don't try to require them when
|
|
|
|
// bundling.
|
|
|
|
|
2013-08-27 20:17:42 +02:00
|
|
|
// We don't want to wait for DOM ready to register the Handlebars helpers
|
|
|
|
// below. There's no need to, as they do not access the DOM.
|
|
|
|
// Furthermore, waiting for DOM ready would introduce race conditions with
|
|
|
|
// other DOM-ready callbacks that attempt to render templates.
|
|
|
|
|
2019-06-25 02:54:18 +02:00
|
|
|
Handlebars.registerHelper({
|
2020-07-20 22:18:43 +02:00
|
|
|
eq(a, b) {
|
2020-07-15 00:34:28 +02:00
|
|
|
return a === b;
|
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
and(...args) {
|
2020-02-12 01:35:16 +01:00
|
|
|
args.pop(); // Handlebars options
|
|
|
|
if (args.length === 0) {
|
2019-06-25 02:54:18 +02:00
|
|
|
return true;
|
2013-08-13 18:08:12 +02:00
|
|
|
}
|
2020-02-12 01:35:16 +01:00
|
|
|
const last = args.pop();
|
|
|
|
for (const arg of args) {
|
|
|
|
if (!arg || Handlebars.Utils.isEmpty(arg)) {
|
|
|
|
return arg;
|
2019-06-25 02:54:18 +02:00
|
|
|
}
|
2014-02-13 21:16:43 +01:00
|
|
|
}
|
2020-02-12 01:35:16 +01:00
|
|
|
return last;
|
2019-06-25 02:54:18 +02:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
or(...args) {
|
2020-02-12 01:35:16 +01:00
|
|
|
args.pop(); // Handlebars options
|
|
|
|
if (args.length === 0) {
|
2019-06-25 02:54:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-12 01:35:16 +01:00
|
|
|
const last = args.pop();
|
|
|
|
for (const arg of args) {
|
|
|
|
if (arg && !Handlebars.Utils.isEmpty(arg)) {
|
|
|
|
return arg;
|
2019-06-25 02:54:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-02-12 01:35:16 +01:00
|
|
|
return last;
|
2019-06-25 02:54:18 +02:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
not(a) {
|
2020-07-15 00:34:28 +02:00
|
|
|
return !a || Handlebars.Utils.isEmpty(a);
|
|
|
|
},
|
2018-09-02 09:38:09 +02:00
|
|
|
});
|
|
|
|
|
2021-04-10 05:53:13 +02:00
|
|
|
Handlebars.registerHelper("t", (message) => {
|
2016-05-13 12:44:03 +02:00
|
|
|
// Marks a string for translation.
|
|
|
|
// Example usage:
|
|
|
|
// {{t "some English text"}}
|
templates: Cache translations.
For Manage Streams, when we render the subscriptions
template, a significant amount of time is taken
by the "t" helper.
Obviously for the first call, we expect "t" to be
somewhat expensive, but subsuquent calls should be
fast, but i18next seems to have some overhead.
Also, we can save a tiny bit of overhead (marking it
as a safe string) that comes from our helper.
As an aside, are we sure it's ok to mark translations
as safe strings?
To test before and after, use blueslip.timings before
and after this commit. When I tested with about 300
streams, the difference is pretty striking:
without cache: 100ms
with cache: 20ms
This is particularly interesting, since the subscriptions
templates have long strings for things like the SVG-based
checkmarks, but they're not really the bottleneck.
Unfortunately, this doesn't seem to be a huge win
elsewhere. In some places we don't call "t", but of
course those might change in the future and benefit from
the cache. And in other places we have smart widgets
that avoid rendering all N objects at one (e.g. buddy
list and list_render).
So this might be too big a hammer to speed up one
screen (albeit a really slow one). It's possible
that we should simply move the i18n.t step **outside**
of certain templates to avoid doing them in a loop.
2020-01-15 15:05:17 +01:00
|
|
|
|
2021-04-10 05:53:13 +02:00
|
|
|
const descriptor = {id: message, defaultMessage: message};
|
|
|
|
return intl.formatMessage(descriptor);
|
2016-05-13 12:44:03 +02:00
|
|
|
});
|
|
|
|
|
2021-04-14 03:04:02 +02:00
|
|
|
Handlebars.registerHelper("tr", function (options) {
|
2016-05-13 12:44:03 +02:00
|
|
|
// Marks a block for translation.
|
|
|
|
// Example usage 1:
|
2021-04-14 03:04:02 +02:00
|
|
|
// {{#tr}}
|
2016-05-13 12:44:03 +02:00
|
|
|
// <p>some English text</p>
|
|
|
|
// {{/tr}}
|
|
|
|
//
|
|
|
|
// Example usage 2:
|
2021-04-14 03:04:02 +02:00
|
|
|
// {{#tr}}
|
|
|
|
// <p>This {variable} will get value from the current context</p>
|
2016-05-13 12:44:03 +02:00
|
|
|
// {{/tr}}
|
|
|
|
//
|
2021-04-14 03:04:02 +02:00
|
|
|
// Note: use `{` and `}` instead of `{{` and `}}` to declare
|
|
|
|
// variables.
|
2021-04-10 05:53:13 +02:00
|
|
|
const message = options
|
2021-04-14 03:04:02 +02:00
|
|
|
.fn(this)
|
2021-04-10 05:53:13 +02:00
|
|
|
.trim()
|
|
|
|
.split("\n")
|
|
|
|
.map((s) => s.trim())
|
|
|
|
.join(" ");
|
|
|
|
const descriptor = {id: message, defaultMessage: message};
|
|
|
|
const result = intl.formatMessage(descriptor, {
|
|
|
|
...default_html_elements,
|
|
|
|
...Object.fromEntries(
|
|
|
|
Object.entries(options.fn.partials ?? {}).map(([name, value]) => [
|
|
|
|
name,
|
|
|
|
(s) => value(this, {data: {"partial-block": () => s.join("")}}),
|
|
|
|
]),
|
|
|
|
),
|
|
|
|
...Object.fromEntries(
|
2021-04-14 03:04:02 +02:00
|
|
|
Object.entries(this ?? {}).map(([key, value]) => [
|
2021-04-10 05:53:13 +02:00
|
|
|
key,
|
|
|
|
Handlebars.Utils.escapeExpression(value),
|
|
|
|
]),
|
|
|
|
),
|
|
|
|
});
|
2016-05-13 12:44:03 +02:00
|
|
|
return new Handlebars.SafeString(result);
|
|
|
|
});
|
|
|
|
|
2020-02-28 23:59:07 +01:00
|
|
|
Handlebars.registerHelper(
|
|
|
|
"rendered_markdown",
|
2020-07-02 02:16:03 +02:00
|
|
|
(content) => new Handlebars.SafeString(util.clean_user_content_links(content)),
|
2020-02-28 23:59:07 +01:00
|
|
|
);
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
Handlebars.registerHelper("numberFormat", (number) => number.toLocaleString());
|