Repository: frappe/frappe
Pull request: refactor: Replace template syntax for translations
- Replace invalid translation syntax usages with the correct one. (Template strings in translation strings are invalid)
- Updated translation linter to warn about template string as invalid syntax.
	- Now linter checks for correctness of syntax if the string starts with the following substrings
		<img width="247" alt="Screenshot 2020-10-12 at 11 56 48 AM" src="https://user-images.githubusercontent.com/13928957/95712416-54a03780-0c82-11eb-821e-4d5822c88e4c.png">



Problem statement:
Translation Tool does not pick up ``-denoted strings
There are about 180 translatable strings in the javascript files of frappe and erpnext that use this notation (with ``): 

```
__(`Translate me!`)
```

However the translation tool does not pick them up. It seems to look only for strings like `__('Translate me!')` or `__("Translate me!")`. This means that the respective 180 strings cannot be translated, currently.

Hints from issue discussion:

Commit message: fix: Replace incorrect translation syntax usages

Patch to review:
diff --git a/.github/helper/translation.py b/.github/helper/translation.py
index 5d33355a1b17..ee04de916180 100644
--- a/.github/helper/translation.py
+++ b/.github/helper/translation.py
@@ -3,7 +3,7 @@
 
 errors_encounter = 0
 pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
-start_pattern = re.compile(r"_{1,2}\([\"']{1,3}")
+start_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}")
 
 # skip first argument
 files = sys.argv[1:]
diff --git a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js
index c3cf701d92da..0898fcf4e7b8 100644
--- a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js
+++ b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js
@@ -30,7 +30,7 @@ frappe.ui.form.on('Data Migration Connector', {
 							frm.set_value('connector_type', 'Custom');
 							frm.set_value('python_module', r.message);
 							frm.save();
-							frappe.show_alert(__(`New module created ${r.message}`));
+							frappe.show_alert(__("New module created {0}", [r.message]));
 							d.hide();
 						}
 					});
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
index 7f26bd910147..d763ce5009f6 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
@@ -169,7 +169,7 @@ frappe.ui.form.on('Dashboard Chart', {
 					frm.field_options = frappe.report_utils.get_field_options_from_report(data.columns, data);
 					frm.set_df_property('x_field', 'options', frm.field_options.non_numeric_fields);
 					if (!frm.field_options.numeric_fields.length) {
-						frappe.msgprint(__(`Report has no numeric fields, please change the Report Name`));
+						frappe.msgprint(__("Report has no numeric fields, please change the Report Name"));
 					} else {
 						let y_field_df = frappe.meta.get_docfield('Dashboard Chart Field', 'y_field', frm.doc.name);
 						y_field_df.options = frm.field_options.numeric_fields;
diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js
index 63b41b956e52..6d1454a2cb52 100644
--- a/frappe/desk/doctype/number_card/number_card.js
+++ b/frappe/desk/doctype/number_card/number_card.js
@@ -207,7 +207,7 @@ frappe.ui.form.on('Number Card', {
 				frm.field_options = frappe.report_utils.get_field_options_from_report(data.columns, data);
 				frm.set_df_property('report_field', 'options', frm.field_options.numeric_fields);
 				if (!frm.field_options.numeric_fields.length) {
-					frappe.msgprint(__(`Report has no numeric fields, please change the Report Name`));
+					frappe.msgprint(__("Report has no numeric fields, please change the Report Name"));
 				}
 			} else {
 				frappe.msgprint(__('Report has no data, please modify the filters or change the Report Name'));
diff --git a/frappe/desk/page/user_profile/user_profile.js b/frappe/desk/page/user_profile/user_profile.js
index 646c31f7a161..58d405479a17 100644
--- a/frappe/desk/page/user_profile/user_profile.js
+++ b/frappe/desk/page/user_profile/user_profile.js
@@ -360,11 +360,12 @@ class UserProfile {
 
 		this.get_user_rank().then(() => {
 			this.get_user_points().then(() => {
-				let html = $(__(`<p class="user-energy-points text-muted">${__('Energy Points: ')}<span class="rank">{0}</span></p>
-					<p class="user-energy-points text-muted">${__('Review Points: ')}<span class="rank">{1}</span></p>
-					<p class="user-energy-points text-muted">${__('Rank: ')}<span class="rank">{2}</span></p>
-					<p class="user-energy-points text-muted">${__('Monthly Rank: ')}<span class="rank">{3}</span></p>
-				`, [this.energy_points, this.review_points, this.rank, this.month_rank]));
+				let html = $(`
+					<p class="user-energy-points text-muted">${__('Energy Points:')} <span class="rank">${this.energy_points}</span></p>
+					<p class="user-energy-points text-muted">${__('Review Points:')} <span class="rank">${this.review_points}</span></p>
+					<p class="user-energy-points text-muted">${__('Rank:')} <span class="rank">${this.rank}</span></p>
+					<p class="user-energy-points text-muted">${__('Monthly Rank:')} <span class="rank">${this.month_rank}</span></p>
+				`);
 
 				$profile_details.append(html);
 			});
diff --git a/frappe/public/js/frappe/form/controls/dynamic_link.js b/frappe/public/js/frappe/form/controls/dynamic_link.js
index 02e970091e7a..92d7dfeb40a0 100644
--- a/frappe/public/js/frappe/form/controls/dynamic_link.js
+++ b/frappe/public/js/frappe/form/controls/dynamic_link.js
@@ -1,14 +1,12 @@
 frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({
 	get_options: function() {
 		let options = '';
-		if(this.df.get_options) {
+		if (this.df.get_options) {
 			options = this.df.get_options();
-		}
-		else if (this.docname==null && cur_dialog) {
+		} else if (this.docname==null && cur_dialog) {
 			//for dialog box
 			options = cur_dialog.get_value(this.df.options);
-		}
-		else if (!cur_frm) {
+		} else if (!cur_frm) {
 			const selector = `input[data-fieldname="${this.df.options}"]`;
 			let input = null;
 			if (cur_list) {
@@ -21,13 +19,12 @@ frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({
 			if (input) {
 				options = input.val();
 			}
-		}
-		else {
+		} else {
 			options = frappe.model.get_value(this.df.parent, this.docname, this.df.options);
 		}
 
 		if (frappe.model.is_single(options)) {
-			frappe.throw(__(`${options.bold()} is not a valid DocType for Dynamic Link`));
+			frappe.throw(__("{} is not a valid DocType for Dynamic Link", [options.bold()]));
 		}
 
 		return options;
diff --git a/frappe/public/js/frappe/form/sidebar/review.js b/frappe/public/js/frappe/form/sidebar/review.js
index 2cf2980bf705..b321e3d3d549 100644
--- a/frappe/public/js/frappe/form/sidebar/review.js
+++ b/frappe/public/js/frappe/form/sidebar/review.js
@@ -78,7 +78,6 @@ frappe.ui.form.Review = class Review {
 	}
 	show_review_dialog() {
 		const user_options = this.get_involved_users();
-		const doc_owner = this.frm.doc.owner;
 		const review_dialog = new frappe.ui.Dialog({
 			'title': __('Add Review'),
 			'fields': [{
@@ -106,7 +105,7 @@ frappe.ui.form.Review = class Review {
 				fieldtype: 'Int',
 				label: __('Points'),
 				reqd: 1,
-				description: __(`Currently you have ${this.points.review_points} review points`)
+				description: __("Currently you have {0} review points", [this.points.review_points])
 			}, {
 				fieldtype: 'Small Text',
 				fieldname: 'reason',
@@ -181,7 +180,7 @@ frappe.ui.form.Review = class Review {
 			trigger: 'hover',
 			delay: 500,
 			placement: 'top',
-			template:`
+			template: `
 				<div class="review-popover popover">
 					<div class="arrow"></div>
 					<div class="popover-content"></div>
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 4d8121ebd67f..f5dbe0fcb141 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -33,7 +33,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
 
 		if (!this.has_permissions()) {
 			frappe.set_route('');
-			frappe.msgprint(__(`Not permitted to view ${this.doctype}`));
+			frappe.msgprint(__("Not permitted to view {0}", [this.doctype]));
 			return;
 		}
 
diff --git a/frappe/public/js/frappe/ui/capture.js b/frappe/public/js/frappe/ui/capture.js
index 2161970bf7d7..f08257d39418 100644
--- a/frappe/public/js/frappe/ui/capture.js
+++ b/frappe/public/js/frappe/ui/capture.js
@@ -3,155 +3,139 @@
 
 /**
  * @description Converts a canvas, image or a video to a data URL string.
- * 
+ *
  * @param 	{HTMLElement} element - canvas, img or video.
  * @returns {string} 			  - The data URL string.
- * 
+ *
  * @example
  * frappe._.get_data_uri(video)
  * // returns "data:image/pngbase64,..."
  */
-frappe._.get_data_uri = element =>
-{
-	const $element = $(element)
-	const width    = $element.width()
-	const height   = $element.height()
+frappe._.get_data_uri = element => {
+	const $element = $(element);
+	const width = $element.width();
+	const height = $element.height();
 
-	const $canvas     = $('<canvas/>')
-	$canvas[0].width  = width
-	$canvas[0].height = height
+	const $canvas = $('<canvas/>');
+	$canvas[0].width = width;
+	$canvas[0].height = height;
 
-	const context     = $canvas[0].getContext('2d')
-	context.drawImage($element[0], 0, 0, width, height)
-	
-	const data_uri = $canvas[0].toDataURL('image/png')
+	const context = $canvas[0].getContext('2d');
+	context.drawImage($element[0], 0, 0, width, height);
 
-	return data_uri
-}
+	const data_uri = $canvas[0].toDataURL('image/png');
+
+	return data_uri;
+};
 
 /**
  * @description Frappe's Capture object.
- * 
+ *
  * @example
  * const capture = frappe.ui.Capture()
  * capture.show()
- * 
+ *
  * capture.click((data_uri) => {
  * 	// do stuff
  * })
- * 
+ *
  * @see https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos
  */
-frappe.ui.Capture = class
-{
-	constructor (options = { })
-	{
-		this.options = frappe.ui.Capture.OPTIONS
-		this.set_options(options)
+frappe.ui.Capture = class {
+	constructor(options = {}) {
+		this.options = frappe.ui.Capture.OPTIONS;
+		this.set_options(options);
 	}
-	
-	set_options (options)
-	{
-		this.options = { ...frappe.ui.Capture.OPTIONS, ...options }
-		
-		return this
+
+	set_options(options) {
+		this.options = { ...frappe.ui.Capture.OPTIONS, ...options };
+
+		return this;
 	}
-	
-	render ( )
-	{
-		return navigator.mediaDevices.getUserMedia({ video: true }).then(stream =>
-		{
-			this.dialog 	 = new frappe.ui.Dialog({
-				  title: this.options.title,
+
+	render() {
+		return navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
+			this.dialog = new frappe.ui.Dialog({
+				title: this.options.title,
 				animate: this.options.animate,
-				 action:
-				{
-					secondary:
-					{
-						label: "<b>&times</b>"
+				action: {
+					secondary: {
+						label: '<b>&times</b>'
 					}
 				}
-			})
-	
-			const $e 		 = $(frappe.ui.Capture.TEMPLATE)
-			
-			const video      = $e.find('video')[0]
-			video.srcObject  = stream
-			video.play()
-			
-			const $container = $(this.dialog.body)
-			$container.html($e)
-			
-			$e.find('.fc-btf').hide()
-
-			$e.find('.fc-bcp').click(() =>
-			{
-				const data_url = frappe._.get_data_uri(video)
-				$e.find('.fc-p').attr('src', data_url)
-
-				$e.find('.fc-s').hide()
-				$e.find('.fc-p').show()
-
-				$e.find('.fc-btu').hide()
-				$e.find('.fc-btf').show()
-			})
+			});
 
-			$e.find('.fc-br').click(() =>
-			{
-				$e.find('.fc-p').hide()
-				$e.find('.fc-s').show()
+			const $e = $(frappe.ui.Capture.TEMPLATE);
 
-				$e.find('.fc-btf').hide()
-				$e.find('.fc-btu').show()
-			})
+			const video = $e.find('video')[0];
+			video.srcObject = stream;
+			video.play();
 
-			$e.find('.fc-bs').click(() =>
-			{
-				const data_url = frappe._.get_data_uri(video)
-				this.hide()
-				
-				if (this.callback)
-					this.callback(data_url)
-			})
-		})
+			const $container = $(this.dialog.body);
+			$container.html($e);
+
+			$e.find('.fc-btf').hide();
+
+			$e.find('.fc-bcp').click(() => {
+				const data_url = frappe._.get_data_uri(video);
+				$e.find('.fc-p').attr('src', data_url);
+
+				$e.find('.fc-s').hide();
+				$e.find('.fc-p').show();
+
+				$e.find('.fc-btu').hide();
+				$e.find('.fc-btf').show();
+			});
+
+			$e.find('.fc-br').click(() => {
+				$e.find('.fc-p').hide();
+				$e.find('.fc-s').show();
+
+				$e.find('.fc-btf').hide();
+				$e.find('.fc-btu').show();
+			});
+
+			$e.find('.fc-bs').click(() => {
+				const data_url = frappe._.get_data_uri(video);
+				this.hide();
+
+				if (this.callback) this.callback(data_url);
+			});
+		});
 	}
 
-	show ( )
-	{
-		this.render().then(() =>
-		{
-			this.dialog.show()
-		}).catch(err => {
-			if ( this.options.error )
-			{
-				const alert = `<span class="indicator red"/> ${frappe.ui.Capture.ERR_MESSAGE}`
-				frappe.show_alert(alert, 3)
-			}
-
-			throw err
-		})
+	show() {
+		this.render()
+			.then(() => {
+				this.dialog.show();
+			})
+			.catch(err => {
+				if (this.options.error) {
+					const alert = `<span class="indicator red"/> ${
+						frappe.ui.Capture.ERR_MESSAGE
+					}`;
+					frappe.show_alert(alert, 3);
+				}
+
+				throw err;
+			});
 	}
 
-	hide ( )
-	{
-		if ( this.dialog )
-			this.dialog.hide()
+	hide() {
+		if (this.dialog) this.dialog.hide();
 	}
 
-	submit (fn)
-	{
-		this.callback = fn
+	submit(fn) {
+		this.callback = fn;
 	}
-}
-frappe.ui.Capture.OPTIONS =
-{
-	  title: __(`Camera`),
+};
+frappe.ui.Capture.OPTIONS = {
+	title: __("Camera"),
 	animate: false,
-	  error: false,
-}
-frappe.ui.Capture.ERR_MESSAGE = __("Unable to load camera.")
-frappe.ui.Capture.TEMPLATE 	  =
-`
+	error: false
+};
+frappe.ui.Capture.ERR_MESSAGE = __('Unable to load camera.');
+frappe.ui.Capture.TEMPLATE = `
 <div class="frappe-capture">
 	<div class="panel panel-default">
 		<img class="fc-p img-responsive"/>
@@ -181,14 +165,7 @@ frappe.ui.Capture.TEMPLATE 	  =
 		<div class="fc-btu">
 			<div class="row">
 				<div class="col-md-6">
-					${
-						''
-						// <div class="pull-left">
-						// 	<button class="btn btn-default">
-						// 		<small>${__('Take Video')}</small>
-						// 	</button>
-						// </div>
-					}
+					${''}
 				</div>
 				<div class="col-md-6">
 					<div class="pull-right">
@@ -201,4 +178,4 @@ frappe.ui.Capture.TEMPLATE 	  =
 		</div>
 	</div>
 </div>
-`
\ No newline at end of file
+`;
diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js
index 4dedfb32feb9..023b28431596 100644
--- a/frappe/public/js/frappe/ui/filters/filter.js
+++ b/frappe/public/js/frappe/ui/filters/filter.js
@@ -54,7 +54,7 @@ frappe.ui.Filter = class {
 			this.filters_config = frappe.boot.additional_filters_config;
 			for (let key of Object.keys(this.filters_config)) {
 				const filter = this.filters_config[key];
-				this.conditions.push([key, __(`{0}`, [filter.label])]);
+				this.conditions.push([key, __("{0}", [filter.label])]);
 				for (let fieldtype of Object.keys(this.invalid_condition_map)) {
 					if (!filter.valid_for_fieldtypes.includes(fieldtype)) {
 						this.invalid_condition_map[fieldtype].push(key);
@@ -542,13 +542,13 @@ frappe.ui.filter_utils = {
 			if (period_map[period]) {
 				period_map[period].forEach((p) => {
 					options.push({
-						label: __(`{0} {1}`, [period, p]),
+						label: __("{0} {1}", [period, p]),
 						value: `${period.toLowerCase()} ${p.toLowerCase()}`,
 					});
 				});
 			} else {
 				options.push({
-					label: __(`{0}`, [period]),
+					label: __("{0}", [period]),
 					value: `${period.toLowerCase()}`,
 				});
 			}
diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js
index 6c577aa0bcca..9b424f9ed26b 100644
--- a/frappe/public/js/frappe/ui/filters/filter_list.js
+++ b/frappe/public/js/frappe/ui/filters/filter_list.js
@@ -63,12 +63,11 @@ frappe.ui.FilterGroup = class {
 	}
 
 	validate_args(doctype, fieldname) {
-
-		if(doctype && fieldname
+		if (doctype && fieldname
 			&& !frappe.meta.has_field(doctype, fieldname)
 			&& !frappe.model.std_fields_list.includes(fieldname)) {
 
-			frappe.throw(__(`Invalid filter: "${[fieldname.bold()]}"`));
+			frappe.throw(__("Invalid filter: {0}", [fieldname.bold()]));
 			return false;
 		}
 		return true;
diff --git a/frappe/public/js/frappe/utils/dashboard_utils.js b/frappe/public/js/frappe/utils/dashboard_utils.js
index fca26d6ecea0..d69ec1ddde6d 100644
--- a/frappe/public/js/frappe/utils/dashboard_utils.js
+++ b/frappe/public/js/frappe/utils/dashboard_utils.js
@@ -182,7 +182,7 @@ frappe.dashboard_utils = {
 				try {
 					f[3] = eval(f[3]);
 				} catch (e) {
-					frappe.throw(__(`Invalid expression set in filter ${f[1]} (${f[0]})`));
+					frappe.throw(__("Invalid expression set in filter {0} ({1})", [f[1], f[0]]));
 				}
 			});
 			filters = [...filters, ...dynamic_filters];
@@ -192,7 +192,7 @@ frappe.dashboard_utils = {
 					const val = eval(dynamic_filters[key]);
 					dynamic_filters[key] = val;
 				} catch (e) {
-					frappe.throw(__(`Invalid expression set in filter ${key}`));
+					frappe.throw(__("Invalid expression set in filter {0}", [key]));
 				}
 			}
 			Object.assign(filters, dynamic_filters);
@@ -238,7 +238,7 @@ frappe.dashboard_utils = {
 					let dashboard_route_html =
 						`<a href = "#dashboard/${values.dashboard}">${values.dashboard}</a>`;
 					let message =
-						__(`${doctype} ${values.name} added to Dashboard ` + dashboard_route_html);
+						__("{0} {1} added to Dashboard {2}", [doctype, values.name, dashboard_route_html]);
 
 					frappe.msgprint(message);
 				});
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 6f6279fd0889..92e5a76f9dd9 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -249,7 +249,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
 	show_add_chart_dialog() {
 		let fields = this.get_field_options();
 		const dialog = new frappe.ui.Dialog({
-			title: __(`Add a ${this.doctype} Chart`),
+			title: __("Add a {0} Chart", [this.doctype]),
 			fields: [
 				{
 					fieldname: 'new_or_existing',
diff --git a/frappe/public/js/frappe/views/desktop/desktop.js b/frappe/public/js/frappe/views/desktop/desktop.js
index e967e14dfca2..c5eee45932aa 100644
--- a/frappe/public/js/frappe/views/desktop/desktop.js
+++ b/frappe/public/js/frappe/views/desktop/desktop.js
@@ -306,7 +306,7 @@ class DesktopPage {
 
 	make_onboarding() {
 		this.onboarding_widget = frappe.widget.make_widget({
-			label: this.data.onboarding.label || __(`Let's Get Started`),
+			label: this.data.onboarding.label || __("Let's Get Started"),
 			subtitle: this.data.onboarding.subtitle,
 			steps: this.data.onboarding.items,
 			success: this.data.onboarding.success,
@@ -371,7 +371,7 @@ class DesktopPage {
 
 	make_cards() {
 		let cards = new frappe.widget.WidgetGroup({
-			title: this.data.cards.label || __(`Reports & Masters`),
+			title: this.data.cards.label || __("Reports & Masters"),
 			container: this.page,
 			type: "links",
 			columns: 3,
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index a32307c6c44f..53cee5b348d7 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -322,12 +322,12 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
 			let message;
 			if (dashboard_name) {
 				let dashboard_route_html = `<a href = "#dashboard/${dashboard_name}">${dashboard_name}</a>`;
-				message = __(`New {0} {1} added to Dashboard ` + dashboard_route_html, [doctype, name]);
+				message = __("New {0} {1} added to Dashboard {2}", [doctype, name, dashboard_route_html]);
 			} else {
-				message = __(`New {0} {1} created`, [doctype, name]);
+				message = __("New {0} {1} created", [doctype, name]);
 			}
 
-			frappe.msgprint(message, __(`New {0} Created`, [doctype]));
+			frappe.msgprint(message, __("New {0} Created", [doctype]));
 		});
 	}
 
@@ -433,7 +433,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
 				try {
 					out = eval(expression.substr(5));
 				} catch (e) {
-					frappe.throw(__(`Invalid "depends_on" expression set in filter ${filter_label}`));
+					frappe.throw(__('Invalid "depends_on" expression set in filter {0}', [filter_label]));
 				}
 			} else {
 				var value = doc[expression];
@@ -738,14 +738,18 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
 
 	get_queued_prepared_reports_warning_message(reports) {
 		const route = `#List/Prepared Report/List?status=Queued&report_name=${this.report_name}`;
+		const report_link_html = reports.length == 1
+			? `<a class="underline" href="${route}">${__('1 Report')}</a>`
+			: `<a class="underline" href="${route}">${__("{0} Reports", [reports.length])}</a>`;
+
 		const no_of_reports_html = reports.length == 1
-			? `${__('There is ')}<a class="underline" href="${route}">${__('1 Report')}</a>`
-			: `${__('There are ')}<a class="underline" href="${route}">${__(`{} Reports`, [reports.length])}</a>`;
+			? `${__('There is {0} with the same filters already in the queue:', [report_link_html])}`
+			: `${__('There are {0} with the same filters already in the queue:', [report_link_html])}`;
 
 		let warning_message = `
 			<p>
-				${__(`Are you sure you want to generate a new report?
-					{} with the same filters already in the queue:`, [no_of_reports_html])}
+				${__("Are you sure you want to generate a new report?")}
+				${no_of_reports_html}
 			</p>`;
 
 		let get_item_html = item => `<a class="underline" href="#Form/Prepared Report/${item.name}">${item.name}</a>`;
@@ -1013,7 +1017,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
 						field.value == values.x_field
 					)[0].label;
 
-				options.title = __(`${this.report_name}: ${x_field_label} vs ${y_field_label}`);
+				options.title = __("{0}: {1} vs {2}", [this.report_name, x_field_label, y_field_label]);
 
 				this.render_chart(options);
 				this.add_chart_buttons_to_toolbar(true);
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index ccec5b3ef4a1..c89ee96520d4 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -387,7 +387,7 @@ export default class ChartWidget extends Widget {
 	setup_filter_dialog(fields) {
 		let me = this;
 		let dialog = new frappe.ui.Dialog({
-			title: __(`Set Filters for ${this.chart_doc.chart_name}`),
+			title: __("Set Filters for {0}", [this.chart_doc.chart_name]),
 			fields: fields,
 			primary_action: function() {
 				let values = this.get_values();
diff --git a/frappe/public/js/frappe/widgets/new_widget.js b/frappe/public/js/frappe/widgets/new_widget.js
index e0cfd51ab471..892fae59f402 100644
--- a/frappe/public/js/frappe/widgets/new_widget.js
+++ b/frappe/public/js/frappe/widgets/new_widget.js
@@ -19,7 +19,8 @@ export default class NewWidget {
 	get_title() {
 		// DO NOT REMOVE: Comment to load translation
 		// __("New Chart") __("New Shortcut") __("New Number Card")
-		return __(`New ${frappe.model.unscrub(this.type)}`);
+		let title = `New ${frappe.model.unscrub(this.type)}`;
+		return __(title);
 	}
 
 	make_widget() {
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index d5b6df6021e5..c111158bad8d 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -35,7 +35,8 @@ class WidgetDialog {
 		// __("New Chart") __("New Shortcut") __("Edit Chart") __("Edit Shortcut")
 
 		let action = this.editing ? "Edit" : "Add";
-		return __(`${action} ${frappe.model.unscrub(this.type)}`);
+		let label = action = action + " " + frappe.model.unscrub(this.type);
+		return __(label);
 	}
 
 	get_fields() {
diff --git a/frappe/workflow/doctype/workflow/workflow.js b/frappe/workflow/doctype/workflow/workflow.js
index 6ebba89e58c7..07e3da19e66b 100644
--- a/frappe/workflow/doctype/workflow/workflow.js
+++ b/frappe/workflow/doctype/workflow/workflow.js
@@ -53,9 +53,9 @@ frappe.ui.form.on("Workflow", {
 			`<p class="bold">
 				${__('Are you sure you want to save this document?')}
 			</p>
-			<p>${__(`There are documents which have workflow states that do not exist in this Workflow.
-				It is recommended that you add these states to the Workflow and change their states
-				before removing these states.`)}
+			<p>
+				${__("There are documents which have workflow states that do not exist in this Workflow.")}
+				${__("It is recommended that you add these states to the Workflow and change their states before removing these states.")}
 			</p>`;
 		const message_html = warning_html + frm.state_table_html;
 		let proceed_action = () => {
@@ -63,7 +63,12 @@ frappe.ui.form.on("Workflow", {
 			frm.save();
 		};
 
-		frappe.warn(__(`Worflow States Don't Exist`), message_html, proceed_action, __(`Save Anyway`));
+		frappe.warn(
+			__("Worflow States Don't Exist"),
+			message_html,
+			proceed_action,
+			__("Save Anyway")
+		);
 	},
 	set_table_html: function(frm) {
 


Code review comments:
diff_hunk: @@ -21,13 +19,12 @@ frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({
 			if (input) {
 				options = input.val();
 			}
-		}
-		else {
+		} else {
 			options = frappe.model.get_value(this.df.parent, this.docname, this.df.options);
 		}
 
 		if (frappe.model.is_single(options)) {
-			frappe.throw(__(`${options.bold()} is not a valid DocType for Dynamic Link`));
+			frappe.throw(__("{} is not a valid DocType for Dynamic Link", [options.bold()]));
path: frappe/public/js/frappe/form/controls/dynamic_link.js
text: @user1:
```suggestion
			frappe.throw(__("{0} is not a valid DocType for Dynamic Link", [options.bold()]));
```

diff_hunk: @@ -53,17 +53,22 @@ frappe.ui.form.on("Workflow", {
 			`<p class="bold">
 				${__('Are you sure you want to save this document?')}
 			</p>
-			<p>${__(`There are documents which have workflow states that do not exist in this Workflow.
-				It is recommended that you add these states to the Workflow and change their states
-				before removing these states.`)}
+			<p>
+				${__("There are documents which have workflow states that do not exist in this Workflow.")}
+				${__("It is recommended that you add these states to the Workflow and change their states before removing these states.")}
path: frappe/workflow/doctype/workflow/workflow.js
text: @user1:
Multiple lines should be in one line. Breaking them up will lose context.
```suggestion
				${__("There are documents which have workflow states that do not exist in this Workflow. It is recommended that you add these states to the Workflow and change their states before removing these states.")}
```

diff_hunk: @@ -542,13 +542,13 @@ frappe.ui.filter_utils = {
 			if (period_map[period]) {
 				period_map[period].forEach((p) => {
 					options.push({
-						label: __(`{0} {1}`, [period, p]),
+						label: __("{0} {1}", [period, p]),
 						value: `${period.toLowerCase()} ${p.toLowerCase()}`,
 					});
 				});
 			} else {
 				options.push({
-					label: __(`{0}`, [period]),
+					label: __("{0}", [period]),
path: frappe/public/js/frappe/ui/filters/filter.js
text: @user1:
This one too
```suggestion
					label: __(period),
```

diff_hunk: @@ -54,7 +54,7 @@ frappe.ui.Filter = class {
 			this.filters_config = frappe.boot.additional_filters_config;
 			for (let key of Object.keys(this.filters_config)) {
 				const filter = this.filters_config[key];
-				this.conditions.push([key, __(`{0}`, [filter.label])]);
+				this.conditions.push([key, __("{0}", [filter.label])]);
path: frappe/public/js/frappe/ui/filters/filter.js
text: @user1:
```suggestion
				this.conditions.push([key, __(filter.label)]);
```

diff_hunk: @@ -542,13 +542,13 @@ frappe.ui.filter_utils = {
 			if (period_map[period]) {
 				period_map[period].forEach((p) => {
 					options.push({
-						label: __(`{0} {1}`, [period, p]),
+						label: __("{0} {1}", [period, p]),
path: frappe/public/js/frappe/ui/filters/filter.js
text: @user1:
This does not make sense. This will just concatenate `period` and `p` without translating anything.
```suggestion
						label: `${period} ${p}`,
```