Forum Discussion

ecovonrein's avatar
ecovonrein
Iron Contributor
May 02, 2023
Solved

International data validation

I am programming for an international team and I know how I can make Excel display drop downs in the chosen language of a user.  What I don't know is this:  When an English user selects "Roastbeef", saves the spreadsheet and a German user opens that same spreadsheet, is there any way that the input from the English user could morph into the German "Rinderbraten"? 

 

For the problem is of course this:  The validation list behind the drop down I dynamically populate from the user's chosen language.  Processing of this input is based on a code.  This code is looked up by reference to the user's choice.  But "Roastbeef" - not being a valid choice in German - cannot be resolved by the code lookup.

 

Thanks.

  • mathetesSorry, John, there really isn't any magic to his.  Sure, my actual selection has nothing to do with meat, but it might as well.  It really does not matter.  Anyway, in the absence of any feedback from fellow travellers, I thought of a VBA solution to the problem.   I attach it here in case it helps out anybody else.  Plenty of explanations are in comments.

     

     

    Option Explicit
    
    ' Multi-lingual programming in Excel is horrible in general, but multi-lingual dropdowns (aka
    ' Data Validation) are a particular challenge.
    
    ' The validation list is easy enough to create.  We use a simple two-column range somewhere in
    ' the spreadsheet:
    
    '     xxxx yyyy
    '     xxxx yyyy
    '     xxxx yyyy
    
    ' This range is the mapChoices below.  We can then proceed to install
    
    '   =INDEX(mapChoices,0,1)
    
    ' as the list of valid choices for Excel's Data Validation.  In the final step of a single-
    ' language spreadsheet, we would then obtain yyyy for a given (valid) xxxx by simple
    
    '   =VLOOKUP("xxxx",mapChoises,2,FALSE)
    
    ' Easy enough.  But all hell breaks loose when xxxx in mapChoices is itself the result of a
    ' lookup in some dictionary.  That is, the Data Validation is localized.
    
    ' In this case, when an English user saves  the spreadsheet with his (valid) input of "Pork",
    ' this input must translate into "Schweinefleisch" for a German user of that same spreadsheet
    ' because "Pork" is no longer a valid choice among the German translations of mapChoices.
    
    ' Use validItem in lieu of VLOOKUP to achieve this functionality.  The logic behind validItem
    ' is that the current code yyyy MUST BE a valid choice - from whichever xxxx it was oringally
    ' obtained.  If that is so, then  at any time,  the correct current xxxx is simply that which
    ' corresponds to the given yyyy (ie a reverse VLOOKUP).
    
    ' Now, there are countless mechanical challenges within this trivial logic:
    '   1 Excel will not reveal the value of thisCell and reward any access attempt with a "ciruclar
    '     reference" error.  That is, I cannot find yyyy.  To work around this irriating problem, I
    '     must duplicate yyyy somewhere in the spreadsheet.  The inputs dRow and dCol allow you to
    '     designate the caching cell by relative reference to the current cell.  A call like
    
    '       =validItem(E13,MapCHOICES,0,1)
    
    '     will cache yyyy one cell to the right of the call.  BE CAREFUL:  DO NOT place the cache
    '     anywhere where its update might prompt an unnecessary calculation !!
    
    '   2 Excel will not allow a FUNCTION to manipulate any cell in the Worksheet.  Therefore, the
    '     duplication of yyyy must be delegated to a SUB.
    
    '   3 The same applies to the first input into validItem - the range reference to xxxx.  validItem
    '     cannot alter that value (xxxx).
    
    '   To make 2 + 3 work, I record instructions into two dictionaries.  Any Worksheet that wants to
    '   use validItem must include the follwoing event code into its VBA event module:
    
    ' Private Sub Worksheet_Calculate()
    '     sweepDynamicDataValidations Me.Name
    ' End Sub
    
    '   The macro sweepDynamicDataValidations will then process any instructions, updating cache values
    '   and dropdown inputs where required.
    
    ' NOTES:
    ' Requires a reference to the Microsoft Scripting Runtime
    ' NEVER reference the cache in any calculation!  Your downstream programming must exclusively
    '   reference validItem.
    ' When you use validItem in a Workbook set to Manual Calculation, you will sometimes find that
    ' after F9 finishes a calculation, the Calculate indicator remains on.  That is because
    ' sweepDynamicDataValidations has leapt into action and altered some cells.  A second
    ' F9 should then eventually extinguish the Calculate indicator.
    
    Dim ddvDict As New Dictionary
    Dim cacheDict As New Dictionary
    
    Public Sub sweepDynamicDataValidations(sn As String)
        Dim ws As Worksheet
        Set ws = ThisWorkbook.Worksheets(sn)
        
        Dim d As Dictionary
        Dim k As Variant, v As Variant
            
        If ddvDict.Exists(sn) Then
            Set d = ddvDict(sn)
    '       Processing updates of all input cells after a mapChoices change
            For Each k In d.keys
                v = d(k)
                d.Remove k
                ws.Range(k) = v
            Next
        End If
        
        If cacheDict.Exists(sn) Then
            Set d = cacheDict(sn)
    '       Processing updates of the cache cells where a user selection was changed
            For Each k In d.keys
                v = d(k)
                d.Remove k
                ws.Range(k) = v
            Next
        End If
    End Sub
    
    Public Function validItem(aChoice As Range, mapChoices As Range, dRow As Long, dCol As Long) As Variant
        Dim c As Range
        Set c = Application.ThisCell
        
        Dim sn As String
        sn = c.Parent.Name
        
        Dim ws As Worksheet
        Set ws = ThisWorkbook.Worksheets(sn)
        
        Dim cacheAddr As String
        cacheAddr = ws.Cells(c.Row + dRow, c.Column + dCol).Address
        
        If cacheAddr = c.Address Then
            validItem = [#VALUE!]
            Exit Function
        End If
        
        Dim cache As Range
        Set cache = ws.Range(cacheAddr)
            
        Dim nChoices As Long
        nChoices = mapChoices.Rows.Count
        
    '   Let's perform a quick VLOOKUP(aChoice,mapChoises,2,FALSE)
        
        Dim iChoice As Long
        For iChoice = 1 To nChoices
            If aChoice.Value = mapChoices(iChoice, 1).Value Then
                validItem = mapChoices(iChoice, 2)
                
    '           Let's do a wee optimization here: Instruct an update of the cache only
    '           when the user selection was changed. Doesn't just reduce CPU cycle, it
    '           also eliminates most repeat calculations.
    
                If cache.Value <> validItem Then
                    If Not cacheDict.Exists(sn) Then Set cacheDict(sn) = New Dictionary
                    
    '               Creating an instruction for sweepDynamicDataValidations to update the
    '               cell at cacheAddr with the value of validItem.
                    cacheDict(sn)(cacheAddr) = validItem
                End If
                
                Exit Function
            End If
        Next
        
        validItem = cache.Value
    
    '   We only find here if aChoice does not feature in mapChoices. This cannot be - the
    '   whole point of Data Validation is to make this impossible - UNLESS we were called
    '   following a change to mapChoices.  Now, the whole point of mapChoices is to map a
    '   user-friendly input (in column 1) into a code-friendly token (in column 2).  That
    '   is, the token in cache (from a previous  valid selection  among different choices
    '   in column 1) must allow us to  establish the current user-friendly input for this
    '   selection.
    
        For iChoice = 1 To nChoices
            If mapChoices(iChoice, 2).Value = cache.Value Then
    
    '           Hurrah - we found the code.  mapChoices(iChoice,1) is hence the dropdown
    '           label that currently corresponds to the user's selection. Let's schedule
    '           an update by sweepDynamicDataValidations of aChoice with that new label.
    
                If Not ddvDict.Exists(sn) Then Set ddvDict(sn) = New Dictionary
                ddvDict(sn)(aChoice.Address) = mapChoices(iChoice, 1).Value
                
                Exit Function
            End If
        Next
        
    '   Well, that's odd...
        validItem = [#VALUE!]
    End Function

     

     

     

    I also attach a little demo including this code.  It works well-enough and isn't heavy.  I will try it in production.  See how it goes.  Thanks.

     

    PS:  There is a mild hazard in Manual Calculation.  It is possible for the user to change his selection in English, NOT calculate and then change the language selection to German.  In that bizarre case, sweepDynamicDataValidations will reset the user input to the German word for the previous selection.

     

  • mathetes's avatar
    mathetes
    Silver Contributor

    ecovonrein 

     

    Intriguing question. I can conceive of a possible solution for the drop-down, such that the options presented could vary based on language. (It would be a variation on the "Three levels" tab in the attached demo sheet. It does rely on some of the functions only available in quite new versions of Excel.) Although whether a prior selection of "Roast Beef" would appear as "Rinderbraten" when opened by a user speaking German....that may be asking too much (for my skill level at any rate).

     

    BUT some of the utility of this would depend on what happens downstream of the selection.

    • For example, although it would not be hard to offer the speaker of English options for
      • Beef
      • Pork
      • Chicken
    • and the German
      • Rindfleisch
      • Schweinefleisch 
      • Huhn

    .... would it be necessary to adjust formulas downstream to account for the different languages. If, for example, there was a need to look up ingredients for a recipe using the chosen meat, would there be multiple languages for tables the VLOOKUP or XLOOKUP formulas would search?

     

    Would you be at liberty to explain the larger context here? I somehow suspect it doesn't really have to do with selections of meat dishes.

    • ecovonrein's avatar
      ecovonrein
      Iron Contributor

      mathetesSorry, John, there really isn't any magic to his.  Sure, my actual selection has nothing to do with meat, but it might as well.  It really does not matter.  Anyway, in the absence of any feedback from fellow travellers, I thought of a VBA solution to the problem.   I attach it here in case it helps out anybody else.  Plenty of explanations are in comments.

       

       

      Option Explicit
      
      ' Multi-lingual programming in Excel is horrible in general, but multi-lingual dropdowns (aka
      ' Data Validation) are a particular challenge.
      
      ' The validation list is easy enough to create.  We use a simple two-column range somewhere in
      ' the spreadsheet:
      
      '     xxxx yyyy
      '     xxxx yyyy
      '     xxxx yyyy
      
      ' This range is the mapChoices below.  We can then proceed to install
      
      '   =INDEX(mapChoices,0,1)
      
      ' as the list of valid choices for Excel's Data Validation.  In the final step of a single-
      ' language spreadsheet, we would then obtain yyyy for a given (valid) xxxx by simple
      
      '   =VLOOKUP("xxxx",mapChoises,2,FALSE)
      
      ' Easy enough.  But all hell breaks loose when xxxx in mapChoices is itself the result of a
      ' lookup in some dictionary.  That is, the Data Validation is localized.
      
      ' In this case, when an English user saves  the spreadsheet with his (valid) input of "Pork",
      ' this input must translate into "Schweinefleisch" for a German user of that same spreadsheet
      ' because "Pork" is no longer a valid choice among the German translations of mapChoices.
      
      ' Use validItem in lieu of VLOOKUP to achieve this functionality.  The logic behind validItem
      ' is that the current code yyyy MUST BE a valid choice - from whichever xxxx it was oringally
      ' obtained.  If that is so, then  at any time,  the correct current xxxx is simply that which
      ' corresponds to the given yyyy (ie a reverse VLOOKUP).
      
      ' Now, there are countless mechanical challenges within this trivial logic:
      '   1 Excel will not reveal the value of thisCell and reward any access attempt with a "ciruclar
      '     reference" error.  That is, I cannot find yyyy.  To work around this irriating problem, I
      '     must duplicate yyyy somewhere in the spreadsheet.  The inputs dRow and dCol allow you to
      '     designate the caching cell by relative reference to the current cell.  A call like
      
      '       =validItem(E13,MapCHOICES,0,1)
      
      '     will cache yyyy one cell to the right of the call.  BE CAREFUL:  DO NOT place the cache
      '     anywhere where its update might prompt an unnecessary calculation !!
      
      '   2 Excel will not allow a FUNCTION to manipulate any cell in the Worksheet.  Therefore, the
      '     duplication of yyyy must be delegated to a SUB.
      
      '   3 The same applies to the first input into validItem - the range reference to xxxx.  validItem
      '     cannot alter that value (xxxx).
      
      '   To make 2 + 3 work, I record instructions into two dictionaries.  Any Worksheet that wants to
      '   use validItem must include the follwoing event code into its VBA event module:
      
      ' Private Sub Worksheet_Calculate()
      '     sweepDynamicDataValidations Me.Name
      ' End Sub
      
      '   The macro sweepDynamicDataValidations will then process any instructions, updating cache values
      '   and dropdown inputs where required.
      
      ' NOTES:
      ' Requires a reference to the Microsoft Scripting Runtime
      ' NEVER reference the cache in any calculation!  Your downstream programming must exclusively
      '   reference validItem.
      ' When you use validItem in a Workbook set to Manual Calculation, you will sometimes find that
      ' after F9 finishes a calculation, the Calculate indicator remains on.  That is because
      ' sweepDynamicDataValidations has leapt into action and altered some cells.  A second
      ' F9 should then eventually extinguish the Calculate indicator.
      
      Dim ddvDict As New Dictionary
      Dim cacheDict As New Dictionary
      
      Public Sub sweepDynamicDataValidations(sn As String)
          Dim ws As Worksheet
          Set ws = ThisWorkbook.Worksheets(sn)
          
          Dim d As Dictionary
          Dim k As Variant, v As Variant
              
          If ddvDict.Exists(sn) Then
              Set d = ddvDict(sn)
      '       Processing updates of all input cells after a mapChoices change
              For Each k In d.keys
                  v = d(k)
                  d.Remove k
                  ws.Range(k) = v
              Next
          End If
          
          If cacheDict.Exists(sn) Then
              Set d = cacheDict(sn)
      '       Processing updates of the cache cells where a user selection was changed
              For Each k In d.keys
                  v = d(k)
                  d.Remove k
                  ws.Range(k) = v
              Next
          End If
      End Sub
      
      Public Function validItem(aChoice As Range, mapChoices As Range, dRow As Long, dCol As Long) As Variant
          Dim c As Range
          Set c = Application.ThisCell
          
          Dim sn As String
          sn = c.Parent.Name
          
          Dim ws As Worksheet
          Set ws = ThisWorkbook.Worksheets(sn)
          
          Dim cacheAddr As String
          cacheAddr = ws.Cells(c.Row + dRow, c.Column + dCol).Address
          
          If cacheAddr = c.Address Then
              validItem = [#VALUE!]
              Exit Function
          End If
          
          Dim cache As Range
          Set cache = ws.Range(cacheAddr)
              
          Dim nChoices As Long
          nChoices = mapChoices.Rows.Count
          
      '   Let's perform a quick VLOOKUP(aChoice,mapChoises,2,FALSE)
          
          Dim iChoice As Long
          For iChoice = 1 To nChoices
              If aChoice.Value = mapChoices(iChoice, 1).Value Then
                  validItem = mapChoices(iChoice, 2)
                  
      '           Let's do a wee optimization here: Instruct an update of the cache only
      '           when the user selection was changed. Doesn't just reduce CPU cycle, it
      '           also eliminates most repeat calculations.
      
                  If cache.Value <> validItem Then
                      If Not cacheDict.Exists(sn) Then Set cacheDict(sn) = New Dictionary
                      
      '               Creating an instruction for sweepDynamicDataValidations to update the
      '               cell at cacheAddr with the value of validItem.
                      cacheDict(sn)(cacheAddr) = validItem
                  End If
                  
                  Exit Function
              End If
          Next
          
          validItem = cache.Value
      
      '   We only find here if aChoice does not feature in mapChoices. This cannot be - the
      '   whole point of Data Validation is to make this impossible - UNLESS we were called
      '   following a change to mapChoices.  Now, the whole point of mapChoices is to map a
      '   user-friendly input (in column 1) into a code-friendly token (in column 2).  That
      '   is, the token in cache (from a previous  valid selection  among different choices
      '   in column 1) must allow us to  establish the current user-friendly input for this
      '   selection.
      
          For iChoice = 1 To nChoices
              If mapChoices(iChoice, 2).Value = cache.Value Then
      
      '           Hurrah - we found the code.  mapChoices(iChoice,1) is hence the dropdown
      '           label that currently corresponds to the user's selection. Let's schedule
      '           an update by sweepDynamicDataValidations of aChoice with that new label.
      
                  If Not ddvDict.Exists(sn) Then Set ddvDict(sn) = New Dictionary
                  ddvDict(sn)(aChoice.Address) = mapChoices(iChoice, 1).Value
                  
                  Exit Function
              End If
          Next
          
      '   Well, that's odd...
          validItem = [#VALUE!]
      End Function

       

       

       

      I also attach a little demo including this code.  It works well-enough and isn't heavy.  I will try it in production.  See how it goes.  Thanks.

       

      PS:  There is a mild hazard in Manual Calculation.  It is possible for the user to change his selection in English, NOT calculate and then change the language selection to German.  In that bizarre case, sweepDynamicDataValidations will reset the user input to the German word for the previous selection.

       

      • mathetes's avatar
        mathetes
        Silver Contributor
        Impressive. Unfortunately, I don't speak VBA, not fluently at any rate.

Resources