El sistema que utilizamos para el multi-idioma en Access es el siguiente:
- Primero se realiza la aplicación Access en castellano (por ejemplo)
- Le pasamos una rutina que documenta en una tabla Access los siguientes elementos:
- Etiquetas de Formularios
- Etiquetas de Informes
- Caption’s de Botones
- Caption’s de Páginas en el control ficha
- Los nombres de las etiquetas quedan asociados al idioma ‘es’
- Ahorra mucho tiempo en documentar, es automático
- Si queremos que la aplicación se presente también en Inglés copiamos la tabla de etiquetas del idioma ‘es’ al ‘en’ y ponemos la descripción de la etiqueta en inglés
- Definimos los usuarios que tienen acceso a la aplicación y con que idioma quieren trabajar
- Cada vez que se carga un formulario en el evento Form_load añadimos una línea que llama a una función donde se asigna a cada Caption de Etiquetas, botones, etc. su descripción en el idioma del usuario, cuando se presenta el formulario o informe éste ya esta traducido y no se nota ningún tiempo de espera significativo.
Ventaja
Si una etiqueta como ‘Codigo Cliente’ se repite 50 veces en una aplicación de gestión sólo es necesario traducirla una vez. Traducir una aplicación completa lleva 20 minutos.
Consultar más Ejemplos de VBA Access.
A continuación he puesto algunos ejemplos de las principales clases y módulos de VBA que se usan en el sistema multi-idioma que programé para ser utilizado en Microsoft Access. La utilidad multi-idioma completa está en este archivo ZIP: INTEX Translator
Idioma.cls
Clase Idioma que genera el objeto del mismo nombre para encapsular todas las funciones y propiedades relacionadas con un idioma-
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Idioma" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = True Option Explicit Dim rstIdiomas As Recordset Dim rstTextos As Recordset Dim rstTextos1 As Recordset Dim rstDiccionario As Recordset Dim strSQL As String Dim lngPalabrasNuevas As Long Public Codigo As String Public Descripcion As String Public Existe As Boolean Public Event PalabraTraducida() Public Event PalabrasTraducidas() Public Event PalabraGenerada() Public Event PalabrasGeneradas(NumeroPalabrasNuevas As Long) Public Property Get NumeroRegistros(Optional CodigoIdioma As String = "es") As Long ' El idioma predeterminado es "es" por la barra de progreso para saber las palabras en castellano strSQL = "SELECT * FROM Textos WHERE " & _ "Aplicacion='" & strAplicacion & "' AND Idioma='" & CodigoIdioma & "'" Set rstTextos = New ADODB.Recordset rstTextos.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText rstTextos.MoveLast NumeroRegistros = rstTextos.RecordCount rstTextos.Close End Property Public Sub Inicializar(CodigoIdioma As String) strSQL = "SELECT * FROM Idiomas WHERE Idioma='" & CodigoIdioma & "'" Set rstIdiomas = New ADODB.Recordset rstIdiomas.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText Existe = Not rstIdiomas.EOF If Existe Then Codigo = rstIdiomas!Idioma Descripcion = rstIdiomas!Descripcion End If rstIdiomas.Close End Sub Public Sub TraducirPalabra(TextoCastellano As String, TextoTraducido As String) Set rstTextos = New ADODB.Recordset rstTextos.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText Do While Not rstTextos.EOF rstTextos!Texto = Replace(rstTextos!Texto, TextoCastellano, TextoTraducido) Debug.Print rstTextos!Texto rstTextos.Update RaiseEvent PalabraTraducida rstTextos.MoveNext Loop rstTextos.Close RaiseEvent PalabrasTraducidas End Sub Public Sub Generar() lngPalabrasNuevas = 0 strSQL = "SELECT * FROM Textos WHERE " & _ "Aplicacion='" & strAplicacion & "' AND Idioma='es'" Set rstTextos = New ADODB.Recordset Set rstTextos1 = New ADODB.Recordset Set rstDiccionario = New ADODB.Recordset rstTextos.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText Do While Not rstTextos.EOF On Error Resume Next ' Por los Duplicados strSQL = "SELECT * FROM Textos WHERE " & _ "Aplicacion='" & rstTextos!Aplicacion & "' AND TextoES='" & rstTextos!TextoES & "' AND " & _ "Idioma='" & Codigo & "'" rstTextos1.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText If rstTextos1.EOF Then rstTextos1.AddNew rstTextos1!Aplicacion = rstTextos!Aplicacion rstTextos1!TextoES = rstTextos!TextoES rstTextos1!Idioma = Codigo strSQL = "SELECT * FROM Diccionario WHERE TextoES='" & rstTextos!TextoES & "' AND Idioma='" & Codigo & "'" rstDiccionario.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText If Not rstDiccionario.EOF Then rstTextos1!Texto = rstDiccionario!Texto Else rstTextos1!Texto = rstTextos!Texto End If rstDiccionario.Close rstTextos1.Update lngPalabrasNuevas = lngPalabrasNuevas + 1 End If rstTextos1.Close RaiseEvent PalabraGenerada rstTextos.MoveNext Loop rstTextos.Close RaiseEvent PalabrasGeneradas(lngPalabrasNuevas) End Sub
Funciones.cls
Un módulo de clase que contiene la función principal de traducción
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "Funciones" Attribute VB_GlobalNameSpace = True Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = True Option Explicit Dim ctl As Object Dim ctl1 As Object Dim ctl2 As Object Dim colh As Object Dim strSQL As String Dim strSQLApl As String Dim strCaption As String Dim clsObjeto As Objeto Attribute clsObjeto.VB_VarHelpID = -1 Dim strPaso As String Dim rstTextos As ADODB.Recordset Dim strFormularioNombre As String Dim strAplicacionCodigo As String Dim itxFormularioTipo As itxTipoObjeto Dim strKey As String Dim strParametro As String Dim intParametro As Integer Dim varParametro As Variant Dim varBackColor As Variant Dim varForeColor As Variant Dim intCadena As Integer Public Sub TituloAplicacion(app As Object, Optional Titulo As String = "Microsoft Access") On Error Resume Next app.CurrentDb.Properties("AppTitle").Value = Titulo app.RefreshTitleBar End Sub ' Formulario: Puede ser un Formulario Access o Visual Basic por eso se pasa como Object ' Evento : Para distinguir si son acciones de "LOAD" o de "UNLOAD" ' ConfigurarPosicion : Para ajustar el tamaño en formularios VisualBasic ' ConfigurarParametros: Para configurar parametros por defecto Public Function ConfigurarForm(Formulario As Object, Optional Evento As String = "LOAD", Optional ConfigurarPosicion As Boolean = False, Optional ConfigurarParametros As Boolean = False, Optional ParametroDeCarpeta As Boolean = False) On Error GoTo ErrorSub ' Determinar Variables Generales strAplicacionCodigo = GetSetting("INTEXIT", "INTEX Tools", "APLide", "Aplicacion Desconocida") strFormularioNombre = Formulario.Name itxFormularioTipo = TipoObjeto(Formulario) ' Procesar según evento Select Case UCase(Evento) Case "LOAD" If ConfigurarPosicion Then Formulario.Left = GetSetting("INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainLeft", 1000) Formulario.Top = GetSetting("INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainTop", 1000) Formulario.Width = GetSetting("INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainWidth", 6500) Formulario.Height = GetSetting("INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainHeight", 6500) End If ActualizarFondo Formulario Traducir Formulario, ConfigurarParametros Case "UNLOAD" If ConfigurarPosicion And itxFormularioTipo = itxVisualForm Then If Formulario.WindowState <> vbMinimized Then SaveSetting "INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainLeft", Formulario.Left SaveSetting "INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainTop", Formulario.Top SaveSetting "INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainWidth", Formulario.Width SaveSetting "INTEXIT", strAplicacionCodigo, strFormularioNombre & "MainHeight", Formulario.Height End If End If If ConfigurarParametros Then GrabarParametros Formulario End If End Select Exit Function ErrorSub: Debug.Print Err.Description End Function Public Sub ActualizarFondo(Formulario As Object) Dim strFondo As String On Error GoTo ErrorSub If ValorParametro("PersonalizarAspectoWinBol") <> True Then Exit Sub ' Se recalculan parametros por si se llama directamente a esta función sin ConfigurarForm itxFormularioTipo = TipoObjeto(Formulario) strAplicacionCodigo = GetSetting("INTEXIT", "INTEX Tools", "APLide", "Aplicacion Desconocida") strFondo = GetSetting("INTEXIT", strAplicacionCodigo, "Fondo", "sinfondo.abc") If LCase(strFondo) = "sinfondo.abc" Or LCase(strFondo) = "i:\system\easy plantillas\plantilla.jpg" Then Exit Sub End If If Dir(strFondo) <> "" Then Select Case itxFormularioTipo = itxVisualForm Case itxAccessForm Formulario.Picture = strFondo Case itxVisualForm Formulario.Picture = LoadPicture(strFondo) End Select End If Exit Sub ErrorSub: Debug.Print Err.Description End Sub Public Sub Traducir(Formulario As Object, Optional CargarParametros As Boolean = False) Dim rstTextosF As ADODB.Recordset On Error GoTo ErrorSub ' Determinar Variables Generales itxFormularioTipo = TipoObjeto(Formulario) strAplicacionCodigo = GetSetting("INTEXIT", "INTEX Tools", "APLide", "Aplicacion Desconocida") strFormularioNombre = Formulario.Name ' Comprobar Formularios If blnComprobarControles Then Set clsObjeto = New Objeto clsObjeto.Inicializar Formulario Set clsObjeto = Nothing End If ' Traducir Controles del Informe o Formulario varBackColor = ValorParametro("ColorFondoControl") varForeColor = ValorParametro("ColorTextoControl") For Each ctl In Formulario.Controls If blnTraduccionActiva Then TraducirControl ctl If CargarParametros Then ProcesarParametroControl ctl, "Cargar" If ValorParametro("PersonalizarAspectoWinBol") = True Then ModificarAspectoControl ctl, itxBackColor ModificarAspectoControl ctl, itxForeColor End If Next ctl ' Caption del Formulario si la traducción está activa If blnTraduccionActiva Then strCaption = ValidarString(Formulario.Caption, strAplicacion) Set rstTextosF = New ADODB.Recordset strSQL = "SELECT * FROM Textos WHERE " & _ "Aplicacion Like '" & strAplicacion & "' AND " & _ "TextoES='" & strCaption & "' AND " & _ "Idioma='" & strIdioma & "'" rstTextosF.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText If Not rstTextosF.EOF Then Formulario.Caption = ValidarString(rstTextosF!Texto, strCaption) End If rstTextosF.Close End If Exit Sub ErrorSub: MsgErr "Funciones.Traducir" End Sub Private Sub TraducirControl(ByRef ControlOrigen As Object) 'ProcesarControl ctl, "pz" ESTA PENDIENTE On Error GoTo TraducirEspecial ' Select Case TypeName(ControlOrigen) ' Case "" ' End Select ' Controles que tienen la propiedad Caption strCaption = "" strCaption = ValidarString(ControlOrigen.Caption) If strCaption <> "" Then ControlOrigen.Caption = TraducirTexto(ControlOrigen.Caption) End If Exit Sub TraducirEspecial: On Error GoTo TraducirFin TraducirColumnas ControlOrigen ' Controles de Lista Exit Sub TraducirFin: MsgErr "Funciones.Traducir" End Sub Private Sub TraducirColumnas(ByRef ControlOrigen As Object) On Error GoTo TraducirFin For Each colh In ctl.ColumnHeaders colh.Text = TraducirTexto(colh.Text) Next Exit Sub TraducirFin: ' No se pone mensaje para evitar error reiterativos End Sub Public Function TraducirTexto(Texto As String) As String Dim strTexto As String On Error GoTo ErrorSub Debug.Print Texto TraducirTexto = ValidarString(Texto) If Not blnTraduccionActiva Then Exit Function If ValidarString(Texto) = "" Then Exit Function Select Case Right(Texto, 1) Case ":" strTexto = Mid(Texto, 1, Len(Texto) - 1) Case " " strTexto = Mid(Texto, 1, Len(Texto) - 1) Case Else strTexto = Texto End Select strSQL = "SELECT * FROM Textos WHERE " & _ "TextoES=" & """" & strTexto & """" & " AND Idioma='" & strIdioma & "'" If ValidarString(strAplicacion) <> "" Then If strAplicacion <> "*" Then strSQL = strSQL & " AND Aplicacion='" & strAplicacion & "'" End If End If If rstTextos Is Nothing Then Set rstTextos = New ADODB.Recordset End If rstTextos.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText If Not rstTextos.EOF Then TraducirTexto = ValidarString(rstTextos!Texto) If TraducirTexto <> "" Then Select Case Right(Texto, 1) Case ":" TraducirTexto = TraducirTexto & ":" Case " " TraducirTexto = TraducirTexto & " " End Select Else If blnComprobarControles Then ' Se actualiza el texto rstTextos!Texto = strTexto rstTextos.Update End If End If Else If blnComprobarControles Then AltaControl Texto End If End If rstTextos.Close Exit Function ErrorSub: MsgErr "Funciones.TraducirTexto" Debug.Print Err.Description 'Resume End Function Private Sub AltaControl(Texto As String) Dim rstTextosA As ADODB.Recordset On Error GoTo ErrorSub If Len(Texto) >= 256 Then Exit Sub Set rstTextosA = New ADODB.Recordset rstTextosA.Open "Textos", Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdTable rstTextosA.AddNew rstTextosA("Aplicacion") = strAplicacion rstTextosA("TextoES") = Texto rstTextosA("Idioma") = strIdioma rstTextosA("Texto") = Texto rstTextosA.Update rstTextosA.Close Exit Sub ErrorSub: ' No se pone error por los duplicados End Sub Public Sub OpcionesINTEXTranslator() frmOpciones.Show 1 End Sub Public Sub CambioIdiomaINTEXTranslator(NuevoIdioma As String) strIdioma = NuevoIdioma SaveSetting app.Title, "Settings", "Idioma", NuevoIdioma End Sub Public Sub TraducirMenusAccess(BarraMenu As Object, CodigoIdioma As String) If Not blnTraduccionActiva Then Exit Sub For Each ctl In BarraMenu.Controls If CodigoIdioma = "es" Then ctl.Caption = TextoES(ctl.Caption) Else ctl.Caption = TraducirTexto(ctl.Caption) End If If ctl.Type = 10 Then For Each ctl1 In ctl.Controls If CodigoIdioma = "es" Then ctl1.Caption = TextoES(ctl1.Caption) Else ctl1.Caption = TraducirTexto(ctl1.Caption) End If If ctl1.Type = 10 Then For Each ctl2 In ctl1.Controls If CodigoIdioma = "es" Then ctl2.Caption = TextoES(ctl2.Caption) Else ctl2.Caption = TraducirTexto(ctl2.Caption) End If Next End If Next End If Next End Sub Private Function TextoES(Texto As String) As String On Error GoTo ErrorSub TextoES = Texto strSQL = "SELECT * FROM Textos WHERE Texto=" & """" & Texto & """" If rstTextos Is Nothing Then Set rstTextos = New ADODB.Recordset End If rstTextos.Open strSQL, Parametros.ADO.ITR, adOpenKeyset, adLockOptimistic, adCmdText If Not rstTextos.EOF Then TextoES = rstTextos!TextoES End If rstTextos.Close Exit Function ErrorSub: 'MsgErr TextoES End Function Public Sub Mensaje(Texto As String, Optional Titulo As String = "") On Error Resume Next If Titulo = "" Then Titulo = GetSetting("INTEXIT", "INTEX Tools", "APLide", "Mensaje") If Titulo = "INTEX WinBol2000" Then Titulo = "WinBol" End If If blnTraduccionActiva Then MsgBox TraducirTexto(Texto), vbInformation, Titulo Else MsgBox ValidarString(Texto), vbInformation, Titulo End If End Sub Private Sub GrabarParametros(Formulario As Object) ' Determinar Variables Generales On Error GoTo ErrorSub For Each ctl In Formulario.Controls ProcesarParametroControl ctl, "Grabar" Next Exit Sub ErrorSub: End Sub Private Sub ProcesarParametroControl(ByRef ControlOrigen As Object, Accion As String) On Error GoTo ErrorSub ' Control de que exista el literal PARAM intParametro = InStr((UCase(ControlOrigen.Tag)), "PARAM") ' Por parametro If intParametro = 0 Then Exit Sub ' Control de que sea un parametro de carpeta intParametro = InStr((UCase(ControlOrigen.Tag)), "CARP") ' Por parametro de Carpeta If intParametro = 0 Then ' No es un parametro de Carpeta strKey = "Parametro." & strFormularioNombre & "." & ControlOrigen.Name Else ' Es un parametro que depende de la carpeta activa strKey = "Parametro." & strFormularioNombre & "." & ControlOrigen.Name & "." & GetSetting("INTEXIT", strAplicacionCodigo, "CarpetaActiva", "Shared") End If ' Pendiente probar para campos de fechas de momento solo funciona para textos Select Case itxFormularioTipo Case itxVisualForm ' Para formularios de Visual Basic If Accion = "Grabar" Then Select Case TypeName(ControlOrigen) Case "DTPicker" SaveSetting "INTEXIT", strAplicacionCodigo, strKey, ControlOrigen.Value Case Else SaveSetting "INTEXIT", strAplicacionCodigo, strKey, ControlOrigen.Text End Select Else ' Accion=Cargar strParametro = GetSetting("INTEXIT", strAplicacionCodigo, strKey, "") Select Case TypeName(ControlOrigen) Case "DTPicker" If strParametro <> "" Then ControlOrigen.Value = strParametro Case Else If strParametro <> "" Then ControlOrigen.Text = strParametro End Select End If Case itxAccessForm ' Para formularios de Access If Accion = "Grabar" Then 'MsgBox TypeName(ControlOrigen) Checkbox Select Case TypeName(ControlOrigen) Case "DTPicker" SaveSetting "INTEXIT", strAplicacionCodigo, strKey, ControlOrigen.Value Case Else SaveSetting "INTEXIT", strAplicacionCodigo, strKey, ControlOrigen.Value End Select Else ' Accion=Cargar strParametro = GetSetting("INTEXIT", strAplicacionCodigo, strKey, "") Select Case TypeName(ControlOrigen) Case "DTPicker" If strParametro <> "" Then ControlOrigen.Value = strParametro Case Else If strParametro <> "" Then ControlOrigen.Value = strParametro End Select End If SaveSetting "INTEXIT", strAplicacionCodigo, strKey, ControlOrigen.Value End Select Exit Sub ErrorSub: End Sub Public Function ValorParametro(NombreParametro As String, Optional ValorPorDefecto As Variant) As Variant ValorParametro = "" strAplicacionCodigo = GetSetting("INTEXIT", "INTEX Tools", "APLide", "Aplicacion Desconocida") varParametro = GetAllSettings("INTEXIT", strAplicacionCodigo) ' Primero intenta encontrar los parametros Generales y despues comprobar la carpeta activa For intParametro = LBound(varParametro, 1) To UBound(varParametro, 1) intCadena = InStr(1, UCase(varParametro(intParametro, 0)), UCase(NombreParametro)) If intCadena > 0 Then ' Comprobar si hay un parametro con este nombre y tomar el primero que se encuentre ValorParametro = varParametro(intParametro, 1) intCadena = InStr(1, UCase(varParametro(intParametro, 0)), GetSetting("INTEXIT", strAplicacionCodigo, "CarpetaActiva", "Shared")) If intCadena > 0 Then ' Comprobar si puede depender de la carpeta ValorParametro = varParametro(intParametro, 1) Exit For ' Se sale no puede haber 2 parametros iguales para la misma carpeta End If 'Debug.Print varParametro(intParametro, 0), varParametro(intParametro, 1) End If Next intParametro ' Comprobar Valores If ValorParametro = "" Then If Not IsMissing(ValorPorDefecto) Then ValorParametro = ValorPorDefecto End If End If End Function ' Modificar Propiedades del Control: ' 1. BackColor Private Sub ModificarAspectoControl(ByRef ControlOrigen As Object, PropiedadControl As itxPropiedadControl) On Error GoTo ModificarAspectoControlErr If ValorParametro("PersonalizarAspectoWinBol") <> True Then Exit Sub ' Select Case TypeName(ControlOrigen) ' Case "" ' End Select Select Case PropiedadControl Case itxBackColor ControlOrigen.BackColor = varBackColor Case itxForeColor ControlOrigen.ForeColor = varForeColor End Select Exit Sub ModificarAspectoControlErr: ' Por si el control no tiene la propiedad Exit Sub End Sub Private Sub Class_Terminate() On Error Resume Next End Sub Private Sub clsObjeto_Documentar() ' End Sub