Version 0 of Internationalization and the Tcl C APIs

Updated 2011-03-09 15:55:12 by RZ

The current state can be found at [L1 ].

The msgcat package provides internationalization on the script level. This functionality is missing on the C function side. May be something like the following idea could be incorporated in the core.

msgcat uses dictionaries to save the translation strings. Access to nested dictionaries is not (yet?) exposed. So the msgcat::ns function saves the available translation strings in an array. The function should be called after setting the locale (can be done with a trace on msgcat::Loclist) and after setting the translation strings.

proc ::msgcat::ns {ns src} {
  variable Msgs
  variable Loclist

  if {[string range $ns 0 1] ne {::}} {set ns ::$ns}
  namespace eval $ns {}
  catch {unset $ns}
  array set $ns {}
  foreach loc $Loclist {
    if {[dict exists $Msgs $loc $ns]} {
      dict for {key value} [dict get $Msgs $loc $ns] {
        if {![info exists $ns($key)]} {set $ns($key) $value}
      }
    }
  }
}

C-function to get a translated string.

const char *Tcl_Msg(Tcl_Interp *interp, Tcl_Obj *ns; Tcl_Obj *text) {
  Tcl_Obj *myVar;

  myVar = Tcl_ObjGetVar2(interp, ns, text, TCL_GLOBAL_ONLY);
  if (myVar == NULL) {
    return (Tcl_GetString(text));
  } else {
    return (Tcl_GetString(myVar));
  }
}

C-Extension (p.e. 'Ext1' in namespace 'ext1')

Tcl_Interp *myInterp; /* used interpreter */
Tcl_Obj *myNs; /* used namespace, also name of array variable to hold translation strings *
#define EXT1_MSG(text) Tcl_Msg(myInterp,myNs,Tcl_SetStringObj(myText,text,-1))
int Ext1_Init(Tcl_Interp *interp) {
  ...
  myInterp = interp;
  myNs = Tcl_GetStringObj("::ext1",-1);
}
void Ext1_command() {
  printf(EXT1_MSG("some dummy text at line %d",__LINE__);
}

Find all translation strings of extension 'Ext1' with:

proc findstrings {function cfiles} {
  set regex $function
  append regex {[ \t]*\([ \t]*("(?:\\"|[^"])[ \t]*)}
  foreach f $cfiles {
    set fd [open $f r]
    set c [read $fd]
    close $fd
    foreach {x text} [regexp -all -inline $regex $c] {puts $text}
  }
}
findstrings EXT1_MSG [glob *.c]