CA1303: Do not pass literals as localized parameters

CheckId CA1303
Category Microsoft.Globalization
Breaking change Non-breaking

Cause

A method passes a string literal as a parameter to a .NET constructor or method and that string should be localizable.

This warning is raised when a literal string is passed as a value to a parameter or property and one or more of the following cases is true:

  • The LocalizableAttribute attribute of the parameter or property is set to true.

  • The name of the string parameter that is passed to a Console.Write or Console.WriteLine method is either "value" or "format".

By default, this rule analyzes the entire codebase, but this is configurable.

Starting with version 3.0.0 of the analyzer package, the naming heuristic is no longer used by default but this is configurable.

Rule description

String literals that are embedded in source code are difficult to localize.

How to fix violations

To fix a violation of this rule, replace the string literal with a string retrieved through an instance of the ResourceManager class.

When to suppress warnings

It is safe to suppress a warning from this rule if the code library will not be localized or if the string is not exposed to the end user or a developer using the code library.

Users can eliminate noise against methods that should not be passed localized strings by either renaming the parameter or property, or by marking these items as conditional.

Configurability

If you're running this rule from FxCop analyzers (and not with legacy analysis), this rule is configurable.

Excluded type names with derived types

You can configure which types, including its derived types, to exclude from analysis. For example, to specify that the rule should not run on any methods within types named MyType and its derived types, add the following key-value pair to an .editorconfig file in your project:

dotnet_code_quality.CA1303.excluded_type_names_with_derived_types = MyType

Allowed symbol name formats in the option value (separated by '|'):

  • Type name only (includes all types with the name, regardless of the containing type or namespace)
  • Fully qualified names in the symbol's documentation ID format, with an optional T: prefix.

Examples:

Option Value Summary
dotnet_code_quality.CA1303.excluded_type_names_with_derived_types = MyType Matches all types named 'MyType' and all of its derived types in the compilation
dotnet_code_quality.CA1303.excluded_type_names_with_derived_types = MyType1|MyType2 Matches all types named either 'MyType1' or 'MyType2' and all of their derived types in the compilation
dotnet_code_quality.CA1303.excluded_type_names_with_derived_types = M:NS.MyType Matches specific type 'MyType' with given fully qualified name and all of its derived types
dotnet_code_quality.CA1303.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Matches specific types 'MyType1' and 'MyType2' with respective fully qualified names and all of their derived types

You can configure this option for just this rule, for all rules, or for all rules in this category (Globalization). For more information, see Configure FxCop analyzers.

Use naming heuristic

You can configure whether parameters or properties name containing "Text", "Message", or "Caption" will trigger this rule.

dotnet_code_quality.CA1303.use_naming_heuristic = true

Example

The following example shows a method that throws an exception when either of its two arguments are out of range. For the first argument, the exception constructor is passed a literal string, which violates this rule. For the second argument, the constructor is correctly passed a string retrieved through a ResourceManager.

using namespace System;
using namespace System::Globalization;
using namespace System::Reflection;
using namespace System::Resources;
using namespace System::Windows::Forms;

[assembly: NeutralResourcesLanguageAttribute("en-US")];
namespace GlobalizationLibrary
{
    public ref class DoNotPassLiterals
    {
        ResourceManager^ stringManager;

    public:
        DoNotPassLiterals()
        {
            stringManager = 
                gcnew ResourceManager("en-US", Assembly::GetExecutingAssembly());
        }

        void TimeMethod(int hour, int minute)
        {
            if(hour < 0 || hour > 23)
            {
                MessageBox::Show(
                    "The valid range is 0 - 23."); //CA1303 fires because the parameter for method Show is Text
            }

            if(minute < 0 || minute > 59)
            {
                MessageBox::Show(
                    stringManager->GetString("minuteOutOfRangeMessage", CultureInfo::CurrentUICulture));
            }
        }
    };
}
Imports System
Imports System.Globalization
Imports System.Reflection
Imports System.Resources
Imports System.Windows.Forms


<assembly: System.Resources.NeutralResourcesLanguageAttribute("en-US")>
Namespace GlobalizationLibrary

    Public Class DoNotPassLiterals

        Dim stringManager As System.Resources.ResourceManager

        Sub New()
            stringManager = New System.Resources.ResourceManager( _
                "en-US", System.Reflection.Assembly.GetExecutingAssembly())
        End Sub

        Sub TimeMethod(hour As Integer, minute As Integer)
        
            If(hour < 0 Or hour > 23) Then
                MessageBox.Show( _
                    "The valid range is 0 - 23.") 'CA1303 fires because the parameter for method Show is Text
            End If

            If(minute < 0 Or minute > 59) Then
                 MessageBox.Show( _
                    stringManager.GetString("minuteOutOfRangeMessage", _
                        System.Globalization.CultureInfo.CurrentUICulture))
            End If

        End Sub

    End Class

End Namespace
using System;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;



[assembly: NeutralResourcesLanguageAttribute("en-US")]
namespace GlobalizationLibrary
{
    public class DoNotPassLiterals
    {
        ResourceManager stringManager;
        public DoNotPassLiterals()
        {
            stringManager =
            new ResourceManager("en-US", Assembly.GetExecutingAssembly());
        }

        public void TimeMethod(int hour, int minute)
        {
            if (hour < 0 || hour > 23)
            {
                MessageBox.Show(
                "The valid range is 0 - 23."); //CA1303 fires because the parameter for method Show is Text
            }

            if (minute < 0 || minute > 59)
            {
                MessageBox.Show(
                stringManager.GetString(
                "minuteOutOfRangeMessage", CultureInfo.CurrentUICulture));
            }
        }
    }

}

See also