• fr

Changement de langue à la volée

Pourquoi

Parce qu'en tant qu'utilisateur, j'apprécie:

  • lorsque l'interface d'un logiciel est dans une langue que je peux comprendre,
  • de pouvoir changer de langue sans fermer l'application,
  • de changer de langue rapidement et simplement,
  • que la liste des langues soit dans leurs langues originelles et non traduites.

Pour ce dernier point, imaginez une interface en chinois dans laquelle vous cherchez désespérément comment basculer en français. C'est tellement plus simple de chercher le mot "Français" que d'essayer de deviner à quoi ressemble ce même mot en chinois!

Parce qu'en tant que programmeur:

  • je considère qu'une application non localisable a peu de chance d'être diffusée,
  • j'apprécie d'avoir un fichier de langue externe traduisible par quiconque en éprouve le besoin,
  • pouvoir facilement basculer entre la langue de l'utilisateur et la mienne simplifie l'assistance.

Comment

Lorsque j'ai découvert Tcl/Tk, j'ai parcouru beaucoup de page web, fait des recherches et lu des forums sur ce sujet. On en revient presque toujours à l'utilisation de msgcat et à utiliser à chaque fois que c'est possible une option -textvariable plutôt que -text. Malheureusement, tous les widgets ne disposent pas de l'option -textvariable (exemples les entrées d'un menu, NoteBook des BWidgets, ...). Cela implique d'avoir des façons de faire différentes en fonction du widget utilisé.

Pour remédier à cela, voici une façon de faire. Je dis bien "une" façon de faire et non "la" façon de faire. A chacun de trouver celle qui lui convient le mieux. Celle-ci peut certainement être améliorée.

Deux types de textes sont à considérer:

  • les textes de l'interface utilisateur principale dont les widgets sont créés lors du lancement de l'application: on a besoin d'un mécanisme de changement des textes lors d'un changement de langue,
  • les textes des dialogues créés et détruits par l'application au cours de son utilisation: on met directement la chaîne traduite ( [ mc "texte de référence" ]) car ils seront recréés à chaque utilisation.

Pour tous les widgets créés au lancement de l'application, on procède par étapes:

  • construction des widgets sans définir de texte (partie "Construction de l'interface", procédures Buildxxx dans l'exemple),
  • procédure(s) de définition des textes. Elle sera appelée au lancement de l'application et à chaque changement de langue (partie "Adaptation des textes", procédures Updatexxx dans l'exemple),
  • procédures de chargement d'un nouveau fichier de langue et de changement de langue (partie "Gestion des langues" dans l'exemple).

Un exemple vaut mieux qu'un long discours:

#!/usr/bin/tclsh
#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------

# To be able to use/make starkit/starpack
package provide app-chglng 0.1

set APPNAME "ChgLng"
set VERSION [ package require app-chglng ]
set APPSTRING "$APPNAME $VERSION"
set AUTHOR "Myself"

# Adjust auto_path to contain script home and current directory
set script_home [string trimright [file dirname [info script]] ./]
set script_home [file join [pwd] $script_home]
lappend auto_path $script_home

# Specify required packages
set TCLVERSION [ package require Tcl ]
set TKVERSION [ package require Tk ]
set MCVERSION [ package require msgcat ]
namespace import ::msgcat::*
package require lngcode
namespace import ::lngcode::*

namespace eval ::lng:: {
	variable Obj
	variable Idx
	variable Hints
	variable StatusText

	set StatusText "    "
}

#*******************************************************************************
#                         Construction de l'interface                          *
#*******************************************************************************

#-------------------------------------------------------------------------------
# Construit le menu de l'application
#-------------------------------------------------------------------------------
proc ::lng::BuildMenu {} {
	set ::lng::Obj(mnu) [ menu .mb ]
	set index 0

	# Fichier
	set ::lng::Obj(mnuFile) [ menu $::lng::Obj(mnu).file -tearoff 0 ]
	$::lng::Obj(mnu) add cascade -menu $::lng::Obj(mnuFile)
	set ::lng::Idx(mnuFile) [ incr index ]
	# Edition
	set ::lng::Obj(mnuEdit) [ menu $::lng::Obj(mnu).edit -tearoff 0 ]
	$::lng::Obj(mnu) add cascade -menu $::lng::Obj(mnuEdit)
	set ::lng::Idx(mnuEdit) [ incr index ]
	# Option
	set ::lng::Obj(mnuTools) [ menu $::lng::Obj(mnu).option -tearoff 0 ]
	$::lng::Obj(mnu) add cascade -menu $::lng::Obj(mnuTools)
	set ::lng::Idx(mnuTools) [ incr index ]
	# Aide
	set ::lng::Obj(mnuHelp) [ menu $::lng::Obj(mnu).help -tearoff 0 ]
	$::lng::Obj(mnu) add cascade -menu $::lng::Obj(mnuHelp)
	set ::lng::Idx(mnuHelp) [ incr index ]

	# Menu Fichier
	set index -1
	$::lng::Obj(mnuFile) add command -command ::lng::Dummy
	set ::lng::Idx(mnuFileNew) [ incr index ]
	$::lng::Obj(mnuFile) add command -command ::lng::Dummy
	set ::lng::Idx(mnuFileOpen) [ incr index ]
	$::lng::Obj(mnuFile) add command -command ::lng::Dummy
	set ::lng::Idx(mnuFileSave) [ incr index ]
	$::lng::Obj(mnuFile) add command -command ::lng::Dummy
	set ::lng::Idx(mnuFileSaveAs) [ incr index ]
	$::lng::Obj(mnuFile) add separator
	incr index
	$::lng::Obj(mnuFile) add command -command ::lng::FileQuit
	set ::lng::Idx(mnuFileQuit) [ incr index ]

	# Menu Edition
	set index -1
	$::lng::Obj(mnuEdit) add command -command ::lng::Dummy
	set ::lng::Idx(mnuEditUndo) [ incr index ]
	$::lng::Obj(mnuEdit) add command -command ::lng::Dummy
	set ::lng::Idx(mnuEditRedo) [ incr index ]
	$::lng::Obj(mnuEdit) add separator
	incr index
	$::lng::Obj(mnuEdit) add command -command ::lng::Dummy
	set ::lng::Idx(mnuEditCut) [ incr index ]
	$::lng::Obj(mnuEdit) add command -command ::lng::Dummy
	set ::lng::Idx(mnuEditCopy) [ incr index ]
	$::lng::Obj(mnuEdit) add command -command ::lng::Dummy
	set ::lng::Idx(mnuEditPaste) [ incr index ]

	# Menu Option
	set index -1
	set ::lng::Obj(mnuLanguage) [ menu $::lng::Obj(mnuTools).language -tearoff 0 ]
	$::lng::Obj(mnuTools) add cascade -menu $::lng::Obj(mnuLanguage)
 	set ::lng::Idx(mnuToolsLanguage) [ incr index ]
	$::lng::Obj(mnuTools) add command -command ::lng::Dummy
	set ::lng::Idx(mnuToolsOptions) [ incr index ]

	# Langue
	set LngCodeList [ ::lngcode::getCodeList "$::script_home/msg" ]
	foreach code $LngCodeList {
		$::lng::Obj(mnuLanguage) add command \
			-label [ getLngName $code 0 ] \
			-command [ list ::lng::SwitchLanguage $code ]
	}

	# Menu Aide
	set index -1
	$::lng::Obj(mnuHelp) add command -command ::lng::About
	set ::lng::Idx(mnuHelpAbout) [ incr index ]
	$::lng::Obj(mnuHelp) add command -command ::lng::Versions
	set ::lng::Idx(mnuHelpVersions) [ incr index ]

	. config -menu $::lng::Obj(mnu)
}

#-------------------------------------------------------------------------------
# Construit la barre d'atat
#-------------------------------------------------------------------------------
proc ::lng::BuildStatusBar {} {
	frame .statusBar
	label .statusBar.label -textvariable ::lng::StatusText -relief sunken -bd 1 -anchor w
	pack .statusBar.label -side left -padx 2 -expand yes -fill both
	pack .statusBar -side bottom -fill x -pady 2
}

#-------------------------------------------------------------------------------
# Construit le contenu de la fenetre
#-------------------------------------------------------------------------------
proc ::lng::BuildContents {} {

	# Panneau haut
	frame .ft

	# Labels
	set ::lng::Obj(frmLabel) [ labelframe .ft.f1 ]
	set ::lng::Obj(lblLabel) [ label .ft.f1.lab -fg red -bg white ]
	pack $::lng::Obj(lblLabel) -padx 4

	set ::lng::Obj(lblMsg) [ label .ft.f1.msg -wraplength 400 -justify left  ]
	pack $::lng::Obj(lblMsg) -padx 4

 	pack .ft.f1  -side left -fill both -expand yes -padx 4 -pady 4 -ipadx 4 -ipady 4

	# Boite liste
	set select ""
	set ::lng::Obj(frmList) [ labelframe .ft.f2 ]
	set ::lng::Obj(lbList) [ listbox .ft.f2.listb ]
	set ::lng::Obj(lblList) [ label .ft.f2.lab -textvariable select ]
	pack $::lng::Obj(lbList) -fill y -expand yes
	pack $::lng::Obj(lblList)
	bind $::lng::Obj(lbList)  { set select [ selection get ] }

	pack $::lng::Obj(frmList) -side right -fill y -expand yes -padx 4 -pady 4 -ipadx 4 -ipady 4

	pack .ft

	# Panneau ajustable
	frame .f2
	panedwindow .f2.pane -orient horizontal
	pack .f2.pane -side top -expand yes -fill both

	# Boutons radio
	set ::lng::Obj(frmRadio) [ labelframe .f2.pane.f1 ]
	set ::lng::Obj(rb1) [ radiobutton .f2.pane.f1.r1 -variable myvar1 -value "1" ]
	set ::lng::Obj(rb2) [ radiobutton .f2.pane.f1.r2 -variable myvar1 -value "2" ]
	set ::lng::Obj(rb3) [ radiobutton .f2.pane.f1.r3 -variable myvar1 -value "3" ]
	$::lng::Obj(rb2) select
	grid $::lng::Obj(rb1)
	grid $::lng::Obj(rb2)
	grid $::lng::Obj(rb3)

	# Cases a cocher
	set ::lng::Obj(frmCheck) [ labelframe .f2.pane.f2 ]
	set ::lng::Obj(chk1) [ checkbutton .f2.pane.f2.chk1 -variable myvar2 ]
	set ::lng::Obj(chk2) [ checkbutton .f2.pane.f2.chk2 -variable myvar3 ]
	set ::lng::Obj(chk3) [ checkbutton .f2.pane.f2.chk3 -variable myvar4 ]
	$::lng::Obj(chk1) select
	$::lng::Obj(chk3) select
	grid $::lng::Obj(chk1)
	grid $::lng::Obj(chk2)
	grid $::lng::Obj(chk3)

	.f2.pane add .f2.pane.f1 .f2.pane.f2

	pack .f2  -side top -fill both -padx 4 -pady 4 -ipadx 4 -ipady 4

	# Boutons
	set ::lng::Obj(frmButton) [ labelframe .f3 ]
	set ::lng::Obj(btn1) [ button .f3.b1 -command ::lng::Dummy ]
	set ::lng::Obj(btn2) [ button .f3.b2 -command ::lng::Dummy ]
	set ::lng::Obj(btn3) [ button .f3.b3 -command ::lng::Dummy ]
	grid $::lng::Obj(btn1) $::lng::Obj(btn2) $::lng::Obj(btn3)

	grid columnconfigure .f3 0 -weight 1
	grid columnconfigure .f3 1 -weight 1
	grid columnconfigure .f3 2 -weight 1
	pack .f3 -side top -fill x -padx 4 -pady 4 -ipadx 4 -ipady 4
}

#-------------------------------------------------------------------------------
# Construit l'interface graphique
#-------------------------------------------------------------------------------
proc ::lng::BuildGUI {} {
	::lng::BuildMenu
	::lng::BuildContents
	::lng::BuildStatusBar
}

#*******************************************************************************
#                         Gestion des langues                                  *
#*******************************************************************************

#-------------------------------------------------------------------------------
# Charge un nouveau fichier de langue
#-------------------------------------------------------------------------------
proc ::lng::LoadLanguage {code} {
	if {$code != ""} {
		mclocale $code
	}
	# charge le fichier de langue stocke dans le repertoire msg
	mcload "$::script_home/msg"
}

#-------------------------------------------------------------------------------
# Change de langue
#-------------------------------------------------------------------------------
proc ::lng::SwitchLanguage {code} {
	::lng::LoadLanguage $code
	::lng::UpdateAll
}

#*******************************************************************************
#                         Adaptation des textes                                *
#*******************************************************************************

#-------------------------------------------------------------------------------
# Modifie le texte d'un menu
#-------------------------------------------------------------------------------
proc ::lng::SetMnuTxt {menu index name hint} {
	set underline [ string first & $name ]
	if { $underline < 0 } {
		set label $name
	} else {
		if { $underline == 0 } {
			set label [ string range $name 1 end ]
		} else {
			set label [ string range $name 0 [ expr { $underline - 1 } ] ]
			append label [ string range $name [ expr { $underline + 1 } ] end ]
		}
	}

	$menu entryconfigure $index -label $label
	if { $underline >= 0 } {
		$menu entryconfigure $index -underline $underline
	}

	# Hint
	set ::lng::Hints($menu-$index) $hint
}

#-------------------------------------------------------------------------------
# Met a jour la fenetre princupale
#-------------------------------------------------------------------------------
proc ::lng::UpdateMainWindow {} {
	wm title . [ mc "msg Change language" ]
}

#-------------------------------------------------------------------------------
# Met a jour le menu
#-------------------------------------------------------------------------------
proc ::lng::UpdateMenu {} {
	# Menu Fichier
	::lng::SetMnuTxt $::lng::Obj(mnu) $::lng::Idx(mnuFile) \
		[ mc "msg &File" ] [ mc "hint File" ]
	::lng::SetMnuTxt $::lng::Obj(mnuFile) $::lng::Idx(mnuFileNew) \
		[ mc "msg &New" ] [ mc "hint New" ]
	::lng::SetMnuTxt $::lng::Obj(mnuFile) $::lng::Idx(mnuFileOpen) \
		[ mc "msg &Open ..." ] [ mc "hint Open" ]
	::lng::SetMnuTxt $::lng::Obj(mnuFile) $::lng::Idx(mnuFileSave) \
		[ mc "msg &Save" ] [ mc "hint Save" ]
	::lng::SetMnuTxt $::lng::Obj(mnuFile) $::lng::Idx(mnuFileSaveAs) \
		[ mc "msg Save &as ..." ] [ mc "hint SaveAs" ]
	::lng::SetMnuTxt $::lng::Obj(mnuFile) $::lng::Idx(mnuFileQuit) \
		[ mc "msg &Quit" ] [ mc "hint File" ]

	# Menu Edition
	::lng::SetMnuTxt $::lng::Obj(mnu) $::lng::Idx(mnuEdit) \
		[ mc "msg &Edit" ] [ mc "hint Edit" ]
	::lng::SetMnuTxt $::lng::Obj(mnuEdit) $::lng::Idx(mnuEditUndo) \
		[ mc "msg &Undo" ] [ mc "hint Undo" ]
	::lng::SetMnuTxt $::lng::Obj(mnuEdit) $::lng::Idx(mnuEditRedo) \
		[ mc "msg &Redo" ] [ mc "hint Redo" ]
	::lng::SetMnuTxt $::lng::Obj(mnuEdit) $::lng::Idx(mnuEditCut) \
		[ mc "msg &Cut" ] [ mc "hint Cut" ]
	::lng::SetMnuTxt $::lng::Obj(mnuEdit) $::lng::Idx(mnuEditCopy) \
		[ mc "msg C&opy" ] [ mc "hint Copy" ]
	::lng::SetMnuTxt $::lng::Obj(mnuEdit) $::lng::Idx(mnuEditPaste) \
		[ mc "msg &Paste" ] [ mc "hint Paste" ]

	# Menu Option
	::lng::SetMnuTxt $::lng::Obj(mnu) $::lng::Idx(mnuTools) \
		[ mc "msg &Tools" ] [ mc "hint Tools" ]
	::lng::SetMnuTxt $::lng::Obj(mnuTools) $::lng::Idx(mnuToolsLanguage) \
		[ mc "msg &Language" ] [ mc "hint Language" ]
	::lng::SetMnuTxt $::lng::Obj(mnuTools) $::lng::Idx(mnuToolsOptions) \
		[ mc "msg &Options" ] [ mc "hint Options" ]

	# Menu Aide
	::lng::SetMnuTxt $::lng::Obj(mnu) $::lng::Idx(mnuHelp) \
		[ mc "msg &Help" ] [ mc "hint Help" ]
	::lng::SetMnuTxt $::lng::Obj(mnuHelp) $::lng::Idx(mnuHelpAbout) \
		[ mc "msg &About ..." ] [ mc "hint About" ]
	::lng::SetMnuTxt $::lng::Obj(mnuHelp) $::lng::Idx(mnuHelpVersions) \
		[ mc "msg &Versions ..." ] [ mc "hint Versions" ]
}

#-------------------------------------------------------------------------------
# Met a jour le contenu
#-------------------------------------------------------------------------------
proc ::lng::UpdateContents {} {
	$::lng::Obj(frmLabel) configure -text [ mc "msg Labels" ]
	$::lng::Obj(lblLabel) configure	-text [ mc "msg Long label title" ]
    $::lng::Obj(lblMsg) configure -text [ mc "msg Long label contents" ]

	$::lng::Obj(frmList) configure -text [ mc "msg List box" ]

	$::lng::Obj(lbList) delete 0 end
	foreach item [ mc "msg List box contents" ] {
		$::lng::Obj(lbList) insert end $item
	}

	$::lng::Obj(frmRadio) configure -text [ mc "msg Radio buttons" ]
	$::lng::Obj(rb1) configure -text [ mc "msg Radio button 1" ]
	$::lng::Obj(rb2) configure -text [ mc "msg Radio button 2" ]
	$::lng::Obj(rb3) configure -text [ mc "msg Radio button 3" ]

	$::lng::Obj(frmCheck) configure -text [ mc "msg Check boxes" ]
	$::lng::Obj(chk1) configure  -text [ mc "msg Check box 1" ]
	$::lng::Obj(chk2) configure  -text [ mc "msg Check box 2" ]
	$::lng::Obj(chk3) configure  -text [ mc "msg Check box 3" ]

	$::lng::Obj(frmButton) configure -text [ mc "msg Buttons" ]
	$::lng::Obj(btn1) configure  -text [ mc "msg Button 1" ]
	$::lng::Obj(btn2) configure  -text [ mc "msg Button 2" ]
	$::lng::Obj(btn3) configure  -text [ mc "msg Button 3" ]
}

#-------------------------------------------------------------------------------
# Met a jour toute l'application
#-------------------------------------------------------------------------------
proc ::lng::UpdateAll {} {
	::lng::UpdateMainWindow
	::lng::UpdateMenu
	::lng::UpdateContents
}

#*******************************************************************************
#                         Boites d'information                                 *
#*******************************************************************************

#-------------------------------------------------------------------------------
# Boite A propos ...
#-------------------------------------------------------------------------------
proc ::lng::About {} {
    global APPNAME
    global VERSION
    global AUTHOR

    set aboutstring [ mc "msg Program: \t%s" "$APPNAME" ]
	append aboutstring "\n"
    append aboutstring [ mc "msg Version: \t%s" "$VERSION" ]
	append aboutstring "\n\n"
    append aboutstring "(c) $AUTHOR 2006"
	append aboutstring "\n\n"
    append aboutstring "This software is distributed under the terms of NOL (No Obligation License)"
	append aboutstring "\n"
    append aboutstring "http://wfr.tcl.tk/nol\n\n"

    if { [ winfo exists .aboutWin ] } {
        focus .aboutWin.close
        return
    }

    toplevel .aboutWin -padx 5 -pady 5 -relief raised -borderwidth 3
    wm overrideredirect .aboutWin 1

    frame .aboutWin.upper
    frame .aboutWin.lower

	label .aboutWin.upper.label -text [ mc "msg About %s" "$APPNAME" ] -relief groove
    text .aboutWin.lower.about -relief groove -width 40 -height 10 \
		-padx 5 -pady 5 -wrap word -background [ .aboutWin.lower cget -background ]
    .aboutWin.lower.about insert 1.0 $aboutstring
    .aboutWin.lower.about configure -state disabled
    button .aboutWin.close -text [ mc "msg Close" ] -command { destroy .aboutWin }

    bind .aboutWin  {destroy .aboutWin}

    grid .aboutWin.upper -in .aboutWin -row 1 -column 1 -sticky news -padx 5 -pady 5
    grid .aboutWin.lower -in .aboutWin -row 2 -column 1 -sticky news -padx 5 -pady 5
    grid .aboutWin.close -in .aboutWin -row 3 -column 1 -padx 5 -pady 5

    pack .aboutWin.upper.label -in .aboutWin.upper -expand 1 -fill both
    pack .aboutWin.lower.about -in .aboutWin.lower -expand 1 -fill both

	set deltax [ expr { ( [ winfo width . ] - [ winfo reqwidth .aboutWin ] ) / 2 } ]
	set deltay [ expr { ( [ winfo height . ] - [ winfo reqheight .aboutWin ] ) / 2 } ]
	set xpos [ expr { [ winfo x . ] + $deltax - 22 } ]
    set ypos [ expr { [ winfo y . ] + $deltay } ]
    wm geometry .aboutWin "+$xpos+$ypos"

    grab .aboutWin
    focus .aboutWin.close
}

#-------------------------------------------------------------------------------
# Boite Versions ...
#-------------------------------------------------------------------------------
proc ::lng::Versions {} {
    global APPSTRING
	global TCLVERSION
	global TKVERSION
	global MCVERSION

    set versionstring "TCL:\t$TCLVERSION"
	append versionstring "\n"
    append versionstring "TK:\t$TKVERSION"
	append versionstring "\n"
    append versionstring "MSGCAT:\t$MCVERSION"
	append versionstring "\n"

    if { [ winfo exists .versionWin ] } {
        focus .versionWin.close
        return
    }

    toplevel .versionWin -padx 5 -pady 5 -relief raised -borderwidth 3
    wm overrideredirect .versionWin 1

    frame .versionWin.upper
    frame .versionWin.lower

	label .versionWin.upper.label -text "$APPSTRING" -relief groove
    text .versionWin.lower.version -relief groove -width 30 -height 5 \
		-padx 5 -pady 5 -wrap word -background [ .versionWin.lower cget -background ]
    .versionWin.lower.version insert 1.0 $versionstring
    .versionWin.lower.version configure -state disabled
    button .versionWin.close -text [ mc "msg Close" ] -command { destroy .versionWin }

	bind .versionWin  {destroy .versionWin}

    grid .versionWin.upper -in .versionWin -row 1 -column 1 -sticky news -padx 5 -pady 5
    grid .versionWin.lower -in .versionWin -row 2 -column 1 -sticky news -padx 5 -pady 5
    grid .versionWin.close -in .versionWin -row 3 -column 1 -padx 5 -pady 5

    pack .versionWin.upper.label -in .versionWin.upper -expand 1 -fill both
    pack .versionWin.lower.version -in .versionWin.lower -expand 1 -fill both

	set deltax [ expr { ( [ winfo width . ] - [ winfo reqwidth .versionWin ] ) / 2 } ]
	set deltay [ expr { ( [ winfo height . ] - [ winfo reqheight .versionWin ] ) / 2 } ]
	set xpos [ expr { [ winfo x . ] + $deltax - 22 } ]
    set ypos [ expr { [ winfo y . ] + $deltay } ]
    wm geometry .versionWin "+$xpos+$ypos"

    grab .versionWin
    focus .versionWin.close
}

#*******************************************************************************
#                           Gestion des evenements                             *
#*******************************************************************************

#-------------------------------------------------------------------------------
# Aucune action
#-------------------------------------------------------------------------------
proc ::lng::Dummy {} {
	tk_messageBox \
		-icon info \
		-type ok \
		-message "Aucune action, c'est une démo!" \
		-title "Avertissement"
}

#-------------------------------------------------------------------------------
# Quitte l'application
#-------------------------------------------------------------------------------
proc ::lng::FileQuit {} {
	exit
}


#-------------------------------------------------------------------------------
# Ajoute les binding necessaires pour les hint des menus
#-------------------------------------------------------------------------------
proc ::lng::BindHint {} {
	bind Menu <> {
		if { [ catch { %W index active } idx ] } {
			set idx "none"
		}
		if { $idx != "none" } {
			set pos [ expr { [ string last "." %W ] + 1 } ]
			set id [ string range %W $pos end ]-$idx
			set id [ string map {"#" "."} $id ]
			if { [ catch { set label $::lng::Hints($id) } label ] } {
				set label "    "
			}
			set ::lng::StatusText $label
			update idletasks
		}
	}
}

#-------------------------------------------------------------------------------
# Process principal
#-------------------------------------------------------------------------------
proc ::lng::Main {} {
	wm withdraw .

	::lng::BuildGUI
	::lng::LoadLanguage {}
	::lng::UpdateAll
	::lng::BindHint
	update idletasks

	wm deiconify .
	wm protocol . WM_DELETE_WINDOW exit
	raise .
	focus -force .
	wm minsize . [ winfo width . ] [ expr { [ winfo height . ] + 10 } ]
}

::lng::Main
	

Licence

Cette documentation ainsi que l'exemple sont mis à disposition sous licence No Obligation License, soit “Aucune obligation pour vous. Aucune obligation pour moi.”

Téléchargement

Le fichier source de l'exemple chglng.zip

Historique

Date Version Auteur Description des modifications
22 octobre 2006 1.0.0 Alain JAFFRE Première version publique.