Localization with AutoLISP
Tuesday, January 27, 2026It may sometimes be necessary to translate your AutoLISP programs into multiple languages.
To do this, we first need a function that splits a string into a list of substrings.
;;; Splits a string into a list of substrings
;;; string : the string to split
;;; separator : the separator character
;;; ignore-empty : if true, ignores empty substrings
(defun split-string (string separator ignore-empty / result current-word index character)
(setq result '())
(setq current-word "")
(setq index 1)
(while (<= index (strlen string))
(setq character (substr string index 1))
(if (= character separator)
(progn
(if (or (not ignore-empty) (/= current-word ""))
(setq result (append result (list current-word)))
)
(setq current-word "")
)
(setq current-word (strcat current-word character))
)
(setq index (1+ index))
)
(if (or (not ignore-empty) (/= current-word ""))
(setq result (append result (list current-word)))
result
)
)
Next, we need to know which language AutoCAD is using to determine which translations to load. Surprisingly, AutoCAD does not provide a system variable for this. There is a variable named LOCALE, but it returns a code corresponding to the operating system language. To get the language used by AutoCAD, we need to use a workaround:
;;; Returns the current AutoCAD culture in uppercase
;;; FRA for French/France, ENU for English/United States, etc.
(defun get-acad-culture (/)
;; The LOCALE system variable returns the operating system language, not the AutoCAD culture.
;; The LOCALROOTPREFIX system variable contains a path that ends with the culture code.
;; Example: C:\Users\<Username>\AppData\Local\Autodesk\AutoCAD 2026\R25.1\enu\
(strcase (last (split-string (getvar "LOCALROOTPREFIX") "\\" T)))
)
The LOCALROOTPREFIX system variable contains a path that ends with the AutoCAD culture code. So we can use the first function split-string to extract the culture code.
Next, for translations, we will use a TSV (Tab-Separated Values) file. This type of file allows storing tabular data and can be easily created and edited with a spreadsheet application like Microsoft Excel.
ID ENU FRA
hello Hello Bonjour
goodbye Goodbye Au revoir
Be careful to use UTF-8 encoding for the TSV file, otherwise special characters (e.g. accented letters) will not display correctly.
The first column is a unique identifier for each translation. The following columns contain the translations in different languages.
The file can be loaded with the following function:
;;; Returns the index (0-based) of an element in a list, or nil if not found
;;; element : the element to search for
;;; items : the list to search in
(defun find-index (element items / index found)
(setq index 0)
(setq found nil)
(while (and (not found) (nth index items))
(if (= (nth index items) element)
(setq found T)
(setq index (1+ index))
)
)
(if found index nil)
)
;;; Loads translations from a TSV file for the current culture
;;; The file must have a header with culture codes (ENU, FRA, etc.)
;;; The first column contains the identifiers
;;; filename : path to the TSV file
;;; Result: defines the global variable $strings (association list of dotted pairs)
(defun load-strings (filename / file header-line headers culture-index line columns)
(setq file (open filename "r"))
(if (not file)
(progn
(princ (strcat "\nError: unable to open file " filename))
nil
)
(progn
;; Read the header and find the column for the current culture
(setq header-line (read-line file))
(setq headers (split-string header-line "\t" T))
(setq culture-index (find-index (get-acad-culture) headers))
;; If the current culture is not found, default to ENU
(if (not culture-index)
(setq culture-index (find-index "ENU" headers))
)
(if (not culture-index)
(progn
(close file)
(princ "\nError: no translation column found.")
nil
)
(progn
;; Read each line and build the association list
(setq $strings '())
(while (setq line (read-line file))
(setq columns (split-string line "\t" nil))
(if (and (car columns) (nth culture-index columns))
(setq $strings (cons (cons (car columns) (nth culture-index columns)) $strings))
)
)
(setq $strings (reverse $strings))
(close file)
$strings
)
)
)
)
)
The translations are stored in the global variable $strings (note the $ which indicates a global variable). It will contain an association list of dotted pairs similar to ('("hello" "Hello") ("goodbye" "Goodbye")). This is the equivalent of a dictionary in Python or .NET.
Once the translations are loaded, you can use assoc to retrieve a translation:
;;; Returns the translation associated with an identifier
;;; identifier : the identifier of the string to translate
(defun get-string (identifier)
(cdr (assoc identifier $strings))
)
You can now use this small framework like this:
(load-strings "translations.tsv")
(princ (get-string "hello"))
Need an AutoCAD (AutoLISP, ObjectARX, .NET, VBA) development? Contact me for a free quote.