Repository: zulip/zulip

Issue:
Translated messages in Handlebars templates should never contain handlebars
When a Handlebars template contains something like this:
```
           {{#tr this}}Your email is {{ page_params.email }}.{{/tr}}
```
it results in the inside expression (`{{ page_params.email }}`) being evaluated and substituted *first*, and only then the result being looked up for translation. That's rarely going to work, and we should just never do it. The correct way to write this, documented in [our frontend i18n docs](http://zulip.readthedocs.io/en/latest/translating.html#frontend-translations), is with a `__`-marked variable to be substituted by `i18next` itself:
```
           {{#tr this}}Your email is __email__.{{/tr}}
```
(That or reword to separate the message from the variable, as say a label for a field; that'd be the right thing in this simple example.)

There are a handful of cases of this today. We should eliminate them and enforce with a linter that we don't make more.



Discussion:
@umairwaheed can you take this one?
Yeah. @zulipbot claim.
Thanks @umairwaheed ! This is a little trickier to deal with than that commit in #5615 will handle, though -- in particular because translated strings can be multi-line.

For example, with `git grep -PA4 '\{\{\#tr[^{]*$'` I find this example in `static/templates/settings/bot-settings.handlebars`:
```
            {{#tr this}}
            Looking for our <a href="/integrations" target="_blank">Integrations</a>
            or <a href="{{ server_uri }}/api" target="_blank">API</a> documentation?
            {{/tr}}
```

I'm also a bit concerned about the exception that had to be added in that commit -- there'll probably be other cases in the future where people have multiple small `{{#tr}}` blocks on the same line, and it's always awkward for a linter to fire a false positive because it's confusing and then it erodes people's trust in the linter.

I think it's enough to have a rule that says, after a `{{#tr}}` block starts, the next handlebars have to be the close of that block. Here's one way to do that with a regex -- it's important that it runs across the whole file, not one line at a time:
```
$ git ls-files | grep handlebars$ | xargs perl -0ne 'if (m, \{\{\s*\#tr (?: [^{] | \{[^{] )* \{\{(?!/tr\}\}) .* ,x) { print "$ARGV:$.:\n"; print "$&\n\n"; }'
static/templates/settings/bot-settings.handlebars:44:
{{#tr this}}
            Looking for our <a href="/integrations" target="_blank">Integrations</a>
            or <a href="{{ server_uri }}/api" target="_blank">API</a> documentation?

static/templates/single_message.handlebars:62:
{{#tr this}}{{#if msg/starred}}Unstar{{else}}Star{{/if}} this message{{/tr}}">

```

Both of those examples should be prohibited, so that's good.

In Python, the `re.VERBOSE` aka `re.X` flag lets you write complex regexes with whitespace for clarity like I did above, or even with comments.

We should be able to handle this somewhat gracefully using our template parser.
Hmm, in that case our `tools/lint` cannot handle this. Like Steve said, I think we should handle this like we handle capitalization. Let me see what I can do.

Resolving patch:
diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py
--- a/tools/linter_lib/custom_check.py
+++ b/tools/linter_lib/custom_check.py
@@ -385,12 +385,6 @@ def custom_check_file(fn, rules, skip_rules=None, max_length=None):
          'description': "Period should be part of the translatable string."},
         {'pattern': "{{/tr}}[\.\?!]",
          'description': "Period should be part of the translatable string."},
-        {'pattern': "{{#tr.*}}.*{{.*{{/tr}}",
-         'exclude_line': set([
-             ('static/templates/subscription_settings.handlebars',
-              '{{#if subscribed }}{{#tr oneself }}Unsubscribe{{/tr}}{{else}}{{#tr oneself }}Subscribe{{/tr}}{{/if}}</button>'),
-         ]),
-         'description': "Translated messages should not contain handlebars."},
     ]
     jinja2_rules = html_rules + [
         {'pattern': "{% endtrans %}[\.\?!]",


Test patch: