Newsgroup: it.comp.lang.visual-basic
Titolo: Software multi lingue

Non riesco proprio a venire a campo a questo maledetto problema. Ho
creato un programma che deve essere disponibile in lingua inglese oltra
a quella italiana. Da bravo programmatore o creato un file di risorse
(nel complesso i testi nel sw sono < 600) con due string table: una
definita come italiano e l'altra definita come Neutro (predefinito
sistema). Quando faccio girare il programma su un sistema operativo
(2000 o xp) in italiano non ci sono problemi, ovvero mi carica
correttamente la string table definita come italiano con le relative
stringhe in italiano. Quando lo provo su in SO in lingua inglese,
continua a caricarmi la string table in italiano!! Qualche idea?
Volevo evitare l'ipotesi di accodare tutte le stringhe in un unica
string table, definendo quelle in italiano ad esempio dal 1000 al 2000,
mentre quelle inglesi da 2001 a 3000.... non mi piace sopratutto per
fra non molto ho intenzione di inserire lo spagnolo, tedesco e
francesce...

 francesce...
Ne avevo parlato un paio d'anni fa, magari la memoria di Google č cosě
profonda, comunque...
Non usare un file di risorse: ti consiglio di utilizzare un file INI, o
anche un file INI per ogni lingua disponibile nel programma. Addirittura, se
il tuo programma fa uso di un database, e insieme devi distribuire anche
ADO, puoi caricare le stringhe in un database. Tutti questi metodi sono piů
semplici da gestire.
Il file INI č un file di testo, per cui le stringhe possono essere gestite
anche con il Blocco note. Il database (io ho utlizzato un .mdb) puň
permettere di visualizzare la colonna italiana affiancata alla colonna da
tradurre, ed č piů facile lavorarci. Il file Risorse richiede VB, e spesso
un traduttore esterno non ce l'ha.
Io ho utilizzato un sistema con due o tre file INI per applicazioni
bi/tri-lingue, ma per un programma tradotto in sette lingue ho utilizzato un
database di Access, con una tabella Stringhe con le colonne IDStringa,
Italiano, Inglese, Francese, Spagnolo, Portoghese, Tedesco, Olandese, ed ho
aggiunto anche altre colonne, che per ora sono vuote, per ulteriori
traduzioni future. Ogni traduttore esterno che ho interpellato doveva
soltanto avere Access per inserire le stringhe nella sua lingua.
Infine, anche se non intendi installare ADO, puoi immagazzinarti tutte le
stringhe in un database di Access, e poi esportare la tabella in un file di
testo. Puoi anche estrarre soltanto la colonna IDStringa e quella della
lingua desiderata tramite una query, e poi esportarne i risultati in un
documento di testo, che dopo puoi trasformare in un file INI.
Insomma. utilizzare file esterni č un metodo migliore del file di risorse:
se in seguito ti accorgi che c'č un errore di traduzione, o devi aggiungere
una lingua, o modificare una stringa, non hai bisogno di ricompilare il
programma ma ti basta distribuire agli utenti soltanto i file delle
stringhe.
Tutto questo IMHO.
Spero ti sia d'aiuto. Ciao.
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

Posso chiederti se il campo identificativo dell'errore lo hai messo come 
semplice numero o come testo?
Es numerico: 0001 - Errore connessione
Es stringa: err_connessione - Errore connessione
Con il "-" ho simbolicamente diviso la colonna ID dalla colonna italian :)
Grazie. 

 se in seguito ti accorgi che c'è un errore di traduzione, o devi aggiungere


 Es stringa: err_connessione - Errore connessione
Il codice di errore, anche se personalizzato deve sempre essere un numero.
E' la stringa da visualizzare che è identificata dall'ID.
On Error GoTo ErrRoutine
    [...codice...]
    Err.Raise 10001 ' solleva un errore personalizzato
    [....]
ErrRoutine:
    If Err.Number = 10001 Then
        msg = LeggiStringaDiTesto(2505) ' legge la stringa dal file INI
    ElseIf Err.Number = 10002 Then
        msg = LeggiStringaDiTesto(2506) ' legge la stringa dal file INI
    [.....]
    Else
        msg = Err.Description ' riporta la descrizione standard dell'errore
    End If
    MsgBox msg, vbInformation +vbOKOnly, App.Title
La stringa della msgbox è una stringa come le altre. Magari ci può essere
una discordanza se metto dei caratteri per andare a capo, e può succedere
che le righe nella msgbox non hanno la stessa lunghezza, ma è un dettaglio.


>        msg = LeggiStringaDiTesto(2505) ' legge la stringa dal file INI
Questo 2505 è il numero della riga oppure ti testi tutte le righe alla 
ricerca della stringa che inizia per 2505? 

 ricerca della stringa che inizia per 2505? 
Immagino sia una voce del tipo "Stringa 2505=[...]", quindi presumo la
legga con:
stringa=LeggiStringaINI("Stringa " & NUMERO_STRINGA)
ciao
marco

Dopo due giorni intensi, ho trovato la soluzione al mio problema.
PROBLEMA:
Definendo nel progretto due String Table di lingua diversa (una in
Italiano e l'altra in Inglese), dopo la compilazione dello stesso, mi
continuava a caricare sempre la String Table in Italiano anche se il SO
è in Inglese.
SOLUZIONE:
Abbandonata la classica LoadResString() ho adottato la seguente
funzione.
Public Declare Function GetSystemDefaultLCID Lib "kernel32" () As Long
Public Declare Function GetLastError Lib "kernel32" () As Long
Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA"
(ByVal lpLibFileName As String) As Long
Public Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As
Long) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(Destination As Any, Source As Any, ByVal Length As Long)
'Public Declare Function FindResource Lib "kernel32" Alias
"FindResourceA" (ByVal hInstance As Long, ByVal lpName As String, ByVal
lpType As Any) As Long
Public Declare Function FindResource Lib "kernel32" Alias
"FindResourceA" (ByVal hModule As Long, ByVal lpName As Any, ByVal
lpType As Any) As Long
Public Declare Function FindResourceEx Lib "kernel32" Alias
"FindResourceExA" (ByVal hModule As Long, ByVal lpType As Any, ByVal
lpName As Any, ByVal wLanguage As Long) As Long
Public Declare Function LoadResource Lib "kernel32" (ByVal hInstance As
Long, ByVal hResInfo As Long) As Long
Public Declare Function LockResource Lib "kernel32" (ByVal hResData As
Long) As Long
Public Declare Function SizeofResource Lib "kernel32" (ByVal hInstance
As Long, ByVal hResInfo As Long) As Long
Public Declare Function FreeResource Lib "kernel32" (ByVal hResData As
Long) As Long
' Predefined Resource Types
Public Const RT_CURSOR = 1&
Public Const RT_BITMAP = 2&
Public Const RT_ICON = 3&
Public Const RT_MENU = 4&
Public Const RT_DIALOG = 5&
Public Const RT_STRING = 6&
Public Const RT_FONTDIR = 7&
Public Const RT_FONT = 8&
Public Const RT_ACCELERATOR = 9&
Public Const RT_RCDATA = 10&
Public Const RT_HTML = 23&
Public Function LoadResString(ResourceID As Integer) As String
Dim hRes As Long
Dim hInst As Long
Dim lngResID As Long
Dim lngLanguage As Long
Dim hResInfo As Long
Dim lpData As Long
Dim lngArraySize As Long
Dim bytData() As Byte
Dim lngPos As Long
Dim lngID As Long
Dim lngLength As Long
Dim strBuffer As String
Dim FILEName As String
On Error Resume Next
    ' Si ricava impostazione internazionale della lingua del computer:
1040 Italiano, 1033 Inglese (Stati Uniti)
    lngLanguage = GetSystemDefaultLCID
    ' Se è una lingua non conosciuta, la forzo a inglese
    If lngLanguage <> 1040 And lngLanguage <> 1033 Then lngLanguage =
1033
    ' File dove estrarre le risorse (può essere anche esterno...)
    FILEName = App.Path + "\" + App.EXEName + ".exe"
    'get the instance of the EXE or the DLL
    ' Si ricava l'istanza dell'EXE o della DLL
    hInst = LoadLibrary(FILEName)
    ' Le string table sono memorizzate a blocchi di 16
    lngResID = Int(ResourceID / 16 + 1)
    hRes = FindResourceEx(hInst, ByVal CLng(RT_STRING), ByVal lngResID,
ByVal lngLanguage)                                      'search for the
resource
    If hRes > 0 Then
        ' Load the resource in memory
        hResInfo = LoadResource(hInst, hRes)
        ' Lock the resource in memory
        lpData = LockResource(hResInfo)
        ' Get the size in byte of the resource
        lngArraySize = SizeofResource(hInst, hRes)
        If lngArraySize > 0 Then
            ' Redim an array of byte with the size -1 (null terminating
string)
            ReDim bytData(lngArraySize - 1)
            ' Copy from global memory to the local array of byte
            Call CopyMemory(bytData(0), ByVal lpData, lngArraySize)
            ' For the lock of 16 resource string
            For lngID = ((lngResID - 1) * 16) To (lngResID * 16 - 1)
                ' Get the size of the string
                Call CopyMemory(lngLength, bytData(lngPos), 2)
                If lngLength Then
                    ' Prepare the buffer with null string
                    strBuffer = String(lngLength, 0)
                    ' Copy the string to the buffer
                    Call CopyMemory(ByVal StrPtr(strBuffer),
bytData(lngPos + 2), lngLength * 2)
                    ' If the current resource string ID is matching the
argument
                    If lngID = ResourceID Then
                        LoadResString = strBuffer
                        Exit For
                    End If
                    ' Goto the next position in the array of byte of
the resource ID
                    lngPos = lngPos + lngLength * 2 + 2
                Else
                    ' If the length is = 0, no resource string at this
position
                    ' Goto next position in the array of byte of the
resource ID
                    lngPos = lngPos + 2
                End If
            Next lngID
        End If
        Call FreeResource(hResInfo)
 
    End If
 
    FreeLibrary ByVal hInst
End Function

 ricerca della stringa che inizia per 2505?
Nel caso del database, è l'ID della stringa. Nel caso del file INI è la
chiave della stringa:
[Italiano]
1=ATTENZIONE!
2=Funzione non disponibile
[... altre stringhe ...]
2505=Si è verificato un errore durante il lancio del missile. L'operazione
verrà interrotta
[Français]
1=ATTENTION!
2=Fonctionne non disposable
[....altre stringhe...]
2505=Attention! N'est pas eté possible de lancher le missile. L'operation
serà interruptée.
[English]
1=ATTENTION!
2=Function not avaliable
[....altre stringhe...]
2505=Attention! Error during Rocket launch. Operation has been stopped.
[Espanol]
1=ATTENZIONES!
2=Funziones no està disponible
[....altre stringhe...]
2505=Attenziones! No es possible de lanchar el missile. La operazion sarà
interrompida.
[... e così via....]
Capito?


 verrà interrotta
Si ma quello che volevo capire è che cmq la funzione che legge il file ini 
se lo deve scorrere tutto quanto alla ricerca della stringa con id 2505. 
Testando probabilmente, visto che l'id non ha lunghezza fissa (cosa 
tranquillamente impostabile), se il codice dal carattere 0 al carattere 
(uguale -1) è uguale a 2505.
Da capire se questa lettura di stringhe è più pesante a livello 
prestazionale e di ram rispetto ad una analoga struttura in una tabdella di 
un database mdb o qualsiasi altro si decida di usare. Il vantaggio di un 
database è che invece di scorrere tutte le righe posso puntare direttamente 
al record. Il problema è che in fase di distribuzione devo comunque 
ricordarmi di inserire ado tra i pacchetti distribuiti. 

ricordarmi di inserire ado tra i pacchetti distribuiti. 
Io per i sw con lingue assegno le stringhe ad una matrice e poi le scrivo
chiamando una matrice di stringhe
Print GetStr(123)
Da notare che GetStr() può essere una matrice di stringhe oppure anche una
funzione.
Infatti nei programmi scritti in QB e in VB3-4 dove la memoria era limitata a
pocki KB la usavo come funzione, e sono passato alla matrice senza fare
modifiche ai programmi, solo alla dichiarazione.
Leggi questo thread:
http://shorterlink.com/?PLSWHX
--
ciao
  Stefano

 Testando probabilmente, visto che l'id non ha lunghezza fissa ....
[...CUT...]
(Perché reinventare la ruota?)
Anch'io, come SB, carico tutte le stringhe in una matrice durante il load
della form, o dell'applicazione. Ormai i pc hanno abbastanza ram da
dimenticarsi i problemi di prestazioni per tutte le stringhe in memoria
(tieni presente che un file INI è una decina di kilobyte).
La lettura del file INI si fa con le API, ed è velocissima.
Leggi qui (purtroppo però la descrizione è in inglese):
http://www.andreavb.com/forum/news/viewtopic_6298.html
Leggere un file INI è anche più veloce che caricare un recordset di stringhe
con ADO.
Io imposto una variabile pubblica per la lingua corrente, e a seconda del
valore della variabile, leggo la sezione appropriata quando carico la form.
In qs. modo posso implementare anche un menu per cambiare lingua durante il
runtime, mi basta cambiare la variabile e ricaricare le stringhe della form
corrente, e le altre form verranno ricaricate al load. Con un altro file INI
posso anche salvare qule era l'ultima lingua utilizzata, e ricaricarla.
Ma comunque sembra che questa discussione non interessi più all'OP originale
che mi pare abbia deciso bontà sua di continuare ad usare i file di risorse.


 risorse.
Si beh ma la discussione potrebbe interessare anche altri... 

 le API posso leggere velocemente tutti i files.
Dunque, continuo soltanto per approfondire sui file INI.
Dal punto di vista dell'utilizzatore, il file INI è un file di testo come
gli altri. Invece, dal punto di vista del s.o. il file INI è suddiviso in
sezioni. Questo permette di accelerarne la ricerca, con delle API opportune.
Cioè, non è la stessa cosa che scorrere un file di testo normale, seppure
con le API. Le due function di cui ti ho postato il link scorrono soltanto
le sezioni, e appena individuano quella giusta (la prima, ci potrebbero
anche essere due o più sezioni con lo stesso nome, ma le function si fermano
alla prima) interrompono la lettura delle sezioni, e cercano la chiave tra
le chiavi della sezione (anche qui, cercano la prima disponibile), e non
appena la trovano escono dalla lettura, mentre normalmente un file di testo
viene letto tutto, anche dopo che è stata trovata l'occorrenza cercata. In
più, a questo meccanismo accelerato viene aggiunta la maggiore velocità
fornita dall'utilizzare le API invece dei cicli.
Comunque le puoi testare e verificare se sono abbastanza veloci: io le uso
da anni, e nel progetto che sto facendo adesso, il file di stringhe -
italiano, questo progetto non è multilingue - contiene 27000 caratteri, e
non ci sono ritardi nelle msgbox o nel caricamento delle form.
Spero sia utile. Ciao.
