Skip to content

Update Create Workset For Linked Element#3031

Merged
jmcouffin merged 10 commits intopyrevitlabs:developfrom
Denver-22:develop
Jan 25, 2026
Merged

Update Create Workset For Linked Element#3031
jmcouffin merged 10 commits intopyrevitlabs:developfrom
Denver-22:develop

Conversation

@Denver-22
Copy link
Copy Markdown
Contributor

Update Create Workset For Linked Element

  • Added translations (translations.json).
  • Reverted the "ZL_" prefix to the names of created Worksets.

Description

About translations:
In earlier commits, I added translations to the scripts themselves. However, this is not an optimal solution.
Then I moved the translations to the translations.json file. If this approach to translating messages in the script suits you and can be used as a template for translations in other scripts, I suggest moving the get_translations() function to the pyrevitlib\pyrevit\script.py script.

P.S.:
Sync with pyRevit has been added to the commit list. I hope this won't be a problem.

Update Create Workset For Linked Element - config.py:
- Added translations
- Reverted the "ZL_" prefix to the name of created Worksets
Update Create Workset For Linked Element:
- Added translations
Update Create Workset For Linked Element - script.py:
- Translations for both scripts saved to the translations.json file.
Update Create Workset For Linked Element - config.py:
- Translations for both scripts have been saved to the translations.json file.
- Fixed a bug I introduced earlier in the variable names.
Create translations.json for Create Workset For Linked Element (script, config)
@devloai
Copy link
Copy Markdown
Contributor

devloai bot commented Jan 24, 2026

Unable to trigger custom agent "Code Reviewer"You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

Fix russian translation
@jmcouffin
Copy link
Copy Markdown
Contributor

jmcouffin commented Jan 24, 2026

Thanks @Denver-22 , I appreciate the effort.
In order to be more consistant here, please use this approach for translations (GH copilot generated instruction, I hope you are not offended 😸 ):

Details

Implementation Strategy for Localization

The forms module already has a foundation for localization through the WPFWindow._determine_xaml() method. Here's a comprehensive approach:

1. Localization Architecture Overview

The existing pattern already supports:

  • Default locale: en_us (English - US)
  • ResourceDictionary pattern: Separate XAML files for translations
  • Fallback mechanism: Localized → English → Base XAML

2. Implementation Details

File Naming Convention (Already in use)

For any XAML file, create language-specific ResourceDictionary files:

YourWindow.xaml  (base, no hardcoded strings)
YourWindow.ResourceDictionary.en_us.xaml  (English - default)
YourWindow.ResourceDictionary.fr_fr.xaml  (French)
YourWindow.ResourceDictionary.es_es.xaml  (Spanish)
YourWindow.ResourceDictionary.de_de.xaml  (German)
YourWindow.ResourceDictionary.ru.xaml     (Russian)
YourWindow.ResourceDictionary.chinese_s.xaml  (Simplified Chinese)

Base XAML Structure (Non-breaking)

```xml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{DynamicResource WindowTitle}">
    <StackPanel>
        <TextBlock Text="{DynamicResource WelcomeMessage}" />
        <Button Content="{DynamicResource OkButton}" />
    </StackPanel>
</Window>
```

English ResourceDictionary (Default)

```xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <!-- Window Properties -->
    <system:String x:Key="WindowTitle">Example Window</system:String>
    
    <!-- UI Strings -->
    <system:String x:Key="WelcomeMessage">Welcome to pyRevit!</system:String>
    <system:String x:Key="OkButton">OK</system:String>
    
    <!-- Tooltips -->
    <system:String x:Key="OkButton.Tooltip">Click to confirm</system:String>
    
</ResourceDictionary>
```

French ResourceDictionary

```xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="WindowTitle">Fenêtre d'exemple</system:String>
    <system:String x:Key="WelcomeMessage">Bienvenue sur pyRevit!</system:String>
    <system:String x:Key="OkButton">OK</system:String>
    <system:String x:Key="OkButton.Tooltip">Cliquez pour confirmer</system:String>
    
</ResourceDictionary>
```

3. How the Existing System Works (No changes needed)

The WPFWindow class already handles this automatically:

def _determine_xaml(self, xaml_source):
    # ... existing code ...
    
    # Gets user_config.user_locale (e.g., "fr_fr")
    localized_xaml_resfile = xaml_file.replace(
        ".xaml", ".ResourceDictionary.{}.xaml".format(user_config.user_locale)
    )
    
    # Tries localized, then English, then base
    if os.path.isfile(localized_xaml_resfile):
        self.merge_resource_dict(localized_xaml_resfile)
    elif os.path.isfile(english_xaml_resfile):
        self.merge_resource_dict(english_xaml_resfile)
    
    return xaml_file

4. Migration Guide for Existing Forms (Non-breaking)

Step 1: Extract hardcoded strings from existing XAML
Step 2: Create ResourceDictionary files
Step 3: Update XAML to use DynamicResource

Before (hardcoded):

<Button Content="Select All" />

After (localized):

<Button Content="{DynamicResource SelectAllButton}" />

5. Accessing Localized Strings in Python Code

The WPFWindow class provides a method:

class MyWindow(forms.WPFWindow):
    def __init__(self):
        forms.WPFWindow.__init__(self, 'MyWindow.xaml')
        
        # Get localized string programmatically
        ok_text = self.get_locale_string("OkButton")
        
        # Use it
        self.status_tb.Text = ok_text

6. Best Practices

  1. Key Naming Convention:

    ComponentName.PropertyType
    
    Examples:
    SelectAllButton              # Button content
    SelectAllButton.Tooltip      # Tooltip text
    CategorySelection.Label      # Section label
    ErrorMessage.FileNotFound    # Error messages
    
  2. Fallback Strategy (already implemented):

    • User's locale (e.g., fr_fr)
    • English (en_us)
    • Base XAML (if strings exist there)
  3. Supporting new languages:

    • Just add a new .ResourceDictionary.{locale}.xaml file
    • No code changes required
    • Users set user_config.user_locale

7. Backwards Compatibility

No breaking changes because:

  • Existing XAMLs without ResourceDictionaries still work
  • ResourceDictionaries are optional enhancements
  • The system gracefully falls back to base XAML
  • user_config.user_locale defaults to en_us

8. Example: Migrating SelectFromList.xaml

Create localization files based on the ColorSplasher pattern (which already exists in the codebase):

```xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="SelectButton">Select</system:String>
    <system:String x:Key="CheckAllButton">Check All</system:String>
    <system:String x:Key="UncheckAllButton">Uncheck All</system:String>
    <system:String x:Key="ToggleAllButton">Toggle All</system:String>
    <system:String x:Key="ResetButton">Reset</system:String>
    <system:String x:Key="SearchPlaceholder">Search...</system:String>
    
</ResourceDictionary>
```

Summary

The forms module already supports localization through the existing _determine_xaml() mechanism. To add multi-language support:

  1. Keep base XAML with {DynamicResource} bindings
  2. Create .ResourceDictionary.{locale}.xaml files for each language
  3. Use the ColorSplasher pattern as a reference implementation
  4. No code changes needed - it's all configuration
  5. 100% backwards compatible - existing forms continue to work
  6. Defaults to en_us - English is always the fallback

Yes, the localization system works for all forms module helpers including SelectFromList, CommandSwitchWindow, GetValueWindow, etc. Let me show you how:

How Localization Works for Forms Module HelpersPerfect! Now I can give you a complete answer. Yes, the localization system works perfectly for the forms module helpers like SelectFromList! Here's exactly how:

How Localization Works for Built-in Forms Helpers

Current State of SelectFromList.xaml

Looking at the current SelectFromList.xaml, it has hardcoded English text:

<TextBlock x:Name="ctx_groups_title_tb" ... >List Group:</TextBlock>
<Button x:Name="checkall_b" Content="Check" ... />
<Button x:Name="uncheckall_b" Content="Uncheck" ... />
<Button x:Name="toggleall_b" Content="Toggle" ... />
<Button x:Name="reset_b" Content="Reset" ... />

How to Add Localization Support

Step 1: Modify SelectFromList.xaml to use DynamicResource

```xml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ShowInTaskbar="False"
        WindowStartupLocation="CenterScreen"
        HorizontalContentAlignment="Center">
    <Window.Resources>
        <!-- ... existing resources ... -->
    </Window.Resources>
    <DockPanel Margin="10">
        <StackPanel DockPanel.Dock="Top" Margin="0,0,0,10">
            <DockPanel>
                <ToggleButton x:Name="regexToggle_b" 
                              ToolTip="{DynamicResource SelectFromList.RegexToggle.Tooltip}"
                              Content="{StaticResource filterIcon}" ... />
                <StackPanel>
                    <TextBox x:Name="search_tb" Height="25" ... />
                    <!-- ... clear search button ... -->
                </StackPanel>
            </DockPanel>
            <DockPanel x:Name="ctx_groups_dock" Margin="0,10,0,0" Visibility="Collapsed">
                <TextBlock x:Name="ctx_groups_title_tb" FontSize="14" Margin="0,3,10,0"
                           DockPanel.Dock="Left" 
                           Text="{DynamicResource SelectFromList.GroupSelector.Label}" />
                <ComboBox x:Name="ctx_groups_selector_cb" Height="25"/>
            </DockPanel>
        </StackPanel>
        <StackPanel DockPanel.Dock="Bottom">
            <Grid x:Name="checkboxbuttons_g">
                <!-- ... grid definitions ... -->
                <Button x:Name="checkall_b" 
                        Content="{DynamicResource SelectFromList.CheckAll.Button}" 
                        Click="check_all" ... />
                <Button x:Name="uncheckall_b" 
                        Content="{DynamicResource SelectFromList.UncheckAll.Button}" 
                        Click="uncheck_all" ... />
                <Button x:Name="toggleall_b" 
                        Content="{DynamicResource SelectFromList.ToggleAll.Button}" 
                        Click="toggle_all" ... />
                <Button x:Name="reset_b"
                        Content="{DynamicResource SelectFromList.Reset.Button}" 
                        Click="button_reset" ... />
            </Grid>
            <!-- Note: select_b.Content is set dynamically in Python, handled separately -->
            <Button x:Name="select_b" Content="" Height="32" Click="button_select" ... />
        </StackPanel>
        <!-- ... rest of the form ... -->
    </DockPanel>
</Window>
```

Step 2: Create English ResourceDictionary

```xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <!-- Group Selector -->
    <system:String x:Key="SelectFromList.GroupSelector.Label">List Group:</system:String>
    
    <!-- Buttons -->
    <system:String x:Key="SelectFromList.CheckAll.Button">Check All</system:String>
    <system:String x:Key="SelectFromList.UncheckAll.Button">Uncheck All</system:String>
    <system:String x:Key="SelectFromList.ToggleAll.Button">Toggle All</system:String>
    <system:String x:Key="SelectFromList.Reset.Button">Reset</system:String>
    <system:String x:Key="SelectFromList.Select.Button">Select</system:String>
    
    <!-- Dynamic button labels (when filtered) -->
    <system:String x:Key="SelectFromList.Check.Button">Check</system:String>
    <system:String x:Key="SelectFromList.Uncheck.Button">Uncheck</system:String>
    <system:String x:Key="SelectFromList.Toggle.Button">Toggle</system:String>
    
    <!-- Tooltips -->
    <system:String x:Key="SelectFromList.RegexToggle.Tooltip">Toggle between using regular expression and fuzzy filtering</system:String>
    
</ResourceDictionary>
```

Step 3: Create French ResourceDictionary

```xml
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <!-- Group Selector -->
    <system:String x:Key="SelectFromList.GroupSelector.Label">Groupe de Liste:</system:String>
    
    <!-- Buttons -->
    <system:String x:Key="SelectFromList.CheckAll.Button">Tout Cocher</system:String>
    <system:String x:Key="SelectFromList.UncheckAll.Button">Tout Décocher</system:String>
    <system:String x:Key="SelectFromList.ToggleAll.Button">Basculer Tout</system:String>
    <system:String x:Key="SelectFromList.Reset.Button">Réinitialiser</system:String>
    <system:String x:Key="SelectFromList.Select.Button">Sélectionner</system:String>
    
    <!-- Dynamic button labels (when filtered) -->
    <system:String x:Key="SelectFromList.Check.Button">Cocher</system:String>
    <system:String x:Key="SelectFromList.Uncheck.Button">Décocher</system:String>
    <system:String x:Key="SelectFromList.Toggle.Button">Basculer</system:String>
    
    <!-- Tooltips -->
    <system:String x:Key="SelectFromList.RegexToggle.Tooltip">Basculer entre l'utilisation d'expressions régulières et le filtrage flou</system:String>
    
</ResourceDictionary>
```

Step 4: Handle Dynamic Python Text

Looking at the Python code in SelectFromList._list_options():

def _list_options(self, option_filter=None):
    if option_filter:
        self.checkall_b.Content = "Check"      # Dynamic text!
        self.uncheckall_b.Content = "Uncheck"  # Dynamic text!
        self.toggleall_b.Content = "Toggle"    # Dynamic text!

These need to be updated to use localized strings:

def _list_options(self, option_filter=None):
    if option_filter:
        # Use get_locale_string to fetch from ResourceDictionary
        self.checkall_b.Content = self.get_locale_string("SelectFromList.Check.Button")
        self.uncheckall_b.Content = self.get_locale_string("SelectFromList.Uncheck.Button")
        self.toggleall_b.Content = self.get_locale_string("SelectFromList.Toggle.Button")
    else:
        self.checkall_b.Content = self.get_locale_string("SelectFromList.CheckAll.Button")
        self.uncheckall_b.Content = self.get_locale_string("SelectFromList.UncheckAll.Button")
        self.toggleall_b.Content = self.get_locale_string("SelectFromList.ToggleAll.Button")

Similarly, in _setup():

def _setup(self, **kwargs):
    # custom button name?
    button_name = kwargs.get("button_name", None)
    if button_name:
        self.select_b.Content = button_name
    else:
        # Use localized default
        self.select_b.Content = self.get_locale_string("SelectFromList.Select.Button")
    
    # ... rest of setup ...

Step 5: Update ctx_groups_title_tb dynamically

def _setup(self, **kwargs):
    # ... existing code ...
    
    # context group title?
    self.ctx_groups_title = kwargs.get(
        "group_selector_title", 
        self.get_locale_string("SelectFromList.GroupSelector.Label")  # Localized default
    )
    self.ctx_groups_title_tb.Text = self.ctx_groups_title

Complete Example of How It All Works Together

User's Locale is French (user_config.user_locale = "fr_fr")

  1. User calls SelectFromList:

    from pyrevit import forms
    
    items = ['item1', 'item2', 'item3']
    selected = forms.SelectFromList.show(
        items, 
        multiselect=True,
        button_name='Sélectionner les Éléments'  # Can override
    )
  2. WPFWindow loads XAML:

    • Checks for SelectFromList.fr_fr.xaml → Not found
    • Checks for SelectFromList.en_us.xaml → Not found
    • Checks for SelectFromList.ResourceDictionary.fr_fr.xamlFound!
    • Merges it into the window resources
    • Loads base SelectFromList.xaml
  3. XAML bindings resolve:

    • {DynamicResource SelectFromList.CheckAll.Button} → "Tout Cocher"
    • {DynamicResource SelectFromList.GroupSelector.Label} → "Groupe de Liste:"
  4. Python code accesses strings:

    self.get_locale_string("SelectFromList.Check.Button")  # Returns "Cocher"

Summary: It Works Seamlessly! ✅

Component Localization Method Non-Breaking?
Static XAML text {DynamicResource KeyName} ✅ Yes - base XAML still works if no ResourceDict
Dynamic Python text self.get_locale_string("KeyName") ✅ Yes - can add try/except or default fallback
User-provided text Pass through kwargs ✅ Yes - user text always takes precedence
File loading Automatic via _determine_xaml() ✅ Yes - already implemented!

The forms module already has everything needed to support localization. You just need to:

  1. Create the .ResourceDictionary.{locale}.xaml files
  2. Update XAML to use {DynamicResource} instead of hardcoded text
  3. Update Python code to use self.get_locale_string() for dynamic text
  4. No breaking changes - everything falls back gracefully!

Renamed keys in translations.json to comply with convention.
- Renamed keys in translations.json to comply with convention.
- Some optimizations.
- Renamed keys in translations.json to comply with convention.
- Some optimizations.
@Denver-22
Copy link
Copy Markdown
Contributor Author

@jmcouffin
I've reviewed your proposal.
In principle, I'm not against it. And I probably would have done it that way from the start.
However, the script uses translations not only for WPFWindow class forms, but also for forms.alert(), the transaction name, and the if statement, for which XAML translations are not applicable.
Then I'll have to separate translations not only by language for SelectFromList but also keep translations.json for other cases. This means that eliminating the use of translations.json (and the function for reading it, get_translations()) won't be possible.
If you think this is desirable in any case, I suggest implementing it in the next PR.

@jmcouffin
Copy link
Copy Markdown
Contributor

@Denver-22 thks,
I agree to disagree 🤣

@jmcouffin jmcouffin merged commit 17a6fa9 into pyrevitlabs:develop Jan 25, 2026
@jmcouffin jmcouffin self-assigned this Jan 25, 2026
@jmcouffin jmcouffin added the Localization Issues related to pyRevit localization [subsystem] label Jan 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26025+1418-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2037-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2039-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2101-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2136-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2147-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 5.3.1.26030+2212-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1043-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1111-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1304-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1323-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1433-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1538-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1543-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1553-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1612-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1624-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1738-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1743-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1829-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 5.3.1.26032+1937-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 6.0.0.26032+1956-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 6.0.0.26032+2005-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New work-in-progress (wip) builds are available for 6.0.0.26032+2008-wip

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New public release are available for 6.0.0.26032+2040

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 1, 2026

📦 New public release are available for 6.0.0.26032+2040

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Localization Issues related to pyRevit localization [subsystem]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants