dcsimg
Results 1 to 13 of 13

Thread: [RESOLVED] How to properly store and display Unicode captions

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Resolved [RESOLVED] How to properly store and display Unicode captions

    I'm adding multilingual support to a program. I would like to cover at least the main languages.
    I want to do it without any external file.

    I currently have something like this:

    Code:
        Select Case LanguageID
            Case bsLang_SPANISH
                Select Case TextID
                    Case bsPPGUI_General_CloseButton_Caption
                        GetLocalizedString = "&Cerrar"
                    Case bsPPGUI_General_OKButton_Caption
                        GetLocalizedString = "&Aceptar"
                    Case bsPPGUI_General_CancelButton_Caption
                        GetLocalizedString = "&Cancelar"
                End Select
            Case bsLang_CHINESE_SIMPLIFIED
                Select Case TextID
                    Case bsPPGUI_General_CloseButton_Caption
                        GetLocalizedString = "&C 关闭"
                    Case bsPPGUI_General_OKButton_Caption
                        GetLocalizedString = "&O 确定"
                    Case bsPPGUI_General_CancelButton_Caption
                        GetLocalizedString = "&C 取消"
                End Select
            Case bsLang_ITALIAN
                Select Case TextID
                    Case bsPPGUI_General_CloseButton_Caption
                        GetLocalizedString = "&Chiudi"
                    Case bsPPGUI_General_OKButton_Caption
                        GetLocalizedString = "&OK"
                    Case bsPPGUI_General_CancelButton_Caption
                        GetLocalizedString = "&Annulla"
                End Select
            Case Else ' English
                Select Case TextID
                    Case bsPPGUI_General_CloseButton_Caption
                        GetLocalizedString = "&Close"
                    Case bsPPGUI_General_OKButton_Caption
                        GetLocalizedString = "&OK"
                    Case bsPPGUI_General_CancelButton_Caption
                        GetLocalizedString = "&Cancel"
                End Select
        End Select
    This is just a sample, the captions are many more.

    The Chinese captions were written by a Chinese programmer, a member of this forum. They are supposed to work OK, at least in his machine.
    But the characters that I read there are not Chinese. I think he sees them right because he has VB6 in Chinese, but I'm worried that if I compiled these captions as they are now, they will be displayed wrongly, even if the end user has his machine in Chinese.

    The question is: how to store Unicode captions properly within the VB6 program?
    I know that I could write them character by character using ChrW, but there should be another way.

    That's my main question.
    And I have a second question, related:

    He told me that the texts were displayed right in Chinese in standard labels using the font Tahoma.

    I just tested here, by copying some Chinese text to the clipboard and using APIs to get the clipboard text in Unicode, then I set this Unicode Chinese text to a label with font tahoma.
    I got ?? (question marks), it didn't show up properly.
    I suppose that it can work if the end user has the machine in Chinese, but I would like to be sure.

    Thanks in advance.

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    5,840

    Re: How to properly store and display Unicode captions

    Here's some code that might get you part of the way there.

    Code:
    
    Option Explicit
    '
    Public Type RECT
        Left   As Long
        Top    As Long
        Right  As Long ' This is +1 (right - left = width)
        Bottom As Long ' This is +1 (bottom - top = height)
    End Type
    Private Type GETTEXTEX
        cb As Long
        Flags As Long
        CodePage As Long
        lpDefaultChar As Long
        lpUsedDefChar As Long
    End Type
    '
    Private Type GETTEXTLENGTHEX
        Flags As Long
        CodePage As Long
    End Type
    '
    Private Type SETTEXTEX
        Flags As Long
        CodePage As Long
    End Type
    '
    Private Declare Function DefWindowProcW Lib "user32.dll" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function SysAllocStringLen Lib "oleaut32.dll" (ByVal OleStr As Long, ByVal bLen As Long) As Long
    Private Declare Function OpenClipboard Lib "user32.dll" (ByVal hWnd As Long) As Long
    Private Declare Function EmptyClipboard Lib "user32.dll" () As Long
    Private Declare Function CloseClipboard Lib "user32.dll" () As Long
    Private Declare Function IsClipboardFormatAvailable Lib "user32.dll" (ByVal wFormat As Long) As Long
    Private Declare Function GetClipboardData Lib "user32.dll" (ByVal wFormat As Long) As Long
    Private Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Long, ByVal hMem As Long) As Long
    Private Declare Function lstrcpyW Lib "kernel32.dll" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
    Private Declare Function GetClientRect Lib "user32.dll" (ByVal hWnd As Long, lpRect As RECT) As Long
    Private Declare Function InvalidateRect Lib "user32.dll" (ByVal hWnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
    Private Declare Function GlobalAlloc Lib "kernel32.dll" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
    Private Declare Function GlobalLock Lib "kernel32.dll" (ByVal hMem As Long) As Long
    Private Declare Function GlobalUnlock Lib "kernel32.dll" (ByVal hMem As Long) As Long
    'Private Declare Function GlobalSize Lib "kernel32.dll" (ByVal hMem As Long) As Long
    Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long
    Private Declare Sub PutMem2 Lib "msvbvm60" (ByRef Ptr As Any, ByRef Value As Any)
    Private Declare Sub PutMem4 Lib "msvbvm60" (ByRef Ptr As Any, ByRef Value As Any)
    '
    ' The following is from MSDN help:
    '
    ' UNICODE: International Standards Organization (ISO) character standard.
    ' Unicode uses a 16-bit (2-byte) coding scheme that allows for 65,536 distinct character spaces.
    ' Unicode includes representations for punctuation marks, mathematical symbols, and dingbats,
    ' with substantial room for future expansion.
    '
    ' vbUnicode constant:     Converts the string toUnicode using the default code page of the system.
    ' vbFromUnicode constant: Converts the string from Unicode to the default code page of the system.
    '
    ' LCID: The LocaleID, if different than the system LocaleID. (The system LocaleID is the default.)
    '
    
    Public Property Let UniCaption(TheHwnd As Long, sUniCaption As String)
        ' Set Unicode string into the caption.
        '
        ' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
        ' Other controls are not known.
        '
        ' Also, if this is used on the Form, there probably needs to be a bit of subclassing.
        '    Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
        '       Select Case Msg
        '        Case WM_GETTEXT
        '            Result = DefWindowProcW(hWnd, Msg, wParam, lParam)
        '        Case Else
        '            Result = SC.CallWindowProc(Msg, wParam, lParam)
        '        End Select
        '    End Sub
        '
        Const WM_SETTEXT As Long = &HC&
        Dim uRect As RECT
        DefWindowProcW TheHwnd, WM_SETTEXT, 0&, ByVal StrPtr(sUniCaption)
        GetClientRect TheHwnd, uRect
        InvalidateRect TheHwnd, uRect, 1&
    End Property
    
    Public Property Get UniCaption(TheHwnd As Long) As String
        ' Get Unicode string from caption.
        '
        ' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
        ' Other controls are not known.
        '
        Const WM_GETTEXT As Long = &HD&
        Const WM_GETTEXTLENGTH As Long = &HE&
        Dim lLen As Long
        Dim lPtr As Long
        lLen = DefWindowProcW(TheHwnd, WM_GETTEXTLENGTH, 0&, ByVal 0&)  ' Get length of caption.
        If lLen Then ' Must have length.
            lPtr = SysAllocStringLen(0&, lLen)                          ' Create a BSTR of that length.
            PutMem4 ByVal VarPtr(UniCaption), ByVal lPtr                ' Make the property return the BSTR.
            DefWindowProcW TheHwnd, WM_GETTEXT, lLen + 1&, ByVal lPtr   ' Call the default Unicode window procedure to fill the BSTR.
        End If
    End Property
    
    Public Property Let UniClipboard(sUniText As String)
        ' Puts a VB string in the clipboard without converting it to ANSI.
        '
        ' Not sure why, but when we IMMEDIATELY try to fetch the clipboard's data after just putting it in there,
        ' sometimes Windows needs a few cycles to get things organized.  A Sleep 100 between Let and Get always
        ' solves the problem.
        '
        Dim iStrPtr As Long
        Const GMEM_MOVEABLE     As Long = &H2&
        Const GMEM_ZEROINIT     As Long = &H40&
        Const CF_UNICODETEXT    As Long = &HD&
        '
        OpenClipboard 0&
        EmptyClipboard
        iStrPtr = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, LenB(sUniText) + 2&)
        lstrcpyW GlobalLock(iStrPtr), StrPtr(sUniText)
        GlobalUnlock iStrPtr
        SetClipboardData CF_UNICODETEXT, iStrPtr
        CloseClipboard
    End Property
    
    Public Property Get UniClipboard() As String
        ' Gets a UNICODE string from the clipboard and puts it in a standard VB string (which is UNICODE).
        '
        ' Not sure why, but when we IMMEDIATELY try to fetch the clipboard's data after just putting it in there,
        ' sometimes Windows needs a few cycles to get things organized.  A Sleep 100 between Let and Get always
        ' solves the problem.
        '
        Dim iStrPtr As Long
        Const CF_UNICODETEXT    As Long = &HD&
        '
        OpenClipboard 0&
        If IsClipboardFormatAvailable(CF_UNICODETEXT) Then
            iStrPtr = GetClipboardData(CF_UNICODETEXT)
            If iStrPtr Then
                UniClipboard = String$(lstrlenW(iStrPtr), vbNullChar)
                lstrcpyW StrPtr(UniClipboard), GlobalLock(iStrPtr)
                GlobalUnlock iStrPtr
            End If
        End If
        CloseClipboard
    End Property
    
    
    Now, actually storing this unicode gets a bit trickier. If it were the caption of a user-control, I'd put it into an array and save it as a property, forcing it into the CTX file. However, with just a typical form, that's not so easy. I haven't done it, but there might be a way to create a new property for a form, and stuff that into the FRX file. That's the only way I could think to do it. It'll be interesting to see what others come up with.

    Also, you do know that the Properties Window won't show any unicode, so your Caption property on the Properties Window is going to be useless (or maybe even damaging to your efforts).
    Any software I post in these forums written by me is provided 揂S IS without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I抳e been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a 揤B6 random code folder that is overflowing. I抳e been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Elroy View Post
    Here's some code that might get you part of the way there.

    Code:
    
    Option Explicit
    '
    Public Type RECT
        Left   As Long
        Top    As Long
        Right  As Long ' This is +1 (right - left = width)
        Bottom As Long ' This is +1 (bottom - top = height)
    End Type
    Private Type GETTEXTEX
        cb As Long
        Flags As Long
        CodePage As Long
        lpDefaultChar As Long
        lpUsedDefChar As Long
    End Type
    '
    Private Type GETTEXTLENGTHEX
        Flags As Long
        CodePage As Long
    End Type
    '
    Private Type SETTEXTEX
        Flags As Long
        CodePage As Long
    End Type
    '
    Private Declare Function DefWindowProcW Lib "user32.dll" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function SysAllocStringLen Lib "oleaut32.dll" (ByVal OleStr As Long, ByVal bLen As Long) As Long
    Private Declare Function OpenClipboard Lib "user32.dll" (ByVal hWnd As Long) As Long
    Private Declare Function EmptyClipboard Lib "user32.dll" () As Long
    Private Declare Function CloseClipboard Lib "user32.dll" () As Long
    Private Declare Function IsClipboardFormatAvailable Lib "user32.dll" (ByVal wFormat As Long) As Long
    Private Declare Function GetClipboardData Lib "user32.dll" (ByVal wFormat As Long) As Long
    Private Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Long, ByVal hMem As Long) As Long
    Private Declare Function lstrcpyW Lib "kernel32.dll" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
    Private Declare Function GetClientRect Lib "user32.dll" (ByVal hWnd As Long, lpRect As RECT) As Long
    Private Declare Function InvalidateRect Lib "user32.dll" (ByVal hWnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
    Private Declare Function GlobalAlloc Lib "kernel32.dll" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
    Private Declare Function GlobalLock Lib "kernel32.dll" (ByVal hMem As Long) As Long
    Private Declare Function GlobalUnlock Lib "kernel32.dll" (ByVal hMem As Long) As Long
    'Private Declare Function GlobalSize Lib "kernel32.dll" (ByVal hMem As Long) As Long
    Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long
    Private Declare Sub PutMem2 Lib "msvbvm60" (ByRef Ptr As Any, ByRef Value As Any)
    Private Declare Sub PutMem4 Lib "msvbvm60" (ByRef Ptr As Any, ByRef Value As Any)
    '
    ' The following is from MSDN help:
    '
    ' UNICODE: International Standards Organization (ISO) character standard.
    ' Unicode uses a 16-bit (2-byte) coding scheme that allows for 65,536 distinct character spaces.
    ' Unicode includes representations for punctuation marks, mathematical symbols, and dingbats,
    ' with substantial room for future expansion.
    '
    ' vbUnicode constant:     Converts the string toUnicode using the default code page of the system.
    ' vbFromUnicode constant: Converts the string from Unicode to the default code page of the system.
    '
    ' LCID: The LocaleID, if different than the system LocaleID. (The system LocaleID is the default.)
    '
    
    Public Property Let UniCaption(TheHwnd As Long, sUniCaption As String)
        ' Set Unicode string into the caption.
        '
        ' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
        ' Other controls are not known.
        '
        ' Also, if this is used on the Form, there probably needs to be a bit of subclassing.
        '    Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
        '       Select Case Msg
        '        Case WM_GETTEXT
        '            Result = DefWindowProcW(hWnd, Msg, wParam, lParam)
        '        Case Else
        '            Result = SC.CallWindowProc(Msg, wParam, lParam)
        '        End Select
        '    End Sub
        '
        Const WM_SETTEXT As Long = &HC&
        Dim uRect As RECT
        DefWindowProcW TheHwnd, WM_SETTEXT, 0&, ByVal StrPtr(sUniCaption)
        GetClientRect TheHwnd, uRect
        InvalidateRect TheHwnd, uRect, 1&
    End Property
    
    Public Property Get UniCaption(TheHwnd As Long) As String
        ' Get Unicode string from caption.
        '
        ' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
        ' Other controls are not known.
        '
        Const WM_GETTEXT As Long = &HD&
        Const WM_GETTEXTLENGTH As Long = &HE&
        Dim lLen As Long
        Dim lPtr As Long
        lLen = DefWindowProcW(TheHwnd, WM_GETTEXTLENGTH, 0&, ByVal 0&)  ' Get length of caption.
        If lLen Then ' Must have length.
            lPtr = SysAllocStringLen(0&, lLen)                          ' Create a BSTR of that length.
            PutMem4 ByVal VarPtr(UniCaption), ByVal lPtr                ' Make the property return the BSTR.
            DefWindowProcW TheHwnd, WM_GETTEXT, lLen + 1&, ByVal lPtr   ' Call the default Unicode window procedure to fill the BSTR.
        End If
    End Property
    
    Public Property Let UniClipboard(sUniText As String)
        ' Puts a VB string in the clipboard without converting it to ANSI.
        '
        ' Not sure why, but when we IMMEDIATELY try to fetch the clipboard's data after just putting it in there,
        ' sometimes Windows needs a few cycles to get things organized.  A Sleep 100 between Let and Get always
        ' solves the problem.
        '
        Dim iStrPtr As Long
        Const GMEM_MOVEABLE     As Long = &H2&
        Const GMEM_ZEROINIT     As Long = &H40&
        Const CF_UNICODETEXT    As Long = &HD&
        '
        OpenClipboard 0&
        EmptyClipboard
        iStrPtr = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, LenB(sUniText) + 2&)
        lstrcpyW GlobalLock(iStrPtr), StrPtr(sUniText)
        GlobalUnlock iStrPtr
        SetClipboardData CF_UNICODETEXT, iStrPtr
        CloseClipboard
    End Property
    
    Public Property Get UniClipboard() As String
        ' Gets a UNICODE string from the clipboard and puts it in a standard VB string (which is UNICODE).
        '
        ' Not sure why, but when we IMMEDIATELY try to fetch the clipboard's data after just putting it in there,
        ' sometimes Windows needs a few cycles to get things organized.  A Sleep 100 between Let and Get always
        ' solves the problem.
        '
        Dim iStrPtr As Long
        Const CF_UNICODETEXT    As Long = &HD&
        '
        OpenClipboard 0&
        If IsClipboardFormatAvailable(CF_UNICODETEXT) Then
            iStrPtr = GetClipboardData(CF_UNICODETEXT)
            If iStrPtr Then
                UniClipboard = String$(lstrlenW(iStrPtr), vbNullChar)
                lstrcpyW StrPtr(UniClipboard), GlobalLock(iStrPtr)
                GlobalUnlock iStrPtr
            End If
        End If
        CloseClipboard
    End Property
    
    
    Now, actually storing this unicode gets a bit trickier. If it were the caption of a user-control, I'd put it into an array and save it as a property, forcing it into the CTX file. However, with just a typical form, that's not so easy. I haven't done it, but there might be a way to create a new property for a form, and stuff that into the FRX file. That's the only way I could think to do it. It'll be interesting to see what others come up with.

    Also, you do know that the Properties Window won't show any unicode, so your Caption property on the Properties Window is going to be useless (or maybe even damaging to your efforts).
    Thank you Elroy.
    I have to show these captions (mainly) in labels. There is no hWnd.
    The Chinese fellow told me that they show up OK in his Chinese computer using Tahoma font (not here).
    I know how to copy and paste Unicode text from/to the Clipboard, that's not the problem, but thanks anyway.
    I don't need to store them as properties, but I'm currently using these captions in a bas module.

    I'll post a possible solution that I'm considering in a new post.

  4. #4

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    I'm cosidering to convert the string of the languages that require Unicode to byte arrays:

    Code:
        Dim iAuxArray() As Variant
        Dim iByteArray() As Byte
        Dim c As Long
    
            Case bsLang_CHINESE_SIMPLIFIED
                Select Case TextID
                    Case bsPPGUI_General_CloseButton_Caption
                        iAuxArray = Array(38, 0, 67, 0, 32, 0, 115, 81, 237, 149) ' "&C 关闭"
                    Case bsPPGUI_General_OKButton_Caption
                        iAuxArray = Array(38, 0, 79, 0, 32, 0, 110, 120, 154, 91) ' "&O 确定"
                    Case bsPPGUI_General_CancelButton_Caption
                        iAuxArray = Array(38, 0, 67, 0, 32, 0, 214, 83, 136, 109) ' "&C 取消"
                End Select
                ReDim iByteArray(UBound(iAuxArray))
                For c = 0 To UBound(iAuxArray)
                    iByteArray(c) = iAuxArray(c)
                Next c
                GetLocalizedString = iByteArray
    Then, at the end, copy this array element by element to a byte array, and then to String.
    I would do that only for the languages that require Unicode. I currently only have Chinese.

    I already made the conversion program to get the Unicode text from the Clipboard and to generate the code lines "iAuxArray = Array(..." automatically.
    Last edited by Eduardo-; Dec 3rd, 2019 at 06:56 PM.

  5. #5
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    286

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Eduardo- View Post
    The question is: how to store Unicode captions properly within the VB6 program?
    Typically, you would use a resource file for this. See The Automated Teller Machine Sample Application for an example (you can install it by selecting the VB Product Samples from the MSDN disc). Also, check out DEXWERX's CodeBank thread for another way of loading string resources.

    One problem with this approach however, is that the VB6 Resource Editor addin—like pretty much everything else in the IDE—is ANSI only. You can work around this limitation by writing your own Unicode-encoded (UTF-16 LE) resource script and compiling it yourself using the VB6-provided RC.EXE.

    Quote Originally Posted by Eduardo- View Post
    And I have a second question, related:
    As you are probably well-aware by now, VB6's intrinsic controls are ANSI only. If you want to display Unicode characters, you must use a Unicode control. That said, you may be able to use a hack that dilettante has demonstrated here.
    Last edited by Victor Bravo VI; Dec 3rd, 2019 at 08:07 PM.

  6. #6
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    18,544

    Re: How to properly store and display Unicode captions

    Are you only interested in basic captions, like open, close, save ?
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by LaVolpe View Post
    Are you only interested in basic captions, like open, close, save ?
    No, I have like 100, 150 captions (I didn't count). That was just a sample of how I'm handling the issue now.

    Edit: I'm taking the standard captions (and some other not too standard) from here.

    For the other ones, I'm using Google translator, and comparing with other translators: Reverso and Yandex.

    I perform the translations from Engish and from Spanish and study what seems too be best.

    Currently I'm translating to Italian, the first language I've choosen to do.

    What idea did you have in mind LaVolpe?
    Last edited by Eduardo-; Dec 3rd, 2019 at 10:53 PM.

  8. #8

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Victor Bravo VI View Post
    Typically, you would use a resource file for this. See The Automated Teller Machine Sample Application for an example (you can install it by selecting the VB Product Samples from the MSDN disc). Also, check out DEXWERX's CodeBank thread for another way of loading string resources.

    One problem with this approach however, is that the VB6 Resource Editor addin—like pretty much everything else in the IDE—is ANSI only. You can work around this limitation by writing your own Unicode-encoded (UTF-16 LE) resource script and compiling it yourself using the VB6-provided RC.EXE.
    I prefer not to use resource files. They are too cumbersome to update.

    Quote Originally Posted by Victor Bravo VI View Post
    As you are probably well-aware by now, VB6's intrinsic controls are ANSI only. If you want to display Unicode characters, you must use a Unicode control. That said, you may be able to use a hack that dilettante has demonstrated here.
    I'm aware that VB6 handles Unicode fine internally. I know that there are problems with the controls but I don't know exactly the problems because I don't understand the nature of those problems (that's why I'm asking).
    I don't need to store the captions in the labels property (as persistant), I'll supply them on the fly by code.
    I don't need to enter text, like in TextBoxes, they are only labels.
    If they have a font that support the glyphs...

    One Chinese progammer told me that with the font Tahoma the Chinese captions worked well, why should I doubt that?
    Here they don't work at all, but I don't need to display them in my machine, but in machines that are prepared to handle Chinese.
    That's why I'm asking, because I don't know what to expect.

    I'll read dilettante's post. Thank you.
    Last edited by Eduardo-; Dec 3rd, 2019 at 10:33 PM.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Eduardo- View Post
    I'll read dilettante's post. Thank you.
    Well, it worked.

    I just needed to keep the captions converted as I had before, for example:

    Code:
    GetLocalizedString = "&C 关闭" ' (it is "&C 关闭")
    And to set the Charset property of the labels' font to &H86&

    Code:
    Const GB2312_CHARSET = &H86& ' this is for Chinese Simplified
    It seems to be "the old way" to handle internationalization.

    But I envisage problems...

    I'll check to use Unicode capable labels. I guess I won't need to convert to ANSI and to choose the right Charset for each language in the Font property if I use Unicode capable labels, right?
    Last edited by Eduardo-; Dec 4th, 2019 at 12:00 AM.

  10. #10
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    286

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Eduardo- View Post
    I know that there are problems with the controls but I don't know exactly the problems because I don't understand the nature of those problems (that's why I'm asking).
    The problem is due to Unicode to ANSI conversion. Unicode characters with code points above 255 can't be converted properly because the ANSI character set only has room for 256 characters. Characters that can't be converted are usually rendered as a question mark (?).

    Quote Originally Posted by Eduardo- View Post
    I guess I won't need to convert to ANSI and to choose the right Charset for each language in the Font property if I use Unicode capable labels, right?
    Yes, that's right. Krool's Common Controls Replacement project includes a LabelW control, so you may want to consider using that.

    Quote Originally Posted by Eduardo- View Post
    I prefer not to use resource files. They are too cumbersome to update.
    Well, in my opinion, the benefits of using resource files outweighs the disadvantages. For instance, Windows will load only the strings that your program needs instead of all of them at the same time. I haven't timed this, but I believe loading string resources should be faster than the approach you've taken because there's no data type conversion involved; the strings are stored as UTF-16 and copied directly to VB6 Strings. Also, there will be less code in the module where you're loading the strings, so there's less chance of exceeding VB6's code size limitations. Finally, storing the strings in the resource section makes it easier for the end user to modify, add or remove the strings, should the need ever arise.

    I don't expect the reasons I listed above will convince you to abandon your strategy, but I will post a demo of the string resource based approach nevertheless. Perhaps someone else might find it useful.


    Name:  Multilingual Demo.png
Views: 44
Size:  3.9 KB

    Code:
    Option Explicit
    
    Private Const IDS_CLOSE                  As Integer = 101
    Private Const IDS_OK                     As Integer = 102
    Private Const IDS_CANCEL                 As Integer = 103
    
    Private Const LANG_CHINESE_SIMPLIFIED    As Integer = &H4
    Private Const LANG_ENGLISH               As Integer = &H9
    Private Const LANG_ITALIAN               As Integer = &H10
    Private Const LANG_NEUTRAL               As Integer = &H0
    Private Const LANG_SPANISH               As Integer = &HA
    Private Const LANG_SYSTEM_DEFAULT        As Integer = &H2
    
    Private Const SUBLANG_CHINESE_SIMPLIFIED As Integer = &H2
    Private Const SUBLANG_ENGLISH_US         As Integer = &H1
    Private Const SUBLANG_ITALIAN            As Integer = &H1
    Private Const SUBLANG_SPANISH            As Integer = &H1
    Private Const SUBLANG_SYS_DEFAULT        As Integer = &H2
    
    Private Const SORT_CHINESE_BIG5          As Integer = &H0
    Private Const SORT_DEFAULT               As Integer = &H0
    
    Private Const LOCALE_SYSTEM_DEFAULT      As Long = &H800
    
    Private Const BS_MULTILINE               As Long = &H2000
    
    Private Const WS_EX_NOPARENTNOTIFY       As Long = &H4
    Private Const WS_CHILD                   As Long = &H40000000
    Private Const WS_CLIPSIBLINGS            As Long = &H4000000
    Private Const WS_TABSTOP                 As Long = &H10000
    Private Const WS_VISIBLE                 As Long = &H10000000
    
    Private Const WM_SETFONT                 As Long = &H30
    
    Private Const WC_BUTTON                  As String = "Button"
    
    Private Declare Function CreateWindowExW Lib "user32.dll" (ByVal dwExStyle As Long, ByVal lpClassName As Long, ByVal lpWindowName As Long, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, Optional ByVal lpParam As Long) As Long
    Private Declare Function DestroyWindow Lib "user32.dll" (ByVal hWnd As Long) As Long
    Private Declare Function GetMem2 Lib "msvbvm60.dll" (ByRef Src As Any, ByRef Dst As Any) As Long
    Private Declare Function GetThreadLocale Lib "kernel32.dll" () As Long
    Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
    Private Declare Function FindResourceEx Lib "kernel32.dll" Alias "FindResourceExW" (ByVal hModule As Long, ByVal lpType As Long, ByVal lpName As Long, ByVal wLanguage As Integer) As Long
    Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryW" (ByVal lpLibFileName As Long) As Long
    Private Declare Function LoadResource Lib "kernel32.dll" (ByVal hInstance As Long, ByVal hResInfo As Long) As Long
    Private Declare Function LockResource Lib "kernel32.dll" (ByVal hResData As Long) As Long
    Private Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function SetThreadLocale Lib "kernel32.dll" (ByVal Locale As Long) As Long
    Private Declare Function SetWindowTextW Lib "user32.dll" (ByVal hWnd As Long, ByVal lpString As Long) As Long
    Private Declare Function SysReAllocStringLen Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long, Optional ByVal Length As Long) As Long
    Private Declare Sub InitCommonControls Lib "comctl32.dll" ()
    
    Private m_hWndClose            As Long
    Private m_hWndOK               As Long
    Private m_hWndCancel           As Long
    Private m_LastLanguageMenu     As VB.Menu
    Private m_LastThreadLocaleMenu As VB.Menu
    
    Private Sub Form_Initialize()
        InitCommonControls
    End Sub
    
    Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
        If KeyCode = vbKeyEscape Then Unload Me
    End Sub
    
    Private Sub Form_Load()
        Const BUTTON_STYLES = BS_MULTILINE Or WS_CHILD Or WS_CLIPSIBLINGS Or WS_TABSTOP Or WS_VISIBLE
        Dim hWnd As Long, hInstance As Long, oFont As stdole.IFont
    
        Set m_LastLanguageMenu = mnuLanguageSystemDefault
        Set m_LastThreadLocaleMenu = mnuThreadLocaleSystemDefault
    
        hWnd = Me.hWnd
        hInstance = App.hInstance
    
        m_hWndClose = CreateWindowExW(WS_EX_NOPARENTNOTIFY, StrPtr(WC_BUTTON), 0&, BUTTON_STYLES, 23&, 85&, 81&, 36&, hWnd, IDS_CLOSE, hInstance)
        m_hWndOK = CreateWindowExW(WS_EX_NOPARENTNOTIFY, StrPtr(WC_BUTTON), 0&, BUTTON_STYLES, 114&, 85&, 81&, 36&, hWnd, IDS_OK, hInstance)
        m_hWndCancel = CreateWindowExW(WS_EX_NOPARENTNOTIFY, StrPtr(WC_BUTTON), 0&, BUTTON_STYLES, 205&, 85&, 81&, 36&, hWnd, IDS_CANCEL, hInstance)
    
        Set oFont = Font
    
        SendMessageW m_hWndClose, WM_SETFONT, oFont.hFont, 0&
        SendMessageW m_hWndOK, WM_SETFONT, oFont.hFont, 0&
        SendMessageW m_hWndCancel, WM_SETFONT, oFont.hFont, 0&
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        Dim RV As Long
    
        RV = DestroyWindow(m_hWndClose):    Debug.Assert RV
        RV = DestroyWindow(m_hWndOK):       Debug.Assert RV
        RV = DestroyWindow(m_hWndCancel):   Debug.Assert RV
    End Sub
    
    Private Sub mnuLanguageClick(ByVal PrimaryLang As Integer, ByVal SubLang As Integer, ByRef Menu As VB.Menu)
        Dim hInstance As Long, LangID As Integer
    
        If App.LogMode = 0& Then MsgBox "FindStringResourceEx works only when compiled!", vbExclamation
        hInstance = App.hInstance
        LangID = MAKELANGID(PrimaryLang, SubLang)
    
        SetWindowTextW m_hWndClose, StrPtr(FindStringResourceEx(hInstance, IDS_CLOSE, LangID))
        SetWindowTextW m_hWndOK, StrPtr(FindStringResourceEx(hInstance, IDS_OK, LangID))
        SetWindowTextW m_hWndCancel, StrPtr(FindStringResourceEx(hInstance, IDS_CANCEL, LangID))
    
        m_LastLanguageMenu.Checked = False
        Menu.Checked = True
        Set m_LastLanguageMenu = Menu
    End Sub
    
    Private Sub mnuLanguageChineseSimplified_Click()
        mnuLanguageClick LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED, mnuLanguageChineseSimplified
    End Sub
    
    Private Sub mnuLanguageEnglishUS_Click()
        mnuLanguageClick LANG_ENGLISH, SUBLANG_ENGLISH_US, mnuLanguageEnglishUS
    End Sub
    
    Private Sub mnuLanguageItalian_Click()
        mnuLanguageClick LANG_ITALIAN, SUBLANG_ITALIAN, mnuLanguageItalian
    End Sub
    
    Private Sub mnuLanguageSpanish_Click()
        mnuLanguageClick LANG_SPANISH, SUBLANG_SPANISH, mnuLanguageSpanish
    End Sub
    
    Private Sub mnuLanguageSystemDefault_Click()
        mnuLanguageClick LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT, mnuLanguageSystemDefault '<-- This doesn't work because FindStringResourceEx can't find this string resource
    End Sub
    
    Private Sub mnuThreadLocaleClick(ByVal PrimaryLang As Integer, ByVal SubLang As Integer, ByVal SortID As Integer, ByRef Menu As VB.Menu)
        Dim RV As Long
    
        RV = SetThreadLocale(MAKELCID(MAKELANGID(PrimaryLang, SubLang), SortID)): Debug.Assert RV
    
        SetWindowTextW m_hWndClose, StrPtr(LoadResString(IDS_CLOSE))    'It seems that VB6 caches the return value of LoadResString
        SetWindowTextW m_hWndOK, StrPtr(LoadResString(IDS_OK))          'SetThreadLocale doesn't seem to have any effect on it
        SetWindowTextW m_hWndCancel, StrPtr(LoadResString(IDS_CANCEL))
    
        m_LastThreadLocaleMenu.Checked = False
        Menu.Checked = True
        Set m_LastThreadLocaleMenu = Menu
    End Sub
    
    Private Sub mnuThreadLocaleChineseSimplified_Click()
        mnuThreadLocaleClick LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED, SORT_CHINESE_BIG5, mnuThreadLocaleChineseSimplified
    End Sub
    
    Private Sub mnuThreadLocaleEnglishUS_Click()
        mnuThreadLocaleClick LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT, mnuThreadLocaleEnglishUS
    End Sub
    
    Private Sub mnuThreadLocaleItalian_Click()
        mnuThreadLocaleClick LANG_ITALIAN, SUBLANG_ITALIAN, SORT_DEFAULT, mnuThreadLocaleItalian
    End Sub
    
    Private Sub mnuThreadLocaleSpanish_Click()
        mnuThreadLocaleClick LANG_SPANISH, SUBLANG_SPANISH, SORT_DEFAULT, mnuThreadLocaleSpanish
    End Sub
    
    Private Sub mnuThreadLocaleSystemDefault_Click()
       '#define LANG_SYSTEM_DEFAULT    (MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT))
       '#define LOCALE_SYSTEM_DEFAULT  (MAKELCID(LANG_SYSTEM_DEFAULT, SORT_DEFAULT))
        mnuThreadLocaleClick LANG_NEUTRAL, SUBLANG_SYS_DEFAULT, SORT_DEFAULT, mnuThreadLocaleSystemDefault
    End Sub
    
    Private Function PtrAdd(ByVal Address As Long, ByVal Offset As Long) As Long
       'unsigned pointer arithmetic, moves overflow by toggling the sign bit
       'required when using /LARGEADDRESSAWARE on 64bit windows
        Const SIGN_BIT As Long = &H80000000
        PtrAdd = (Address Xor SIGN_BIT) + Offset Xor SIGN_BIT
    End Function
    
    Private Property Get DUInt(ByVal Address As Long) As Long
       'Compensate for VB's lack of unsigned types
       'Copies a 16bit Unsigned Integer from a pointer into a Long
        GetMem2 ByVal Address, DUInt
    End Property
    
    'http://www.vbforums.com/showthread.php?828867-VB6-Code-Snippet-Load-Language-Specific-resource-String-FindResourceEx&p=5045975&viewfull=1#post5045975
    'https://blogs.msdn.microsoft.com/oldnewthing/20040130-00/?p=40813/
    Private Function FindStringResourceEx(ByVal hInstance As Long, ByVal uId As Long, ByVal LangID As Integer) As String
        Const STRINGS_PER_BUCKET = 16&, RT_STRING = 6&, WCHAR_SIZE = 2&
        Dim hResource As Long, hGlobal As Long, Ptr As Long, i As Long
    
        hResource = FindResourceEx(hInstance, RT_STRING, uId \ STRINGS_PER_BUCKET + 1, LangID)
    
        If hResource Then
            hGlobal = LoadResource(hInstance, hResource)
    
            If hGlobal Then
                Ptr = LockResource(hGlobal)
    
                If Ptr Then
                    For i = 1& To uId And (STRINGS_PER_BUCKET - 1&)
                        Ptr = PtrAdd(Ptr, (1& + DUInt(Ptr)) * WCHAR_SIZE)
                    Next
    
                    SysReAllocStringLen VarPtr(FindStringResourceEx), PtrAdd(Ptr, 2&), DUInt(Ptr)
                End If
            End If
        End If
    End Function
    
    Private Function MAKELANGID(ByVal PrimaryLang As Integer, ByVal SubLang As Integer) As Integer
        MAKELANGID = SubLang * &H400 Or PrimaryLang And &H3FF
    End Function
    
    Private Function MAKELCID(ByVal LangID As Integer, ByVal SortID As Integer) As Long
        MAKELCID = SortID * &H10000 Or LangID And &HFFFF&
    End Function
    Code:
    #define IDS_CLOSE                  101
    #define IDS_OK                     102
    #define IDS_CANCEL                 103
    
    #define LANG_CHINESE_SIMPLIFIED    0x04
    #define LANG_ENGLISH               0x09
    #define LANG_ITALIAN               0x10
    #define LANG_SPANISH               0x0A /*
    #define LANG_SYSTEM_DEFAULT        0x02 */
    
    #define SUBLANG_CHINESE_SIMPLIFIED 0x02
    #define SUBLANG_ENGLISH_US         0x01
    #define SUBLANG_ITALIAN            0x01
    #define SUBLANG_SPANISH            0x01 /*
    #define SUBLANG_SYS_DEFAULT        0x02 */
    
    STRINGTABLE
    LANGUAGE LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED
    {
        IDS_CLOSE,  "&C 关闭"
        IDS_OK,     "&O 确定"
        IDS_CANCEL, "&C 取消"
    }
    
    STRINGTABLE
    LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
    {
        IDS_CLOSE,  "&Close"
        IDS_OK,     "&OK"
        IDS_CANCEL, "&Cancel"
    }
    
    STRINGTABLE
    LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN
    {
        IDS_CLOSE,  "&Chiudi"
        IDS_OK,     "&OK"
        IDS_CANCEL, "&Annulla"
    }
    
    STRINGTABLE
    LANGUAGE LANG_SPANISH, SUBLANG_SPANISH
    {
        IDS_CLOSE,  "&Cerrar"
        IDS_OK,     "&Aceptar"
        IDS_CANCEL, "&Cancelar"
    }
    
    /*
    STRINGTABLE
    LANGUAGE LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT
    {
        IDS_CLOSE,  "&Close"
        IDS_OK,     "&OK"
        IDS_CANCEL, "&Cancel"
    }
    */
    
    #define MANIFEST_RESOURCE_ID  1
    #define RT_MANIFEST          24
    
    MANIFEST_RESOURCE_ID RT_MANIFEST
    {"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>\
    \r\n<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">\
    \r\n    <dependency>\
    \r\n        <dependentAssembly>\
    \r\n            <assemblyIdentity\
    \r\n                language=""*""\
    \r\n                name=""Microsoft.Windows.Common-Controls""\
    \r\n                processorArchitecture=""x86""\
    \r\n                publicKeyToken=""6595b64144ccf1df""\
    \r\n                type=""win32""\
    \r\n                version=""6.0.0.0"" />\
    \r\n        </dependentAssembly>\
    \r\n    </dependency>\
    \r\n    <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3"">\
    \r\n        <security>\
    \r\n            <requestedPrivileges>\
    \r\n                <requestedExecutionLevel\
    \r\n                    level=""asInvoker""\
    \r\n                    uiAccess=""false"" />\
    \r\n            </requestedPrivileges>\
    \r\n        </security>\
    \r\n    </trustInfo>\
    \r\n    <compatibility xmlns=""urn:schemas-microsoft-com:compatibility.v1"">\
    \r\n        <application>\
    \r\n            <supportedOS Id=""{E2011457-1546-43C5-A5FE-008DEEE3D3F0}""/><!-- Windows Vista -->\
    \r\n            <supportedOS Id=""{35138B9A-5D96-4FBD-8E2D-A2440225F93A}""/><!-- Windows 7 -->\
    \r\n            <supportedOS Id=""{4A2F28E3-53B9-4441-BA9C-D69D4A4A6E38}""/><!-- Windows 8 -->\
    \r\n            <supportedOS Id=""{1F676C76-80E1-4239-95BB-83D0F6D0DA78}""/><!-- Windows 8.1 -->\
    \r\n            <supportedOS Id=""{8E0F7A12-BFB3-4FE8-B9A5-48FD50A15A9A}""/><!-- Windows 10 -->\
    \r\n        </application>\
    \r\n    </compatibility>\
    \r\n</assembly>\r\n"}
    Attached Files Attached Files

  11. #11

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2017
    Posts
    1,931

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Victor Bravo VI View Post
    I don't expect the reasons I listed above will convince you to abandon your strategy, but I will post a demo of the string resource based approach nevertheless. Perhaps someone else might find it useful.
    You are right Victor, but thanks anyway for sharing the code. Surely someone will use it.

    I think the resource approach is something of the past, something that MS used when the programs were shipped different versions for different countries. The idea was not having to modify the exes and to use DLLs for different localizations.
    The programs once developed were like set in stone. Now they are continuously evolving.

    If I don't like a caption, I want to go and change it in a few seconds, not to have to edit a text file, find the RC compiler, remember how to use it, and a few steps more.

    The advantages were also important at that time IMO, when RAM was too limited.
    Now some programs, like the web browsers, load like 100 MB, 200 MB or 300 MB just to open a blank window.

    How much could take 150 captions in let's say 10 languages?

    I may be wrong, but I have to be convinced.
    Last edited by Eduardo-; Dec 4th, 2019 at 05:17 AM.

  12. #12
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    18,544

    Re: How to properly store and display Unicode captions

    Quote Originally Posted by Eduardo- View Post
    No, I have like 100, 150 captions (I didn't count). That was just a sample of how I'm handling the issue now...
    What idea did you have in mind LaVolpe?
    In one of my projects, I just needed simple captions common to dialogs. So I used LoadString APIs to extract them from standard dlls: user32, shell32, etc. That way I would get the caption relative to the user's regional settings without having to have separate lookup tables/resource entries

    Edited: I'd think using external resource-only DLLs would be beneficial. Each DLL could be its own language. When needed, your app would determine the user's LocaleID and load the strings from the appropriate DLL. This has a few extra benefits:

    1) Only the language you need is loaded/searched, not every language

    2) You don't need to recompile & redistribute the exe when a string needs to be updated or a new language added -- just distribute the updated/new resource-only DLL

    3) Scalable. Even if you are not supporting say Turkish at the moment, you can later. Just distribute the new dll with (setup or instructions) on how to create the appropriate target folder and drop the dll there.

    When your app starts, it checks for the current language resource-only DLL you need and if doesn't exists, defaults to whatever (English likely?)
    Last edited by LaVolpe; Dec 4th, 2019 at 09:33 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  13. #13
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    3,620

    Re: How to properly store and display Unicode captions

    I know, not what you asked, but I do use simple UTF8 text files for each language.
    Easy to add a new language without recompiling whatever.
    Easy to update an existing file.
    And if you are worried that the files are tampered with then store them for example in an encrypted ZIP file.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width