To Optimize Memory Consumption of Localized Data

To optimize the memory consumption of static data, you can move the localization data from all customer application instances to centralized storage. By default, the localization data is kept in the database of every MYOB Acumatica instance, and the total size of this data therefore equals the number of instances times the size of the data. If you move the localization data to centralized storage, there is only one copy of this data.

Alternatively, you can optimize the consumption of memory by disabling localization.

Whether you set up centralized storage of localization data or disable localization, you should perform the following steps:

  1. Implement a custom translation provider. Follow the instructions in To Implement a Custom Translation Provider or To Disable Localization in this topic depending on which way of optimization of memory consumption you select.
  2. Place the assembly file with the new provider in the Bin directory of the MYOB Acumatica instance, and add the assembly to the customization project as a File element.
  3. Register the new provider in the pxtranslate element of the web.config file, as described in To Register the New Provider in Web.config in this topic.

To Implement a Custom Translation Provider

To implement a custom translation provider, derive a class from the PXTranslationProvider class and override the LoadCultureDictionary() method, as the following example shows.

public class DemoTranslationProvider : PXTranslationProvider
{
    public override PXCultureDictionary LoadCultureDictionary(
        string locale, bool includeObsolete, bool escapeStrings)
    {
        PXCultureDictionary dictionary = new PXCultureDictionary();
        ...
        // Adding a general translation for some string
        dictionary.Append(
            valueToTranslate,
            new PXCultureValue(locale, translation));
        // Adding a special translation for some string
        dictionary.AppendException(
            valueToTranslate,
            new PXCultureEx(resourceID, locale, translation));
        ...
        return dictionary;
    }
}

The LoadCultureDictionary() method returns an instance of the PXCultureDictionary type. Values are added to objects of this type through the Append() and AppendException() methods. Append() adds a general translation for a string. AppendException() adds a translation for a special case (exception) identified by the resource key.

The code below defines a custom translation provider that loads the localization data from an external MYOB Acumatica database by using ADO.NET tools.

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Translation;

namespace Demo.Translation
{
    public class DemoTranslationProvider : PXTranslationProvider
    {
        private struct TranslationKey
        {
            public Guid id;
            public string resKey;
            public string locale;
        }

        // The connection string for the database that stores localization
        // data
        // Specify a specific value of the connection string
        private const string connectionString = "";

        // Overriding the method that returns the dictionary of
        // localization data
        public override PXCultureDictionary LoadCultureDictionary(
            string locale, bool includeObsolete, bool escapeStrings)
        {
            string localizationValueSelect;
            string localizationTranslationSelect;
            InitializeSelectCommand(locale, includeObsolete,
                                    out localizationValueSelect,
                                    out localizationTranslationSelect);

            Dictionary<Guid, string> localizationValue;
            Dictionary<TranslationKey, string> localizationTranslation;
            SelectLocalizationValues(localizationValueSelect,
                                     localizationTranslationSelect,
                                     out localizationValue,
                                     out localizationTranslation);

            return CreateCultureDictionary(escapeStrings, localizationValue,
                                           localizationTranslation);
        }

        // Builds the SQL statement for selecting localization data
        private void InitializeSelectCommand(
            string locale, bool includeObsolete,
            out string localizationValueSelect,
            out string localizationTranslationSelect)
        {
            StringBuilder localizationValueSelectBld = 
                new StringBuilder("Select IDlv, NeutralValue" + 
                                  "From LocalizationValue");
            if (!includeObsolete)
            {
                localizationValueSelectBld.Append(" Where IsObsolete = 0");
            }
            localizationValueSelect = localizationValueSelectBld.ToString();

            StringBuilder localizationTranslationSelectBld =
                new StringBuilder("Select IDlt, ResKey, Value, Locale" + 
                                  "From LocalizationTranslation");
            if (!string.IsNullOrEmpty(locale))
            {
                localizationTranslationSelectBld.AppendFormat(
                    " Where Locale = '{0}'", locale);
            }
            localizationTranslationSelect = 
                localizationTranslationSelectBld.ToString();
        }

        // Retrieves localization data from the database by using the provided
        // SQL statement
        private void SelectLocalizationValues(
            string localizationValueSelect,
            string localizationTranslationSelect,
            out Dictionary<Guid, string> localizationValue,
            out Dictionary<TranslationKey, string> localizationTranslation)
        {
            localizationValue = new Dictionary<Guid, string>();
            localizationTranslation = 
                new Dictionary<TranslationKey, string>();

            using (SqlConnection connection = 
                   new SqlConnection(connectionString))
            {
                connection.Open();

                SqlCommand command = new SqlCommand(localizationValueSelect,
                                                    connection);
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        localizationValue.Add(reader.GetGuid(0),
                                              reader.GetString(1));
                    }
                }

                command.CommandText = localizationTranslationSelect;
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        TranslationKey newTranslationKey = new TranslationKey()
                        {
                            id = reader.GetGuid(0),
                            resKey = reader.GetString(1),
                            locale = reader.GetString(3)
                        };
                        localizationTranslation.Add(newTranslationKey,
                                                    reader.GetString(2));
                    }
                }
            }
        }

        // Fills the PXCultureDictionary object with localization data by using
        // the provided dictionaries of values to translate and the
        // corresponding translations
        private PXCultureDictionary CreateCultureDictionary(
            bool escapeStrings,
            Dictionary<Guid, string> localizationValue,
            Dictionary<TranslationKey, string> localizationTranslation)
        {
            PXCultureDictionary dictionary = new PXCultureDictionary();

            if (localizationTranslation.Count != 0)
            {
                foreach (Guid id in localizationValue.Keys)
                {
                    IEnumerable<TranslationKey> localizationTranslationKeys = 
                        from translationRowKey in localizationTranslation.Keys
                        where translationRowKey.id == id
                        select translationRowKey;
                    foreach (TranslationKey key in localizationTranslationKeys)
                    {
                        string translationResKey = key.resKey;
                        string translationLocale = key.locale;
                        string translationValue = localizationTranslation[key];
                        string value = escapeStrings ?
                            PXLocalizer.EscapeString(translationValue) :
                            translationValue;

                        if (string.IsNullOrEmpty(translationResKey))
                        {
                            dictionary.Append(
                                localizationValue[id],
                                new PXCultureValue(translationLocale, value));
                        }
                        else
                        {
                            dictionary.AppendException(
                                localizationValue[id],
                                new PXCultureEx(translationResKey,
                                                translationLocale, value));
                        }
                    }
                }
            }
            return dictionary;
        }
    }
}

To Disable Localization

To disable localization, implement a custom translation provider with the LoadCultureDictionary() method that returns null, as the following code shows.

public class DemoTranslationProvider : PXTranslationProvider
{
    public override PXCultureDictionary LoadCultureDictionary(
        string locale, bool includeObsolete, bool escapeStrings)
    {
        return null;
    }
}

To Register the New Provider in Web.config

Once the provider class is defined, register it in the web.config file by adding a new key to the providers collection of the pxtranslate element and specifying the new key in the defaultProvider property of pxtranslate. Use the add element to register the provider. Set the name attribute to the key, which can be any unique value, and specify the type of the custom provider in the type attribute.

The following code shows the configuration of DemoTranslationProvider, introduced in the example above, in the pxtranslate element of the web.config file.

<px.core>
  ...
  <pxtranslate defaultProvider="DemoTranslationProvider">
    <providers>
      <!--The default translation provider-->
      <remove name="PXDBTranslatonProvider" />
      <add name="PXDBTranslatonProvider"
           type="PX.Data.PXDBTranslatonProvider, PX.Data" />
  
      <!--The custom translation provider-->
      <remove name="DemoTranslationProvider" />
      <add name="DemoTranslationProvider"
           type="Demo.Translation.DemoTranslationProvider, TranslationProvider"
           applicationName="/"/>
    </providers>
  </pxtranslate>
  ...
</px.core>