LanguageHandler folder structure

I’m developing my first extension, but I’ve run into an issue where I need to use a global variable. Arghhh!

The issue is with the LanguageHandler class, it seems it must be instantiated in the top level ruby file (in the Plugins folder), not in the sub-folder.

I’ve applied to be a developer, but I don’t yet have access to relevant documentation.
e.g. Homepage | SketchUp Developer

Folder structure is, I hope, in accordance with requirements:

.../Plugins/centaur_gltf_export.rb
.../Plugins/centaur_gltf_export/gltfExport.rb
.../Plugins/centaur_gltf_export/Resources/en-US/centaur_gltf_export.strings
.../Plugins/centaur_gltf_export/Resources/fr/centaur_gltf_export.strings
.../Plugins/centaur_gltf_export/Resources/ja/centaur_gltf_export.strings
.../Plugins/centaur_gltf_export/Resources/zh-CN/centaur_gltf_export.strings

And my code that creates the LangHandler class is in the top level ruby:

  require 'sketchup.rb'
  require 'extensions.rb'

  module Centaur
	module GltfExporter
		unless file_loaded?(__FILE__)
		
			# this global is passed to the lower rb files, because LanguageHandler.new works at this directory level
			$centaur_gltf_export_translate = LanguageHandler.new('centaur_gltf_export.strings')
			
			ex = SketchupExtension.new('glTF Exporter', 'centaur_gltf_export/gltfExport')
			ex.description = $centaur_gltf_export_translate["description"]
			ex.version     = '1.0.0'
			ex.copyright   = '©2017'
			ex.creator     = 'Centaur'
			Sketchup.register_extension(ex, true)
			file_loaded(__FILE__)
		end
	end
  end

I’d like to get rid of the $centaur_gltf_export_translate global, but creating the LanguageHandler in the gltfExport.rb file fails, or rather, doesn’t find anything, due to it residing in the sub-folder.

Thanks in advance!

You could use a constant in your top level namespace to point at the LangHandler object instead of using a global variable.

I first saw this in ThomThom’s code and until that point I had never really thought of that constants can point to any object, not just primitive data types.

Regarding the Permission denied I also get the same error. The docs work in mysterious ways.

I didn’t quite follow, a constant is just a variable written in all caps, right?
Replacing $centaur_gltf_export_translate with TRANSLATOR merely gave me an error about an uninitialized constant in the lower level module.

  module Centaur
	module GltfExporter

		
		
		# add item if menu is not already loaded
		unless file_loaded?(__FILE__)
			main_menu = UI.menu("Plugins").add_submenu($centaur_gltf_export_translate['menuMain'])
			main_menu.add_item($centaur_gltf_export_translate['menuGltf']) { GltfExport.new.Export(false) }
			main_menu.add_item($centaur_gltf_export_translate['menuGlb']) { GltfExport.new.Export(true) }
			file_loaded(__FILE__)
		end

		
		class GltfExport

Maybe the global is acceptable for publishing the script?

I’m using a constant this one…

# =begin
#
# Copyright 2016, john@drivenupthewall.co.uk
#
# All Rights Reserved - for now...
#
# =end
module JcB
  # plugin namespace for all methods...
  module SineCircle

    NAME =  'sine_circle'
    TITLE = 'Sine Circle'
    VERSION = '1.0.3'
    PATH = __dir__ rescue File.dirname(__FILE__)

    # pactched languagehandler.rb for earlier versions
		require (File.join(PATH, "jcb_#{NAME}/bin/lang_patch")) if Sketchup.version.to_i < 14

		TRANSL8 = LanguageHandler.new( "jcb_#{NAME}.strings")

		extension_name = TRANSL8["#{TITLE}"]

		PLUGIN = SketchupExtension.new(extension_name, "jcb_#{NAME}/#{NAME}_menu")

    # translatable text on separate lines for easy extraction [aviod quotation marks]
    PLUGIN.description =
     TRANSL8['This plugin allows the creation of cylindercal sinusoldal curves'] + ".\n" +
		 TRANSL8['It preserves settings per Session'] + ".\n" +
		 TRANSL8['It is advised to work at a large scale [use meters as if millimetres]'] + ".\n" +
		 TRANSL8['The string file can be translated'] + "..."

		PLUGIN.version   = VERSION

		PLUGIN.copyright = '2016 john@drivenupthewall.co.uk'

		PLUGIN.creator   = 'john@drivenupthewall.co.uk'

		Sketchup.register_extension( PLUGIN, true )

		load File.join(PATH, File.basename(__FILE__, '.rb'), "version_#{VERSION}.rb")

  end
end

then

# an attempt at a generic menu loader that needs only the lang file ...
module JcB
  # the menu ruby loads the logic ruby on demand...
  module SineCircle
    extend self

    # add rescue for pre Ruby 2
    path = __dir__ rescue File.dirname(__FILE__)

    # to handle the new file formats for toolbar images...
    ext = '.png'
    ver = Sketchup.version.to_i
    if ver >= 16
      osx = Sketchup.platform == :platform_osx
      ext = osx ? '.pdf' : '.svg'
    end

    title =  TRANSL8["#{TITLE}"]
    # create toolbar
    tb = UI::Toolbar.new(title)
    # menu item
    menu = UI.menu('Plugins', title)

    # add a cmd for menu, toolbar or shortcut item...
    cmd = UI::Command.new(title) do
      # use load for testing locally and require for production without...
      load File.join(path, "#{NAME}_logic.rb") if ENV["LOGNAME"] == "johns_iMac"
      require File.join(path, "#{NAME}_logic")
      # this method becomes available on load as it's defined under the same namespace...
      user_input
    end

    cmd.tooltip = cmd.status_bar_text =  TRANSL8['Create sine waves on cylinders'] + '...'

    cmd.large_icon = cmd.small_icon = File.join(path, 'Resources', 'images', "#{NAME}#{ext}")

    unless file_loaded? "#{NAME}_menu.rb"
      menu.add_item(cmd)
      tb.add_item(cmd)
      # showing the toolbar
      tb.get_last_state == -1 ? tb.show : tb.restore
      file_loaded("#{NAME}_menu.rb")
    end # unless

  end # module SineCircle
end # module JcB

john

1 Like

OK, got it working, thanks to you both!
Constant was correct, I must have had a typo the first time I used it.

That is the link to the OLD documentation. Use the new YARD generated API documentation:

@DanRathbun,
Oh, that is the link that I did use!
I found the old link on this forum related to the LanguageHandler class, and thought there must be extra information hidden away from prying eyes. :spy:

Thanks for clarifying! And thanks for all the help you have given to others on this forum!

Incidentally, is the folder for English supposed to be ‘en’ on this YARD API doc, or is it supposed to read ‘en-US’?
Because if I use ‘en’, it doesn’t find the file.

I also use en-US but can’t remember why…

I think it was because Americans spell too many words wrong en-GB caused issues…

maybe it’s a typo in the docs…

john

It’s supposed to be “en-US” the same as returned by the Sketchup::get_locale() method.


Not. There is no good use of globals for data or objects only to be used by a single extension or module.

Ruby has 2 things. Objects and references that point at objects.

A module is an instance of class Module, and in your examples, you’ve created a module instance object referenced as the identifier Centaur, which is a “constant” because it begins with a capital letter.

Class and module references are CamelCase in Ruby by convention. Constants that point at other objects (data, strings, etc.) are all UPPERCASE by convention.

But in Ruby references can be reassigned at any time to point at any other object (of any class.) This is still true for constants with the Ruby versions that SketchUp still uses. Warnings are generated when doing constant reassignments because it is poor programming practice and perhaps in the future it may not be allowed at all. (This cannot be done in many other coding languages.)

In Ruby “variables” are just references that are expected to be reassigned to point at other objects later on. It could be reassigned to point at the nil object, or one of the other objects in the ordinal set of the Integer class, or a new text string, etc. Whatever.


Here is another way of doing the extension dance:

# encoding: UTF-8
require 'sketchup.rb'
require 'extensions.rb'

module Centaur
  module GltfExporter
    unless file_loaded?(__FILE__)

      # Create a library mixin module for language handling:
      module Xlate
        @@say = LanguageHandler.new('centaur_gltf_export.strings')
        def say(arg)
          @@say[arg] rescue @@say.GetString(arg)
        end
      end

      # Mixin the Xlate module into this module
      # giving it local access to the say() method:
      include Xlate

      # Instantiate the extension object, referencing as
      # @@extension, extend it with the Xlate module, and
      # set it's properties via instance evaluation:
      @@extension = SketchupExtension.new(
        'glTF Exporter',
        'centaur_gltf_export/gltfExport'
      ).extend(Xlate).instance_eval {
        description = self.say('description')
        version     = '1.0.0'
        copyright   = '©2017'
        creator     = 'Centaur'
      }

      Sketchup.register_extension(@@extension, true)

      def extension()
        @@extension
      end

      # Make the extension() method available
      # from both inside and outside this module:
      extend self

      file_loaded(__FILE__)
    end

    class GltfExport

      # Mixin the Xlate module to give
      # this class access to the say() method:
      include Xlate

      # ... the rest of the class' code ...

    end

  end
end

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.