Skip to main content

Localized Resource

A Localized Resource defines multi-language strings that can be dynamically retrieved based on the user's language preference. This enables internationalization (i18n) of your business logic without hardcoding text in a single language.

The Challenge of Multi-Language Applications

Organizations operating globally or in multilingual regions face a common challenge: how to present information to users in their preferred language. This goes beyond translating the user interface—business logic itself often produces text that users see:

  • Validation error messages ("This field is required")
  • Email notification content ("Your order has been approved")
  • Generated document text ("Invoice", "Total", "Due Date")
  • Status descriptions ("Pending Approval", "Completed")
  • Business rule explanations ("Amount exceeds credit limit")

Hardcoding these strings in a single language creates a poor experience for users who speak other languages. But managing translations scattered throughout your logic is a maintenance nightmare.

The Solution: Centralized Localized Resources

Localized Resources provide a centralized, structured approach to managing multi-language text:

  1. Define strings once with a unique key/index
  2. Provide translations for each supported language
  3. Reference by key in your Logic Blocks and Flows
  4. Automatic resolution returns the correct language based on user context

When your logic needs to display text, it references the Localized Resource by key. FlowOn Logic automatically returns the appropriate translation based on the current user's language setting in Dynamics 365.

Structure

A Localized Resource consists of:

ComponentDescription
NameUnique identifier for the resource (e.g., "ValidationMessages", "EmailTemplates")
DescriptionDocumentation explaining what strings this resource contains
ProjectThe FlowOn Project this resource belongs to
Default LanguageThe fallback language if a translation is not available
Supported LanguagesList of languages with translations (English, Spanish, French, German, etc.)
String EntriesThe individual strings with their keys and translations

String Entries

Each string entry within a Localized Resource has:

PropertyDescription
Key/IndexUnique identifier for this string (e.g., "RequiredField", "OrderApproved")
TranslationsThe text in each supported language
DescriptionDocumentation explaining when/where this string is used
PlaceholdersDocumentation of any dynamic placeholders in the string

Placeholders for Dynamic Content

Strings often need to include dynamic values—an amount, a name, a date. Localized Resources support placeholders that are replaced with actual values at runtime:

PlaceholderDescription
{0}First dynamic value
{1}Second dynamic value
{2}Third dynamic value
...Additional placeholders as needed

The placeholder positions must remain consistent across all translations, though the surrounding text can differ based on language grammar.

Example with placeholders:

KeyEnglishSpanishFrench
AmountExceeded"Amount 0 exceeds limit of 1""El monto 0 excede el límite de 1""Le montant 0 dépasse la limite de 1"
WelcomeUser"Welcome, 0!""¡Bienvenido, 0!""Bienvenue, 0!"
OrderShipped"Order 0 shipped on 1""Pedido 0 enviado el 1""Commande 0 expédiée le 1"

When the logic executes, it provides the actual values: GetLocalizedString("AmountExceeded", "$15,000", "$10,000") returns "Amount $15,000 exceeds limit of $10,000" (or the equivalent in the user's language).

Language Resolution

When a Localized Resource string is requested, FlowOn Logic resolves the language in this order:

  1. User's Language Setting: The language configured in the user's Dynamics 365 personal options
  2. Organization Default: The organization's base language setting
  3. Resource Default: The default language specified in the Localized Resource

If a translation doesn't exist for the resolved language, the system falls back to the Resource Default language. This ensures users always see something meaningful, even if a specific translation is missing.

Using Localized Resources

Localized Resources can be used in:

  • Validation error messages: Return localized messages when validation fails
  • Flow steps: Use localized text in email bodies, notifications, and generated content
  • Raise Error step: Provide localized error messages
  • Any expression: Reference localized strings wherever text is needed

Example: Validation Messages Resource

Localized Resource: ValidationMessages

Description: "Error messages displayed when data validation fails"
Default Language: English
Supported Languages: English, Spanish, French, German, Portuguese

String Entries:
┌────────────────────┬────────────────────────────────────────────────────────────────────┐
│ Key │ Translations │
├────────────────────┼────────────────────────────────────────────────────────────────────┤
│ RequiredField │ EN: "This field is required" │
│ │ ES: "Este campo es obligatorio" │
│ │ FR: "Ce champ est requis" │
│ │ DE: "Dieses Feld ist erforderlich" │
│ │ PT: "Este campo é obrigatório" │
├────────────────────┼────────────────────────────────────────────────────────────────────┤
│ InvalidEmail │ EN: "Please enter a valid email address" │
│ │ ES: "Por favor ingrese una dirección de correo válida" │
│ │ FR: "Veuillez entrer une adresse email valide" │
│ │ DE: "Bitte geben Sie eine gültige E-Mail-Adresse ein" │
│ │ PT: "Por favor, insira um endereço de email válido" │
├────────────────────┼────────────────────────────────────────────────────────────────────┤
│ AmountExceeded │ EN: "Amount exceeds the maximum limit of {0}" │
│ Placeholders: {0} │ ES: "El monto excede el límite máximo de {0}" │
│ = Maximum Amount │ FR: "Le montant dépasse la limite maximale de {0}" │
│ │ DE: "Der Betrag überschreitet das Maximum von {0}" │
│ │ PT: "O valor excede o limite máximo de {0}" │
├────────────────────┼────────────────────────────────────────────────────────────────────┤
│ DateInPast │ EN: "Date cannot be in the past" │
│ │ ES: "La fecha no puede ser en el pasado" │
│ │ FR: "La date ne peut pas être dans le passé" │
│ │ DE: "Das Datum darf nicht in der Vergangenheit liegen" │
│ │ PT: "A data não pode estar no passado" │
├────────────────────┼────────────────────────────────────────────────────────────────────┤
│ InvalidRange │ EN: "{0} must be between {1} and {2}" │
│ Placeholders: │ ES: "{0} debe estar entre {1} y {2}" │
│ {0} = Field Name │ FR: "{0} doit être compris entre {1} et {2}" │
│ {1} = Min Value │ DE: "{0} muss zwischen {1} und {2} liegen" │
│ {2} = Max Value │ PT: "{0} deve estar entre {1} e {2}" │
└────────────────────┴────────────────────────────────────────────────────────────────────┘

Example: Email Templates Resource

Localized Resource: EmailTemplates

Description: "Email subject lines and body content for automated notifications"
Default Language: English
Supported Languages: English, Spanish, French

String Entries:
┌─────────────────────────┬────────────────────────────────────────────────────────────────┐
│ Key │ Translations │
├─────────────────────────┼────────────────────────────────────────────────────────────────┤
│ OrderConfirmSubject │ EN: "Order Confirmation - #{0}" │
│ Placeholders: {0} │ ES: "Confirmación de Pedido - #{0}" │
│ = Order Number │ FR: "Confirmation de Commande - #{0}" │
├─────────────────────────┼────────────────────────────────────────────────────────────────┤
│ OrderConfirmBody │ EN: "Dear {0},\n\nThank you for your order #{1}.\n │
│ Placeholders: │ Your order total is {2}.\n\nBest regards" │
│ {0} = Customer Name │ ES: "Estimado/a {0},\n\nGracias por su pedido #{1}.\n │
│ {1} = Order Number │ El total de su pedido es {2}.\n\nSaludos cordiales" │
│ {2} = Order Total │ FR: "Cher/Chère {0},\n\nMerci pour votre commande #{1}.\n │
│ │ Le total de votre commande est {2}.\n\nCordialement" │
├─────────────────────────┼────────────────────────────────────────────────────────────────┤
│ ApprovalRequiredSubject │ EN: "Approval Required: {0}" │
│ Placeholders: {0} │ ES: "Aprobación Requerida: {0}" │
│ = Request Type │ FR: "Approbation Requise: {0}" │
└─────────────────────────┴────────────────────────────────────────────────────────────────┘

Best Practices

Organize by Functional Area: Create separate Localized Resources for different purposes (ValidationMessages, EmailTemplates, StatusLabels, ReportText) rather than one giant resource.

Use Consistent Key Naming: Establish a naming convention for keys. For example: {Area}_{Action}_{Detail} like "Order_Validation_AmountExceeded" or simply descriptive names like "AmountExceeded".

Document Placeholders: Always document what each placeholder represents. Someone translating the strings needs to understand what 0, 1, 2 mean to create grammatically correct translations.

Consider Grammar Differences: Different languages have different grammar structures. Placeholder order might feel natural in English but awkward in other languages. Keep placeholders flexible and test with native speakers.

Plan for Text Expansion: Translations are often longer than the original English text. German text, for example, can be 30% longer than English. Ensure your UI and documents can accommodate longer strings.

Maintain Consistency: Use the same key for the same message everywhere. Don't create "RequiredField", "FieldRequired", and "MandatoryField" for the same message—this creates unnecessary translation work and inconsistency.

Handle Missing Translations Gracefully: Always define strings in the default language. If a new string is added but not yet translated, users will see the default language version rather than an error.