Perspective System.util.translate Poor/Inconsistent Performance

I have a script that iterates over rows of a dataset and then calls system.util.translate() on an element of each row. There are about 700 rows in the dataset. When I run this script through the scripting console it runs in about 0.5 seconds. When I run it through an onClick event on a button it takes about 6 minutes. A post from August 2020 (Perspective - Scripting system.util.translate) suggests that this is because each call must make a round trip to the gateway. Most of the translations are unique so caching will not help much. The obvious solution then would be to translate as many terms as possible in a single call, but system.util.translate() only accepts a single string, not a list.

Are there any workarounds for this? Currently the only thing I can think of is implementing my own translation table and using that instead of the translation manager.

1 Like

If the data structure is consistent you could write the table to a dataset tag, call a gateway function to translate it and then use the translated dataset.

I don’t think I have a good understanding of scripting scope in perspective. Where do I declare a script that will run in the gateway scope specifically?

According to this page:https://docs.inductiveautomation.com/display/DOC80/Scripting+in+Perspective#ScriptinginPerspective-Scopes, “All Perspective scripting is run on the Gateway, although session-specific functions (like navigation) will only affect a single session.”

Could you post your code that you are running?

I don’t think that this is a code specific issue, as you would run into this in any similar scenario, but sure.

def ProcessDiagnostics():
	params = { "machineno": 0, "severity_1": 1, "severity_2": 1, "severity_3": 0, "severity_4": 0 }
    query_result = system.db.runNamedQuery("GetDiagnostics", params)
	output_json = []
	
	style_green = {"backgroundColor": "#5EB34D"}
	style_blue = {"backgroundColor": "#5459DD"}
	style_orange = {"backgroundColor": "#E19721"}
	style_red = {"backgroundColor": "#DB1E1E"}
	
	for row in range(query_result.getRowCount()):
		row_object = {}
		row_value = {}
		
		try:
			diagtext = system.util.translate(query_result.getValueAt(row, 4))
		except:
			diagtext = ''
		
		row_value['time'] = query_result.getValueAt(row, 0)
		row_value['diagno'] = query_result.getValueAt(row, 2)
		row_value['diagtext'] = diagtext
		
		row_object['value'] = row_value
		severity = query_result.getValueAt(row, 3)
		
		if severity == 1:
			row_object['style'] = style_green
		if severity == 2:
			row_object['style'] = style_blue
		if severity == 3:
			row_object['style'] = style_orange
		if severity == 4:
			row_object['style'] = style_red
		
		output_json.append(row_object)
	return output_json

The 'round trip' back and forth is probably not as much of a problem as that post makes it out to be, but...

Yes, absolutely. Something like this in a script library should allow you to translate many terms at once, reasonably efficiently:

def translate(terms, strict=False, locale=None):
	from com.inductiveautomation.ignition.gateway import IgnitionGateway
	from com.inductiveautomation.ignition.gateway.script import GatewaySystemUtilities
	package = IgnitionGateway.get().getLocalizationManager().loadFullPackage()
	if locale is None:
		locale = GatewaySystemUtilities.LOCAL_LOCALE.get()
	fn = package.getStrict if strict else package.get
	return [fn(locale, term) for term in terms]

EDIT 10/13/21:
Potentially more end-user useful, a function that returns a dictionary:

def translate(terms, strict=False, locale=None):
	"""
	Translate a collection of terms in bulk, rather than all at once.

	Args:
		terms: The terms to translate.
		strict (bool): If True, a provided key without a translation will return
			None. If False, the original key will be returned.
		locale (java.util.Locale): The locale to translate values to. 
			If not provided, will attempt to use the default from the current 
			context (e.g. Perspective session or Gateway default).

	Returns:
		dict: A mapping of original keys to translated terms.
	"""
	from com.inductiveautomation.ignition.gateway import IgnitionGateway
	from com.inductiveautomation.ignition.gateway.script import GatewaySystemUtilities
	package = IgnitionGateway.get().getLocalizationManager().loadFullPackage()
	if locale is None:
		locale = GatewaySystemUtilities.LOCAL_LOCALE.get()
	fn = package.getStrict if strict else package.get
	return {term: fn(locale, term) for term in terms}
5 Likes

I just get the following error when trying to import those modules from a project library script. Am I defining this function in the wrong spot? As I mentioned above I’m not clear on the actual difference of scopes based on the documentation.

Traceback (most recent call last): File "<input>", line 1, in <module> ImportError: Error loading module Translate: Traceback (most recent call last): File "<module:Translate>", line 1, in <module> ImportError: cannot import name IgnitionGateway

You will only be able to execute that function from a gateway context (Perspective script, gateway event script, tag event script). The script console is running the script on the local machine, in the designer’s context.