I have no idea what “Sine Circle” is, nor how it works, nor who coded it, nor what you are “tracing” …
If this is the menu building code, then that type of code is usually only run once anyway.
Are you disputing something I said above, John ?
I have no idea what “Sine Circle” is, nor how it works, nor who coded it, nor what you are “tracing” …
If this is the menu building code, then that type of code is usually only run once anyway.
Are you disputing something I said above, John ?
I’ve recently started to use Crowdin - uploading a JSON with the original language strings. Then set up desired languages I want and invite translators to join.
They are faced with an interface the shows the translation progress as well as featuring contextual screenshots and descriptions.
Wow, a tonne of information.
Now I’m simply confused. What is the suggested way of providing language support? Write my own system or use the SketchUp builtin system.
I realize a lot of my menus are hard coded for units etc… yes I have some work ahead of me. When I started creating this plugin about two years ago I was completely new to the SketchUp API, ruby and plugins in general, who would have thought I would need language support. This is simply a growing pain that must be dealt with.
I’d say that one factor is whether you want to support languages other than what SketchUp ship with. For my own extensions I used my custom language handler mainly because I wanted to provide more languages than SketchUp itself.
I also wanted some additional flexibility in terms of interpolating strings.
Yea, localisation is a challenge. You must adjust your UI such a way it fit everything. And if you start diving into pluralism in your string that opens up a whole new can of worms.
LangHandler
with all it’s quirks still gets my vote…
regardless of the ‘format’ used it’s the refactoring of the code base that will take the greatest thought…
I rarely write code that includes the translation hook, but I try to be consistent and use 'title = ’ , 'tooltip = ', 'msg = ', etc… so that it’s easy to run a script to find and wrap all the strings…
john
Regardless of which you do or use, it will be very educational for you to READ the "langhandler.rb"
file in the "Tools"
subfolder.
When you do, you’ll see that it is simply a “wrapper class” that wraps a Hash
instance.
You’ll also see how it doubles up on every GUI string (memory-wise.) So if you do decide to use LanguageHandler
, please load up you translated string objects (into arrays or hashes) when the plugin starts, or once when an interface is built, and then destroy the LH object (set it to nil
.) Keep in mind you don’t thereafter need to reference a string with a big long string key. And of course arrays of Defaults and Prompts usually don’t need to change and don’t need to be referenced using English keys once they are built.
I abandon it years ago because of it’s errors (some of which have since been fixed) and quirks. Also since it is just a wrapper around a Hash, with a interpretive string parser, I prefer just defining hashes in Ruby which are parsed with Ruby’s faster compiled parser. (Ruby code hashes also have several different declaration formats which I can choose to use depending on the type of data.)
As I showed above, you do not need to create any “new” system, just to use Ruby core hashes.
Yes, and both ways are used the same in code using a []
access method. LanguageHandler
just wraps it’s internal hash object’s []
method in it’s own []
method.
Thought I might give the language handler one more go, still can’t get it to work, I don’t know why, I’m at wits end.
# First we pull in the standard API hooks.
# require 'sketchup.rb'
# require 'extensions.rb'
require 'langhandler.rb'
# Define Module Hierarchy
module Medeek_Engineering_Inc_Extensions
module MedeekTrussPlugin
# DanLH = LanguageHandler.new('medeek_truss.strings')
##############################
#
# Class Methods of Plugin
#
##############################
class MedeekMethods
DanLH = LanguageHandler.new('medeek_truss.strings')
class << self
# include Math
# DanLH = LanguageHandler.new('medeek_truss.strings')
######################################################
#
# Common Trusses
#
######################################################
def get_truss_geometry_common
##################################
#
# Truss Geometry - Common Trusses
#
##################################
prompts1 = [DanLH['Truss Type: '], DanLH['Out-to-out Span (ft.): '], "Top chord Pitch (x/12): ", "Overhang Left (in.): ", "Overhang Right (in.): ", "TC Size (in.): ", "BC Size (in.): ", "Web Size (in.): ", "Ply Thickness (in.): ", "Raised Heel: ", "Heel Height (in.): "]
defaults1 = ["#{@Trusstype}", "#{@TrussSpan_ft.round(5)}", "#{@Pitch}", "#{@Overhangl}", "#{@Overhangr}", "#{@Tcd}", "#{@Bcd}", "#{@Webd}", "#{@Ply}", "#{@Raisedheel}", "#{@Usrhh}"]
list1 = ["King Post|Queen Post|Fink|Howe|Fan|Mod Queen|Double Fink|Double Howe|Mod Fan|Triple Fink|Triple Howe|Quad Fink", "", "2.5|3.0|3.5|4.0|4.5|5.0|5.5|6.0|6.5|7.0|7.5|8.0|8.5|9.0|9.5|10.0|10.5|11.0|11.5|12.0|12.5|13.0|13.5|14.0|14.5|15.0|15.5|16.0", "", "", "3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0", "3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0", "3.5|5.5|7.25|9.25|11.25", "", "NO|YES", ""]
Any ideas would be helpful. Does the whitespace in the expressions pose any problem?
Language handler relies on a very particular folder structure. Have you double checked this?
Do you have a complete example?
@tt_su
I’ve added to code and appropriate folders into my latest release (2.0.7) but if you want to look at the unencrypted source code just email or PM me and I can send you a link.
Which expressions ?
The keys used in the []
method calls must be identical to the beginning string on the left of the equal sign in the .strings
file(s).
For DanLH['Truss Type: ']
the lefthand “lookup” string must be exactly "Truss Type: "
which becomes the hash key in your LanguageHandler’s wrapped hash.
In the .strings
file(s):
These quirks caused more issues in older SketchUp versions.
Some have since been accounted for (fixed.)
Regarding your posted code example.
Outdenting totally sucks.
It causes the breaking of indent guidelines which helps the author match block closures.
Ruby uses 2 space indents not 4 space.
Use 2 space and you’ll not be so tempted to outdent blocks so much.
Readability and maintenance is the key
Instead of this:
prompts1 = [DanLH['Truss Type: '], DanLH['Out-to-out Span (ft.): '], "Top chord Pitch (x/12): ", "Overhang Left (in.): ", "Overhang Right (in.): ", "TC Size (in.): ", "BC Size (in.): ", "Web Size (in.): ", "Ply Thickness (in.): ", "Raised Heel: ", "Heel Height (in.): "]
list1 = ["King Post|Queen Post|Fink|Howe|Fan|Mod Queen|Double Fink|Double Howe|Mod Fan|Triple Fink|Triple Howe|Quad Fink", "", "2.5|3.0|3.5|4.0|4.5|5.0|5.5|6.0|6.5|7.0|7.5|8.0|8.5|9.0|9.5|10.0|10.5|11.0|11.5|12.0|12.5|13.0|13.5|14.0|14.5|15.0|15.5|16.0", "", "", "3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0", "3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0", "3.5|5.5|7.25|9.25|11.25", "", "NO|YES", ""]
… etc., … try:
prompts1 = [
DanLH['Truss Type: '],
DanLH['Out-to-out Span (ft.): '],
"Top chord Pitch (x/12): ",
"Overhang Left (in.): ",
"Overhang Right (in.): ",
"TC Size (in.): ",
"BC Size (in.): ",
"Web Size (in.): ",
"Ply Thickness (in.): ",
"Raised Heel: ",
"Heel Height (in.): "
]
Truss_Types = [
"King Post",
"Queen Post",
"Fink",
"Howe",
"Fan",
"Mod Queen",
"Double Fink",
"Double Howe",
"Mod Fan",
"Triple Fink",
"Triple Howe",
"Quad Fink"
]
# PITCH
pitch_min = 2.5
pitch_max = 16.0
pitch_step = 0.5
pitch = []
pitch_min.step(pitch_max,pitch_step) {|i| pitch << i }
list1 = [
Truss_Types.join('|'),
"",
pitch.join('|'),
"",
"",
"3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0",
"3.5|5.5|7.25|9.25|9.5|11.25|11.875|14.0|16.0",
"3.5|5.5|7.25|9.25|11.25",
"",
"NO|YES", # Raised Heel
"" # Heel Height
]
Ie, more maintainable and allows for comments after each array element.
Notice the calculation of the pitch choices. You can see this way that a conditional could be used to modify the chocies based upon model units, or a unit option from some plugin dialog.
You do have some good points on code layout. I’m going to have to update my various prompt arrays, I agree they are a nightmare to edit once they get really long.
I’ve decided to create my own system. Basically just a hash lookup:
@Trs = {
"Truss Type: " => "Type de Treillis: ",
"Out-to-out Span (ft.): " => "Sortir Dehors Envergure (ft.): ",
"Top chord Pitch (x/12): " => "Haut Corde Hauteur (x/12): ",
"Overhang Left (in.): " => "Surplombe Gauche (in.): ",
"Overhang Right (in.): " => "Surplombe Droite (in.): ",
"TC Size (in.): " => "Haut Corde Taille (in.): ",
"BC Size (in.): " => "Bas Corde Taille (in.): "
}
I’m using this map routine to lookup each prompt however I would like it not to replace the value if it does not find a match:
prompts1 = ["Truss Type: ", "Out-to-out Span (ft.): ", "Top chord Pitch (x/12): ", "Overhang Left (in.): ", "Overhang Right (in.): ", "TC Size (in.): ", "BC Size (in.): ", "Web Size (in.): ", "Ply Thickness (in.): ", "Raised Heel: ", "Heel Height (in.): "]
prompts1tr = prompts1.map { |prompt| @Trs[prompt] }
@Inputs1 = UI.inputbox(prompts1tr, defaults1, list1, "Medeek Truss Plugin - Geometry")
This seems to work:
prompts1tr = prompts1.map { |prompt| (@Trs[prompt] ? @Trs[prompt] : prompt) }
Ruby Core Docs: Hash constructor ::new
new
→ new_hash
new(obj)
→ new_hash
new {|hash, key| block }
→ new_hashReturns a new, empty hash. If this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash.
In the first form, (with no args) the access returns nil.
If
obj
is specified (second form), this single object will be used for all default values.If a block is specified (third form), it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.
… SO to create a hash object that returns the key argument in the []
instance method if there is no corresponding key in the hash, then:
@Trs = Hash::new {|hash,key| key }
(This is basically what the LanguageHandler
class does with it’s internally wrapped hash object.)
Then add a bunch of key, value pairs from a new literal hash:
@Trs.update Hash[
"Truss Type: ", "Type de Treillis: ",
"Out-to-out Span (ft.): ", "Sortir Dehors Envergure (ft.): ",
"Top chord Pitch (x/12): ", "Haut Corde Hauteur (x/12): ",
"Overhang Left (in.): ", "Surplombe Gauche (in.): ",
"Overhang Right (in.): ", "Surplombe Droite (in.): ",
"TC Size (in.): ", "Haut Corde Taille (in.): ",
"BC Size (in.): ", "Bas Corde Taille (in.): "
]
Notice how when you use the []
alternate Hash class constructor, that you do not need the =>
characters.
(This []
method I’m speaking of here is unique and different from the []
instance method. !)
So, basically you first create an empty Hash object with a default behavior.
Then update it with a literal hash using the [] class method.
(FYI, the update method is an alias for the merge! method. But I’d think coders would forget to use the bang at the end of the method name, so I usually recommend update. Using merge without the bang would create a new [3rd] hash and [I think] abandon the default value we so carefully setup.)
Lastly your tabs are causing excessive indentation on the forum. Most of us set our code editors to replace a tab with 2 spaces for Ruby. Notepad++ allows a different number of spaces to replace TAB for each coding language.
In your literal hash above shouldn’t there be an equal sign between the key, value pairs? The comma doesn’t seem right.
Your mastery of Ruby and the API is mind numbing, now I know why you are call the “Learned One”. I know just enough to get me in trouble.
As I explained …
… and it is not actually an interpretive literal expression that uses the {...}
, … it’s a constructor call that takes a list of objects that are parsed as key / value pairs.
Please refer to the Ruby core documentation as linked.
You’ll see it can also take an array of key / value pair arrays, or any object compatible with a hash.
So you could also do:
@Trs = Hash::new {|hash,key| key }.update( Hash[{
"Truss Type: " => "Type de Treillis: ",
"Out-to-out Span (ft.): " => "Sortir Dehors Envergure (ft.): ",
"Top chord Pitch (x/12): " => "Haut Corde Hauteur (x/12): ",
"Overhang Left (in.): " => "Surplombe Gauche (in.): ",
"Overhang Right (in.): " => "Surplombe Droite (in.): ",
"TC Size (in.): " => "Haut Corde Taille (in.): ",
"BC Size (in.): " => "Bas Corde Taille (in.): "
}])
… but that has to create 3 hashes and is confusing.
Better to just simply do this (if you must use the =>
syntax):
@Trs = Hash::new {|hash,key| key }.update({
"Truss Type: " => "Type de Treillis: ",
"Out-to-out Span (ft.): " => "Sortir Dehors Envergure (ft.): ",
"Top chord Pitch (x/12): " => "Haut Corde Hauteur (x/12): ",
"Overhang Left (in.): " => "Surplombe Gauche (in.): ",
"Overhang Right (in.): " => "Surplombe Droite (in.): ",
"TC Size (in.): " => "Haut Corde Taille (in.): ",
"BC Size (in.): " => "Bas Corde Taille (in.): "
})
Does Notepad++ need some add-on to read Russian letters?
Thank-you Dan, your various Ruby and API hints have done the trick, I think I am well on my way to providing language support now, just a lot of cleaning up to do…
My first menu in Japanese:
Fortunately, I know a little Japanese so I think I’ve got these more or less correct. I will probably need to have a native Japanese speaker who is familiar with the construction industry check these over for me.
If you’re using Notepad++, you need to set the file encoding to UTF-8 without BOM (bit order mark.)
This is 2nd choice on the “Encoding” menu that reads just “UTF-8”
But it is easier to set the default encoding for all Ruby files to UTF-8.
“Settings” menu > “Preferences…” dialog > “New Document” panel
Change the “Default Language” to Ruby
You can choose either Windows or Unix line endings.*
Then set the encoding to UTF-8 and check the box below it to convert any opened ANSI encoded Ruby files.
Then close the dialog when your done with it.
* Note if you think you’ll be copy and pasting code from the editor into the forum, then you’ll need to use Windows line endings.
You should read the class documentation for Ruby’s Encoding
class.
And the section on Script encoding, ie, the “magic comment” that should be at the top of all code files:
# encoding: UTF-8