PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Поддержка многоязычнности в программе


Поддержка многоязычнности в программе

Сообщений 1 страница 14 из 14

1

Мой текущий пример поддержки многоязычности в программе.
Покритикуйте, может есть что улучшить.
Концепция следующая:
1. Так как мы русские, ну или можно сказать русскоязычные, то я стремлюсь, чтобы русский язык присутствовал в проге без заморочек. Открыл и всё на русском сразу. То есть проверка русского языка в ОС по числовому коду 1049.
2. Если открывает с не русской ОС, то язык остаётся англ. по умолчанию.
3. Если человек не русский и не англоязычный, то ему придётся открыть Lang.txt и заменить строки своим языком. Обычно я в комплекте кладу 2 файла Lang.txt на русском и на английском языке.
4. Не так давно решил добавить флаг ForceLang (принудительный язык). Суть его в союзных республиках или даже других странах могут находится русскоязычные с нерусскоязычной Windows, и запрос языка системы оставит по умолчанию английский язык, вот здесь и пригодится ForceLang=2, чтобы без языковых файлов включить второй язык, то есть русский. Жаль только что этот трюк не такой очевидный окажется для многих, не все читают Readme и справки и в большинстве воспользуются внешним файлом. По крайней мере кто спросит, есть что ответить.
5. Любые другие программисты могут вписать свой второй язык прямо в код и использовать такой же метод в отношении себя, то есть из коробки со своим языком.
6. Флаг ForceLang + 4 будет создавать Lang.txt. ForceLang=5 (1+4) или ForceLang=6 (2+4) будут создавать для первого и второго языка, при этом флаг сбрасывается в ноль ForceLang=0, чтобы использовать Lang.txt при последующих запусках.

Код:
;- TOP

EnableExplicit

; Определяет язык ОС
Define isUserLang
Define ForceLang
Define ini$, w, h, isINI

CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
    Global *Lang
    If OpenLibrary(0, "kernel32.dll")
    	*Lang = GetFunction(0, "GetUserDefaultUILanguage")
    	If *Lang And CallFunctionFast(*Lang) = 1049 ; ru. Здесь вы используете код своего языка
        isUserLang = 1
    	EndIf
    	CloseLibrary(0)
    EndIf
	CompilerCase #PB_OS_Linux
    If ExamineEnvironmentVariables()
    	While NextEnvironmentVariable()
        If Left(EnvironmentVariableName(), 4) = "LANG" And Left(EnvironmentVariableValue(), 2) = "ru"
        	; LANG=ru_RU.UTF-8
        	; LANGUAGE=ru
        	isUserLang = 1
        	Break
        EndIf
    	Wend
    EndIf
CompilerEndSelect



#ArrSizeStrLang = 2
Global Dim Lng.s(#ArrSizeStrLang)
;- Language En
Lng(0) = "text0"
Lng(1) = "text1"
Lng(2) = "text2"

Global PathConfig$ = GetPathPart(ProgramFilename())

; Если конфиг рядом с исполняемым файлом не существует, то назначаем конфиги в папки настроек
If FileSize(PathConfig$ + "MyProgramName.ini") = -1
	CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
    	PathConfig$ = GetHomeDirectory() + "AppData\Roaming\MyProgramName\"
    CompilerCase #PB_OS_Linux
    	PathConfig$ = GetHomeDirectory() + ".config/MyProgramName/"
    	;     	If FileSize(PathConfig$) = -1 And FileSize("/usr/share/MyCompanyName/MyProgramName/MyProgramName.ini") > 0
    	;         CopyFile("/usr/share/MyCompanyName/MyProgramName/MyProgramName.ini", PathConfig$)
    	;     	EndIf
    	;     CompilerCase #PB_OS_MacOS
    	;     	PathConfig$ = GetHomeDirectory() + "Library/Application Support/MyProgramName/"
	CompilerEndSelect
EndIf
ini$ = PathConfig$ + "MyProgramName.ini"

;- ini
If FileSize(ini$) > -1 And OpenPreferences(ini$)
	PreferenceGroup("Set")
	w = ReadPreferenceInteger("width", w) ; настроки размера окна, как пример блока взятия всех настроек программы из ini
	h = ReadPreferenceInteger("height", h)
	ForceLang = ReadPreferenceInteger("forcelang", ForceLang) ; принудительно выставить язык
	isINI = 1              ; чтобы больше не использовать FileSize(), хотя он в OpenPreferences() наверняка есть.
	ClosePreferences()
EndIf


; Принудительный язык
; 0 - автоматически
; 1 - принудительно первый
; 2 - принудительно второй
; +4 генерировать языковой файл

If ForceLang & 1
	isUserLang = 0
ElseIf ForceLang & 2
	isUserLang = 1
EndIf

;- Language Ru
If isUserLang
	Lng(0) = "текст0"
	Lng(1) = "текст1"
	Lng(2) = "текст2"
EndIf



; Функция построчного взятия текста с внешнего файла
Procedure SetLangTxt(PathLang$)
	Protected file_id, Format, i, tmp$

	file_id = ReadFile(#PB_Any, PathLang$)
	If file_id ; Если удалось открыть дескриптор файла, то
    Format = ReadStringFormat(file_id) ; перемещаем указатель после метки BOM
    i = 0
    While Eof(file_id) = 0        ; Цикл, пока не будет достигнут конец файла. (Eof = 'Конец файла')
    	tmp$ = ReadString(file_id, Format) ; читаем строку
                    	   ; If Left(tmp$, 1) = ";"
                    	   ; Continue
                    	   ; EndIf
                    	   ;     	tmp$ = ReplaceString(tmp$ , #CR$ , "") ; коррекция если в Windows
    	tmp$ = RTrim(tmp$ , #CR$)     ; коррекция если в Windows
    	If Asc(tmp$) And Asc(tmp$) <> ';'
        If i > #ArrSizeStrLang ; массив Lng() уже задан, но если строк больше нужного, то не разрешаем лишнее
        	Break
        EndIf
        ;         Lng(i) = UnescapeString(tmp$) ; позволяет в строке иметь экранированные метасимволы, \n \t и т.д.
        Lng(i) = ReplaceString(tmp$, "\n", #LF$) ; В ini-файле проблема только с переносами, поэтому заменяем только \n
        i + 1
    	Else
        Continue
    	EndIf
    Wend
    CloseFile(file_id)
	EndIf
EndProcedure

Procedure CreateLang()
	Protected file_id, i
	file_id = CreateFile(#PB_Any, PathConfig$ + "Lang.txt", #PB_UTF8)
	If file_id ; Если удалось открыть дескриптор файла, то
    WriteStringFormat(file_id, #PB_UTF8)
    For i = 0 To #ArrSizeStrLang
    	CompilerIf #PB_Compiler_OS = #PB_OS_Windows
        tmp$ = ReplaceString(Lng(i), #CRLF$, "\n")
    	CompilerElse
        tmp$ = ReplaceString(Lng(i), #LF$, "\n")
    	CompilerEndIf
    	WriteStringN(file_id, tmp$)
    Next
	EndIf
EndProcedure


; Если языковой файл существует, то использует его
If ForceLang = 0 And FileSize(PathConfig$ + "Lang.txt") > 100
	SetLangTxt(PathConfig$ + "Lang.txt")
ElseIf ForceLang & 4
	CreateLang()
	If OpenPreferences(ini$)
    PreferenceGroup("Set")
    WritePreferenceInteger("forcelang", 0) ; сбросить флаг после создания файла
    ClosePreferences()
	EndIf
EndIf

;-┌──GUI──┐
If OpenWindow(0, 0, 0, 220, 110, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ButtonGadget(1, 10, 10, 111, 27, Lng(0))
	ButtonGadget(2, 10, 40, 111, 27, Lng(1))
	ButtonGadget(3, 10, 70, 111, 27, Lng(2))

;-┌──Loop──┐
	Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Отредактировано AZJIO (03.07.2024 21:22:02)

0

2

AZJIO написал(а):
Код:
    Global *Lang
    If OpenLibrary(0, "kernel32.dll")
    	*Lang = GetFunction(0, "GetUserDefaultUILanguage")
    	If *Lang And CallFunctionFast(*Lang) = 1049 ; ru
        UserIntLang = 1
    	EndIf
    	CloseLibrary(0)
    EndIf

Можно заменить на

Код:
Import "Kernel32.lib"
  GetUserDefaultUILanguage()
EndImport

If GetUserDefaultUILanguage() = 1049 ; ru
  UserIntLang = 1
EndIf

Результат тот же, но код чуть по проще.

0

3

Была такая идея - использовать .rc c LANGUAGE LANG_ENGLISH,SUBLANG_NEUTRAL, но вышло слишком громоздко.

0

4

Пётр
Помню с импортом сделал и прога перестала запускаться на WinXP, проблема была что если функции нет в низшей версии винды то прога просто падает (из-за отсутствия функции) вместо того, чтобы игнорировать отсутствие указателя на WinAPI-функцию. Но в данном случае думаю GetUserDefaultUILanguage() есть и в WinXP, так что можно и так сделать.

egons
Многие делают dll файлы с ресурсами.
либо формат языкового файла:
Hello=Привет
мне не нравится что повторять англ строку и переведённую, а чтение сводится к поиску нужной строки по англ тексту (порядок не важен). А в моём варианте важна последовательность строк, но экономно читает. Сам языковой файл переделывать не трудно, просто копирую текст с массивом и делаю замену "Lng\(\d+\) = " заменить на ничего и завершающую кавычку заменить на ничего, и файл готов с правильным порядком строк.

Отредактировано AZJIO (24.06.2024 18:54:30)

0

5

AZJIO написал(а):

Многие делают dll файлы с ресурсами.
либо формат языкового файла:

А как собирать .dll? С помощью ассемблера?

0

6

egons написал(а):

А как собирать .dll? С помощью ассемблера?

PB может же компилировать DLL.
Ресурсы добавлять в исполняемые файлы тоже может.

0

7

egons
Я видел только этот пример

0

8

В общем,  выглядит это так

Код:
  If OpenWindow(0, 0, 0, 230, 90, "Event handling example...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    
    ;#define MAKELANGID(p, s) ((((WORD) (s)) << 10) | (WORD) (p))   
    
  hrsrc=FindResourceEx_(0,#RT_MENU,600,#LANG_RUSSIAN|(#SUBLANG_NEUTRAL<<10))
	hg=LoadResource_(0,hrsrc)
	hmnu=LoadMenuIndirect_(hg)
	SetMenu_(WindowID(0),hmnu)
   Repeat
     Event = WaitWindowEvent()
   Until Event = #PB_Event_CloseWindow
 EndIf

resource script

Код:
#define LANG_RUSSIAN                     0x19 //ntdefs.h
#define SUBLANG_NEUTRAL                  0x00    // language neutral
#define LANG_ENGLISH                     0x09

600 MENU
MOVEABLE PURE LOADONCALL DISCARDABLE
LANGUAGE LANG_RUSSIAN,SUBLANG_NEUTRAL
BEGIN
    POPUP "Файл"
    BEGIN
        MENUITEM "Открыть...\tCtrl+O",1001
        MENUITEM "Сохранить рисунок...\tCtrl+S",1003
        MENUITEM SEPARATOR
        MENUITEM "Выход",1004
    END
END
600 MENU
LANGUAGE LANG_ENGLISH,SUBLANG_NEUTRAL
BEGIN
    POPUP "File"
    BEGIN
        MENUITEM "Open...\tCtrl+O",1001
        MENUITEM "Save as picture...\tCtrl+S",1003
        MENUITEM SEPARATOR
        MENUITEM "Exit",1004
    END
END

0

9

Сунулся в строки на разных языках

Код:
#define IDS_HELLO    1000
#define IDS_GOODBYE  2000

#define LANG_RUSSIAN 25
#define LANG_ENGLISH 9
#define SUBLANG_DEFAULT 1
#define SUBLANG_ENGLISH_US                          0x01    // English (USA)
#define SUBLANG_ENGLISH_UK                          0x02    // English (UK)

LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
STRINGTABLE
{
    IDS_HELLO,   "Hello"

}

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
STRINGTABLE
{
    IDS_GOODBYE,   "\x410Hello"
}

Не работает код

Код:
Procedure.s Error() 
  wError = GetLastError_() 
  If wError 
    *ErrorBuffer = AllocateMemory(1024) 
    FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, wError, 0, *ErrorBuffer, 1024, 0) 
    message$=Str(wError)+":"+PeekS(*ErrorBuffer) 
    FreeMemory(*ErrorBuffer) 
  EndIf 
  ;MessageRequester("Error", message$) 
  ProcedureReturn message$
EndProcedure 

hrsrc=FindResourceEx_(0,#RT_STRING,1000,#LANG_ENGLISH|(#SUBLANG_ENGLISH_UK<<10))

If hrsrc
Else
  Debug "no resource "+Error()
EndIf

На форумах предлагают какой-то изврат и поиском ресурсов.

0

10

egons
не хочется пробовать с ресурсами, потому что пишу проги кроссплатформенные.

Так я писал, когда только-только начинал

Код:
If hrsrc
Else
  Debug "no resource "+Error()
EndIf

Сейчас так пишу:

Код:
If Not hrsrc

0

11

AZJIO написал(а):

Так я писал, когда только-только начинал

Предполагалось, что если найден ресурс, то надо выполнить определенные действия.
Ресурс не нашелся, пропала шальная идея.

0

12

прокатило

Код:
hrsrc=FindResourceEx_(0,#RT_STRING,(1000/16)+1,#LANG_ENGLISH|(#SUBLANG_ENGLISH_UK<<10))

отсюда

0

13

Код:
Procedure.s Error() 
  wError = GetLastError_() 
  If wError 
    *ErrorBuffer = AllocateMemory(1024) 
    FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, wError, 0, *ErrorBuffer, 1024, 0) 
    message$=Str(wError)+":"+PeekS(*ErrorBuffer) 
    FreeMemory(*ErrorBuffer) 
  EndIf 
  ProcedureReturn message$
EndProcedure 

Procedure.s getstring(resid,lngid)
;https://earlier132.rssing.com/chan-5264073/all_p28.html#c5264073a548
  r$=""
  hrsrc=FindResourceEx_(0,#RT_STRING,(resid/16)+1,lngid)
  
  If hrsrc
    hg=LoadResource_(0,hrsrc)
    If hg
      lpr=LockResource_(hg)
      If lpr
        lpsz=lpr
        For i=1 To  resid&15
          lpsz=lpsz+2+2*PeekU(lpsz)
        Next i
        r$=PeekS(lpsz+2,PeekU(lpsz),#PB_Unicode)
;        UnlockResource_(lpr)
      Else
        Debug "lock resource "+Error()
      EndIf
      FreeResource_(hg)
    Else
      Debug "load resource "+Error()
    EndIf
  Else
    Debug "find resource "+Error()
  EndIf
  ProcedureReturn r$
EndProcedure

Debug getstring(1000,#LANG_ENGLISH|(#SUBLANG_ENGLISH_UK<<10))
Debug getstring(1001,#LANG_ENGLISH|(#SUBLANG_ENGLISH_UK<<10))
Debug getstring(1004,#LANG_ENGLISH|(#SUBLANG_ENGLISH_UK<<10))
Debug getstring(1002,#LANG_RUSSIAN|(#SUBLANG_DEFAULT<<10))

ресурс

Код:
#define LANG_RUSSIAN 25
#define LANG_ENGLISH 9
#define SUBLANG_DEFAULT 1
#define SUBLANG_ENGLISH_US                          0x01
#define SUBLANG_ENGLISH_UK                          0x02

LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
STRINGTABLE
{
    1000,   "Hello"
    1002, "just string"
	1001, "very long String 2 hello, world!"
	1004, "String 3"

}

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
STRINGTABLE
{
    1002,   "\x410\x431\x440\x430\x43A\x430\x434\x430\x431\x440\x430"
}

0

14

Я обновил свой код в первом посте.
Добавил флаг 4 чтобы создать файл Lang.txt
Сменил приоритет, теперь Lang.txt не имеет приоритет над ForceLang=1 или 2, то есть только при ForceLang=0 будет использоваться Lang.txt, если он существует.
ForceLang=5 (1+4) или ForceLang=6 (2+4) соответственно будут сохранять в  Lang.txt английский или русский язык и сбрасывать флаг в ForceLang=0, чтобы применить этот Lang.txt. Возможно следует его сразу открыть, ведь он для того и создаётся чтобы изменить, так как встроенные языки не устраивают.

0


Вы здесь » PureBasic - форум » PureBasic для Windows » Поддержка многоязычнности в программе