Author Topic: Best way to localize messages in lisp-code?  (Read 5134 times)

0 Members and 1 Guest are viewing this topic.

Peter2

  • Swamp Rat
  • Posts: 650
Best way to localize messages in lisp-code?
« on: January 26, 2015, 10:04:52 AM »
Any great ideas / workflows / solutions / features to localize messages in lisp, like princ, alert, get.... to different languages?
What should be done, what errors should be avoided?

Thanks
Peter

AutoCAD Map 3D 2023 German (so some technical terms will be badly retranslated to English)
BricsCAD V23

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Best way to localize messages in lisp-code?
« Reply #1 on: January 27, 2015, 02:14:43 AM »
An only one I did a while back - though it's still a bit incomplete: http://sourceforge.net/p/caddons/code/HEAD/tree/Lang/Language.LSP

Not to mention, the major work is not about the code, but the translations.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: Best way to localize messages in lisp-code?
« Reply #2 on: January 27, 2015, 05:08:37 AM »
Nice work Irneb :-)

I think there may be a typo here:

;;; -------------------------------------------------------------------------------------
;;; Function to translate to a message
;;; -------------------------------------------------------------------------------------
;;; Arguments:
;;; Key = the unique number of the message
;;; -------------------------------------------------------------------------------------
;;; Result = the message string or nil if not found
;;; -------------------------------------------------------------------------------------
(defun Caddons:Lang:Del (Key / Msg)

kruuger

  • Swamp Rat
  • Posts: 633

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Best way to localize messages in lisp-code?
« Reply #4 on: January 27, 2015, 08:34:21 AM »
I think there may be a typo here:
Thanks! Will have to get back to that project again, it's been nearly 3 years since my last update. Just have so little time and so many other stuff to do.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12912
  • London, England
Re: Best way to localize messages in lisp-code?
« Reply #5 on: January 27, 2015, 10:52:26 AM »
I think there may be a typo here:
Thanks! Will have to get back to that project again, it's been nearly 3 years since my last update. Just have so little time and so many other stuff to do.

I know the feeling!  :-)

JohnK

  • Administrator
  • Seagull
  • Posts: 10625
Re: Best way to localize messages in lisp-code?
« Reply #6 on: January 27, 2015, 11:08:39 AM »
http://www.theswamp.org/index.php?topic=43368.msg486189#msg486189
K.

Kruuger, I didn't want to take away from Keith's thread so I will ask here.

Why don't you use LiFP for processing all of that? You can set up a nice set of definitions (;@define) or if you want to keep it simple you can just use preprocessor directives.
Code - Auto/Visual Lisp: [Select]
  1. (defun myfoo ( / )
  2.   ;#ifdef EN
  3.   (princ sayHelloInEnglish)
  4.   ;#endif
  5.   ;#ifndef FR
  6.   (princ sayHelloInFrench)
  7.   ;#endif
  8.   (princ)
  9.  )

...In either case you can output several differnt versions [-i.e. using diferent languages for example] of your application with one shot in a fraction of a second.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

kruuger

  • Swamp Rat
  • Posts: 633
Re: Best way to localize messages in lisp-code?
« Reply #7 on: January 27, 2015, 04:01:29 PM »
http://www.theswamp.org/index.php?topic=43368.msg486189#msg486189
K.

Kruuger, I didn't want to take away from Keith's thread so I will ask here.

Why don't you use LiFP for processing all of that? You can set up a nice set of definitions (;@define) or if you want to keep it simple you can just use preprocessor directives.
Code - Auto/Visual Lisp: [Select]
  1. (defun myfoo ( / )
  2.   ;#ifdef EN
  3.   (princ sayHelloInEnglish)
  4.   ;#endif
  5.   ;#ifndef FR
  6.   (princ sayHelloInFrench)
  7.   ;#endif
  8.   (princ)
  9.  )

...In either case you can output several differnt versions [-i.e. using diferent languages for example] of your application with one shot in a fraction of a second.
hi Se7en
yes this should works. but questions is... is Peter2 want create multilingual program or just simple translate all calls in additional file. with lng files we give to user a possibility to create his own translation.
k.

Peter2

  • Swamp Rat
  • Posts: 650
Re: Best way to localize messages in lisp-code?
« Reply #8 on: January 28, 2015, 02:36:26 AM »
...is Peter2 want create multilingual program or just simple translate all calls in additional file. with lng files we give to user a possibility to create his own translation....
Right. If you have some programs in current development in some languages, then LNG-files are more flexible, user-friendly, can translated without entering the code, expanded to more languages, can be reused for the same strings, ....
Peter

AutoCAD Map 3D 2023 German (so some technical terms will be badly retranslated to English)
BricsCAD V23

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Best way to localize messages in lisp-code?
« Reply #9 on: January 28, 2015, 06:27:52 AM »
Here's a similar principle to my Cadons Language pack: Save to some file all the messages (at least in English). Then use the get function to get the translated one (using the getvar "Locale") setting. If it doesn't have that (yet) it reads a translation from GoogleTranslate.
Code - Auto/Visual Lisp: [Select]
  1. (setq *Traslate-Data-File*
  2.        (strcat (getvar "RoamableRootPrefix") "Translate.Data")
  3.       *Traslate-Data*
  4.        '(("EN" ("CmdFin" . "Command completed properly"))))
  5.  
  6. (defun Trans-Load  (FileName / f data)
  7.   (or FileName (setq FileName *Traslate-Data-File*))
  8.   (and (setq f (open FileName "r"))
  9.        (progn (and (setq data (read (read-line f))) (setq *Traslate-Data* data)) (close f))))
  10.  
  11. (defun Trans-Save  (FileName / f data)
  12.   (or FileName (setq FileName *Traslate-Data-File*))
  13.   (and (setq f (open FileName "w")) (progn (prin1 *Traslate-Data* f) (close f))))
  14.  
  15. (Trans-Load nil)
  16.  
  17. (defun Trans-Get-Loc-Message  (Locale Code / loc msg trn)
  18.   (if (and (setq loc (assoc (setq Locale (strcase (substr Locale 1 2))) *Traslate-Data*))
  19.            (setq msg (assoc Code (cdr loc))))
  20.     (cdr msg)
  21.     (if (and (setq loc (assoc "EN" *Traslate-Data*))
  22.              (setq msg (assoc Code (cdr loc)))
  23.              (setq trn (Trans-GoogleTrans (cdr msg) "EN" Locale)))
  24.       (progn (Trans-Add-Loc-Message Locale Code trn) (Trans-Get-Loc-Message Locale Code)))))
  25.  
  26. (defun int2hex2  (int / result mod)
  27.   (while (> int 0)
  28.     (setq result (cons (cond ((< (setq mod (rem int 16)) 10) (+ mod 48))
  29.                              (t (+ mod 55)))
  30.                        result)
  31.           int    (/ int 16)))
  32.   (while (< (length result) 2) (setq result (cons 48 result)))
  33.   (vl-list->string result))
  34.  
  35. (defun str->url  (source)
  36.          (mapcar (function
  37.                    (lambda (elt)
  38.                      (cond ((or (<= 65 elt 90) (<= 97 elt 122) (member elt '(45 46 95 126))) (chr elt))
  39.                            (t (strcat "%" (int2hex2 elt))))))
  40.                  (vl-string->list source))))
  41.  
  42. (defun Trans-GoogleTrans  (Text from to / URL html p)
  43.   (if (and (setq URL  (strcat "https://translate.google.com/?langpair="
  44.                               (substr from 1 2)
  45.                               "%7C"
  46.                               (substr to 1 2)
  47.                               "&text="
  48.                               (str->url Text))
  49.                  html (Get-Http URL))
  50.            (setq p (vl-string-search "TRANSLATED_TEXT=" html))
  51.            (setq p (+ p 17))
  52.            (eq "'" (substr html p 1)))
  53.     (progn (substr html (1+ p) (- (vl-string-position 39 html (1+ p)) p)))))
  54.  
  55.  
  56. (defun Get-Http  (URL / util fn f txt)
  57.   (if (and (vla-IsURL util URL)
  58.            (not (vl-catch-all-error-p
  59.                   (vl-catch-all-apply 'vla-GetRemoteFile (list util URL 'fn :vlax-true))))
  60.            (setq f (open fn "r")))
  61.     (progn (while (setq fn (read-line f)) (setq txt (cons fn txt)))
  62.            (close f)
  63.            (apply 'strcat (reverse txt)))))
  64.  
  65. (defun Trans-Add-Loc-Message  (Locale Code Message / loc locNew msg)
  66.   (if (setq loc (assoc (setq Locale (strcase (substr Locale 1 2))) *Traslate-Data*))
  67.     (progn (if (setq msg (assoc Code (cdr loc)))
  68.              (setq locNew (subst (cons Code Message) msg loc))
  69.              (setq locNew (cons Locale (cons (cons Code Message) (cdr loc)))))
  70.            (setq *Traslate-Data* (subst locNew loc *Traslate-Data*)))
  71.     (setq *Traslate-Data* (cons (list Locale (cons Code Message)) *Traslate-Data*))))

Here's a test for English to Spanish & French (also showing how to set a new message):
Code: [Select]
_$ (Trans-Get-Loc-Message "ENU" "CmdFin")
"Command completed properly"
_$ (Trans-Get-Loc-Message "ESP" "CmdFin")
"Comando se ha completado correctamente"
_$ (Trans-Add-Loc-Message "ENU" "Test" "This is just a testing message")
(("ES" ("CmdFin" . "Comando se ha completado correctamente")) ("EN" ("Test" . "This is just a testing message") ("CmdFin" . "Command completed properly")))
_$ (Trans-Get-Loc-Message "ENU" "Test")
"This is just a testing message"
_$ (Trans-Get-Loc-Message "ESP" "Test")
"Esto es sólo un mensaje de prueba"
_$ (Trans-Get-Loc-Message "FRA" "Test")
"Ce est juste un message de test"

Each time it uses GoogleTranslate to generate the translated message it saves it back into the global language store variable. So the next time the message is requested will just be an assoc call instead.

Then you could just save it back to the file by calling Trans-Save, either a file of your own, or nil to save it under ACad's settings folder in a file named Translate.Data.

Of course google's translations aren't always perfect. For those you can then manually change them using the Trans-Add-Loc-Message (it would overwrite an existing version if already set).
« Last Edit: January 28, 2015, 07:39:15 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: Best way to localize messages in lisp-code?
« Reply #10 on: January 28, 2015, 04:16:41 PM »
might be cool to write a lisp that can read poedit files
http://en.wikipedia.org/wiki/Poedit

Peter2

  • Swamp Rat
  • Posts: 650
Re: Best way to localize messages in lisp-code?
« Reply #11 on: February 02, 2015, 11:42:03 AM »
Hi

thanks to all for their contributions. There are some approaches, some tools which I have to check.

My basic quick-idea was: use an ini-styled language file and read the messages from it. Simple example, pseudo code:
Code - Auto/Visual Lisp: [Select]
  1. myprog.lng
  2. [EN]
  3. 1=Good day
  4. 2=Good bye
  5. 3=...
  6. [Fr]
  7. 1=Bonjour
  8. 2=Au revoir
  9. 3=...
  10. [DE]
  11. 1=Guten Tag
  12. 2=Servus
  13. 3=...
  14.  

Code - Auto/Visual Lisp: [Select]
  1. (defun lngreader (msgnumber)
  2.     (read_from_ini Section (lngtype) key (msgnumber))
  3. )
  4.  
  5. (defun c:mymainprogramm ()
  6.     (read lngtype from somewhere) ; e.g. from baisc settings
  7.     (alert (lngreader "1"))
  8.     (princ (lngreader "2"))
  9. )
But until now there are no solutions like this, so maybe this idea will be buried soon.

Thanks
Peter

AutoCAD Map 3D 2023 German (so some technical terms will be badly retranslated to English)
BricsCAD V23

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Best way to localize messages in lisp-code?
« Reply #12 on: February 03, 2015, 01:01:19 AM »
My basic quick-idea was: use an ini-styled language file and read the messages from it.

Pretty much all of them do something similar. Even that POedit files are basically the same thing, it's just that POedit tends to have tools to automatically change source code to use these translations instead - much like my Caddons language extensions do. The alternative to this is a method where you simply wrap the text in source by a call to the lang utility - thus it searches for the ENG version and translates if necessary (no need for special codes like 1=Good day, just Good day and translate to French / Spanish / etc.; but if not through Lisp I'd say searching for the entire string might have been a lot less efficient).

In Lisp however I'd advise sticking to s-expressions rather than INI files, i.e. store your data into association lists with sub-lists and save these directly to file using prin1 (or similar). They're more efficient to make use of, easier (a simple prin1/read does the job instead of having to write an entire INI parser), much more capable (basically the same capabilities as XML or JSon - e.g. very easy to also have multiple lines and/or special characters which would break INI files), and very easy to search - just use assoc.
« Last Edit: February 03, 2015, 01:10:34 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Peter2

  • Swamp Rat
  • Posts: 650
Re: Best way to localize messages in lisp-code?
« Reply #13 on: February 03, 2015, 02:19:39 AM »
...no need for special codes like 1=Good day, just Good day and translate to French / Spanish / etc.......
Yes, but the solution with the numbers would reduce the typing of often repeated srings in the code.

...In Lisp however I'd advise sticking to s-expressions rather than INI files...
Also yes, but INIs are easier to use for translators which have never seen a code-styled file, and they are easy to check.

But I will think about all the advices later.
Peter

AutoCAD Map 3D 2023 German (so some technical terms will be badly retranslated to English)
BricsCAD V23

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Best way to localize messages in lisp-code?
« Reply #14 on: February 03, 2015, 04:17:05 AM »
Yes, but the solution with the numbers would reduce the typing of often repeated srings in the code.
And more prone to complications due to coordination - it's easy when there's only a handful of lines in the file. But when there's 100s it's much easier to just type the English anyway, even if you've duplicated, than to try and figure out what number code you should use.

Also yes, but INIs are easier to use for translators which have never seen a code-styled file, and they are easy to check.
I'm not too sure if an INI is "easier" to understand than an s-expr file. It would be possible to format an s-expression so it's even more readable to humans than an INI file. E.g. here's the s-expression of your sample INI:
Code: [Select]
(("EN" (1 "Good day") (2 "Good bye") (3 "...")) ("FR" (1 "Bonjour") (2 "Au revoir") (3 "...")) ("DE" (1 "Guten Tag") (2 "Servus") (3 "...")))
And then using a simple indenting formatter (same as any XML editor) you get the following:
Code: [Select]
(
("EN"
(1 "Good day")
(2 "Good bye")
(3 "...")
)
("FR"
(1 "Bonjour")
(2 "Au revoir")
(3 "...")
)
("DE"
(1 "Guten Tag")
(2 "Servus")
(3 "...")
)
)

Anyway, I'd avoid having someone who doesn't understand "coding" files to directly edit any such file (including INI). That's just a recipe for bugs. If you have such "translators", then you'd be better off giving them a program which hides the underlaying file structure so they cannot screw it up - i.e. you need something like POedit.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.