Skip to content

Localization best practices for Dynamo developers

Ben Goh edited this page Aug 14, 2015 · 11 revisions

Add localizable resources to a new project

Making a project localizable includes the following steps:

  • Adding *.resx and *.en-US.resx files to project
  • Updating AssemblyInfo.cs of the project
  • Referencing the string resource in *.cs files
  • Referencing the string resource in *.xaml files

These steps are detailed in the following sections.

Adding *.resx and *.en-US.resx files

Follow the following steps to add a new resource file:

  1. Select Add > New Item from a CSharp project for Add New Item dialog.
  2. Select Visual C# Items on the tree to the left of the dialog.
  3. Select Resources File from the list.
  4. Rename it to Resource.resx (instead of Resource1.resx).
  5. Double-click on Resource.resx file to open the resource editor.
  6. Change the Access Modifier from Internal to Public.
  7. Duplicate the Resource.resx as Resource.en-US.resx in Explorer.
  8. Select Add > Existing Item from the project and select Resource.en-US.resx to include it in the project.

Notice that Resource.en-US.resx does not come with Resource.Designer.cs like Resource.resx file does. This is because its Access Modifier is set to No code generation, and that is okay.

Note that both *.resx and *.en-US.resx files must always be kept completely in-sync. The process of updating these files typically includes adding the desirable strings into *.resx file, and then copy it to overwrite *.en-US.resx file. This way they are always 100% identical.

Updating AssemblyInfo.cs file

With *.resx and *.en-US.resx files added to the project, the AssemblyInfo.cs must be updated to specify a fallback assembly in the event that a desirable localized *.resources.dll cannot be found during runtime (typically the fallback is on en-US resources). This can be done in either of the following ways.

Option 1

This is the preferred way for any development work under DynamoDS/Dynamo repository. Follow the following steps to add a link reference to AssemblySharedInfo.cs file:

  1. Select Add > Existing Item menu to bring up Add Existing Item dialog.
  2. Navigate to this folder: Dynamo/src/AssemblySharedInfoGenerator.
  3. Select AssemblySharedInfo.cs file and select Add as Link (through drop down of Add button).

Having this reference ensures that the version number always matches that of the main Dynamo build.

Option 2

If for some reason Option 1 is not feasible, then the existing Properties/AssemblyInfo.cs can be updated by adding the following line:

//In order to begin building localizable applications, set 
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>.  For example, if you are using US english
//in your source files, set the <UICulture> to en-US.  Then uncomment
//the NeutralResourceLanguage attribute below.  Update the "en-US" in
//the line below to match the UICulture setting in the project file.

[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]

Referencing the string resource in *.cs files

To reference a string by its ID SettingsMigrationDialogTitle in a CSharp file, do this:

string dialogTitle = Properties.Resources.SettingsMigrationDialogTitle;

Referencing the string resource in *.xaml files

To reference the same string in *.xaml file, do this:

<Window xmlns:p="clr-namespace:Dynamo.Wpf.Properties"
        ...
        Title="{x:Static p:Resources.SettingsMigrationDialogTitle}" 
        ...>

    <Button Content="{x:Static p:Resources.MigrationAcceptButton}" ... />

</Window>

Updating resources of an existing project

Note that both *.resx and *.en-US.resx files must always be kept completely in-sync. The process of updating these files typically includes adding the desirable strings into *.resx file, and then copy it to overwrite *.en-US.resx file. This way they are always 100% identical.

Miscellaneous

Use string.Format instead of string concatenation.

Adding description for a NodeModel

  1. Move Get a color given a color range string into the corresponding *.resx file.
  2. Give an Id of ColorRangeDescription to the newly added string.
  3. Copy the *.resx file to overwrite *.en-US.resx file so they match up.
  4. Add a NodeDescription attribute to ColorRange class like so:
[NodeDescription("ColorRangeDescription", 
    typeof(DSCoreNodesUI.Properties.Resources))]
public class ColorRange : NodeModel
{
    // ...
}

Adding search tags for a NodeModel

  1. Move colorrange;color;range; string into the corresponding *.resx file.
  2. Give an Id of ColorRangeSearchTags to the newly added string.
  3. Copy the *.resx file to overwrite *.en-US.resx file so they match up.
  4. Add a NodeSearchTags attribute to ColorRange class like so:
[NodeSearchTags("ColorRangeSearchTags",
    typeof(DSCoreNodesUI.Properties.Resources))]
public class ColorRange : NodeModel
{
    // ...
}

Search tags for a given NodeModel class is made up of multiple ; delimited substrings. In a localized language, it is up to the localizer to add additional search terms in local languages. For the colorrange;color;range example in French language, the search terms can be localized as colorrange;color;range;couleur (with one new entry added just for French language).

Localizing enum values for display

Similar to NodeSearchTags and NodeDescription attributes above, to display localized values for an enum value, a EnumDescription attribute is added right next to each enum value. For example:

public enum DayOfWeek
{
    [EnumDescription("EnumDateOfWeekSunday", typeof(Properties.Resources))]     Sunday,
    [EnumDescription("EnumDateOfWeekMonday", typeof(Properties.Resources))]     Monday,
    [EnumDescription("EnumDateOfWeekTuesday", typeof(Properties.Resources))]    Tuesday,
    [EnumDescription("EnumDateOfWeekWednesday", typeof(Properties.Resources))]  Wednesday,
    [EnumDescription("EnumDateOfWeekThursday", typeof(Properties.Resources))]   Thursday,
    [EnumDescription("EnumDateOfWeekFriday", typeof(Properties.Resources))]     Friday,
    [EnumDescription("EnumDateOfWeekSaturday", typeof(Properties.Resources))]   Saturday
}

And in the corresponding *.resx file:

<data name="EnumDateOfWeekFriday" xml:space="preserve">
  <value>Friday</value>
</data>
<data name="EnumDateOfWeekMonday" xml:space="preserve">
  <value>Monday</value>
</data>
<data name="EnumDateOfWeekSaturday" xml:space="preserve">
  <value>Saturday</value>
</data>
<data name="EnumDateOfWeekSunday" xml:space="preserve">
  <value>Sunday</value>
</data>
<data name="EnumDateOfWeekThursday" xml:space="preserve">
  <value>Thursday</value>
</data>
<data name="EnumDateOfWeekTuesday" xml:space="preserve">
  <value>Tuesday</value>
</data>
<data name="EnumDateOfWeekWednesday" xml:space="preserve">
  <value>Wednesday</value>
</data>
Clone this wiki locally