mORMot and Open Source friends
Check-in [27aedaa271]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:{951} introducing aForceEnglishIfNoMsgFile optional parameter for SetCurrentLanguage() - also change the default behavior so that any missing .msg file won't change the current locale - fixed issue when translating forms under Win64
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 27aedaa271dde98747011816d14ac3e11a716831
User & Date: ab 2015-02-21 14:34:46
Context
2015-02-21
14:37
{952} small refactoring, mainly about Unicode process - no functional change check-in: 14738272f9 user: ab tags: trunk
14:34
{951} introducing aForceEnglishIfNoMsgFile optional parameter for SetCurrentLanguage() - also change the default behavior so that any missing .msg file won't change the current locale - fixed issue when translating forms under Win64 check-in: 27aedaa271 user: ab tags: trunk
14:33
{950} allow clean Control+C handling under Linux for console test applications check-in: 6d9e7ee5ab user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/mORMoti18n.pas.

168
169
170
171
172
173
174

175
176
177
178
179
180
181
...
365
366
367
368
369
370
371




372
373
374




375
376
377
378
379
380
381
382
...
448
449
450
451
452
453
454


455
456
457
458
459
460
461
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
...
715
716
717
718
719
720
721



722
723
724
725

726




727
728
729
730
731
732
733
734
735
736

737
738
739

740
741



742
743
744
745
746
747
748
749
750
751
752
753

754






755
756
757
758
759
760
761
762
763
764
....
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235

1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246

1247
1248

1249
1250
1251


1252
1253
1254
1255
1256
1257
1258
....
1282
1283
1284
1285
1286
1287
1288

1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
....
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
....
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
....
1511
1512
1513
1514
1515
1516
1517

1518
1519
1520
1521
1522
1523
1524
1525
1526
....
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
....
1883
1884
1885
1886
1887
1888
1889
1890

1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
    - BREAKING CHANGE: changed '�' into '~' character, for better ASCII support
      in text file - ALL EXISTING .MSG FILES SHALL BE MODIFIED IN CONSEQUENCE
      (by an automated search/replace in your favorite text editor)
    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI
    - fixed EXTRACTALLRESOURCES process for multi-platform Delphi versions
    - fixed Win64 compilation and execution issues (Delphi XE2+)
    - fixed Unicode issue in function i18nLanguageToRegistry()


*)

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER

interface

................................................................................
  /// this message will be displayed on the screen when the user change the
  // current language, together with its english version
  SHaveToRestart = 'You have to restart the application to apply these language changes.';

{$else} { only called once in Initialization.LangInit: }

/// resets all translation and locale-specific variables via SetThreadLocale()




procedure SetCurrentLanguage(aLanguage: TLanguages); overload;

/// resets all translation and locale-specific variables via SetThreadLocale()




procedure SetCurrentLanguage(const value: RawUTF8); overload;
{$endif}
{$endif}

/// Return the language text, ready to be displayed (after translation if
// necessary)
// - e.g. LanguageName(lngEnglish)='English'
// - return "string" type, i.e. UnicodeString for Delphi 2009 and up
................................................................................
  // - use current language for comparison
  // - can be used for MBCS strings (with such code pages, it will use windows
  // slow but accurate API)
  i18nCompareText: TCompareFunction = nil;


type


  {{ class to load and handle translation files (fr.msg, de.msg, ja.msg.. e.g.)
   - This standard .msg text file contains all the program resources translated
    into any language.
   - Unicode characters (Chinese or Japanese) can be used.
   - The most important part of this file is the [Messages] section, which
    contain all the text to be displayed in NumericValue=Text pairs.
    The numeric value is a hash (i.e. unique identifier) of the Text.
................................................................................
    // then overridden by a DateTimeFmt= entry in the .msg file content
    DateTimeFmt: string;
    /// string used for displaying boolean values
    fBooleanToString: array[boolean] of string;
{$endif}
{$ifndef USEFORMCREATEHOOK}
    /// list of TForm sent to FormTranslate([....])
    AlreadyTranslated: TIntegerDynArray;
{$else}
    /// set a language ID to change the UI into the registry
    // - TComboBox(Sender).Items.Objects[TComboBox(Sender).ItemIndex] is the
    // language ID
    // - TMenuItem(Sender).Tag is the language ID
    procedure LanguageClick(Sender: TObject);
{$endif USEFORMCREATEHOOK}
................................................................................
  {$ifdef WITHUXTHEME}
  UxTheme,
  {$endif}
{$endif}
  Controls, ExtCtrls, Graphics;

var
  // to use FastFindIntegerIndex() in LanguageAbrToIndex():
  LanguageAbrInteger: array[TLanguages] of integer; // copy of LanguageAbr[]

const
  LANG_MACEDONIAN = $2f;
  LANG_DARI = $8c;
  LANG_PASHTO = $63;
  sPriLang: array[TLanguages] of byte =
   (LANG_HEBREW,LANG_GREEK,0,LANG_DARI,0,LANG_CATALAN,0,LANG_CZECH,0,0,0,
................................................................................
  case sPriLang[Language] of
    LANG_CHINESE: result := $0804; // Chinese (PRC) if not $0404
    else
    result := LANG_USER_DEFAULT or sPriLang[Language]; // Process Default Language ($0400)
  end; // leave Sort order to $0 = default
end;




function LCIDToLanguage(LCID: integer): TLanguages;
// compares sPriLang[] values with sysLocale.PriLangID to set current language
// return LanguageUS if this LCID is not known
var b: byte;

begin




  b := LCID and 255;
  case b of
    $1A: // ambigious PriLangID -> get it by full LCID
    case LCID of
      $141a, $201a: result := lngBosnian;
      $041a, $101a: result := lngCroatian;
      else          result := lngSerbian; // by default - don't call UN again
    end; // case SysLocale
  else begin
    for result := low(result) to high(result) do

      if b=sPriLang[result] then
        exit;
    result := lngEnglish;

  end;
  end;



end;


function LanguageAbrToIndex(const value: RawUTF8): TLanguages;
// LanguageAbrToIndex('GR')=1
begin
  if length(value)=2 then
    result := LanguageAbrToIndex(pointer(Value)) else
    result := LANGUAGE_NONE;
end;

function LanguageAbrToIndex(p: pAnsiChar): TLanguages; overload;

begin






  result := TLanguages(IntegerScanIndex(
    @LanguageAbrInteger[low(TLanguages)], ord(high(TLanguages))+1,
    NormToLowerByte[ord(p[0])]+NormToLowerByte[ord(p[1])] shl 8));
end;


const
  // default character set for a specific language (for GUI i18n)
  // list taken from http://www.webheadstart.org/xhtml/encoding
  // see also http://msdn2.microsoft.com/en-us/library/ms776260.aspx
................................................................................
begin
  if aLanguage=LANGUAGE_NONE then
    result := '' else
    result := PTypeInfo(TypeInfo(TLanguages))^.EnumBaseType^.GetCaption(aLanguage);
end;

{$ifndef NOI18N}
procedure SetCurrentLanguage(aLanguage: TLanguages); overload;
{$ifndef USEFORMCREATEHOOK}
var i: integer;
    Already: array of TCustomForm; // to re-translate all forms
{$endif USEFORMCREATEHOOK}
var c: AnsiChar;

begin
  // 1. not already set to this value?
  if CurrentLanguage.Index=aLanguage then
    exit;
// default CurrentLanguage.Index=LANGUAGE_NONE -> force updated english locale if necessary
{$ifdef USEFORMCREATEHOOK}
  if CurrentLanguage.Index<>LANGUAGE_NONE then
    raise Exception.Create('lang unit: language must be set only once');
{$endif USEFORMCREATEHOOK}

  // 2. file must exists if not English

  if aLanguage<>lngEnglish then
    if not FileExists(TLanguageFile.FileName(aLanguage)) then

      if CurrentLanguage.Index=lngEnglish then
        exit else
        aLanguage := lngEnglish; // if .msg not available -> force english



  // 3. reset all Locale settings + AnsiCompare*() functions
  with CurrentLanguage do begin
    Fill(aLanguage); // init all CurrentLanguage fields for this language
{$ifndef LVCL}
    if GetThreadLocale<>LCID then // force locale settings if different
      if SetThreadLocale(LCID) then
................................................................................
      i18nCompareText := Win32CompareText;
    end;
    // AnsiUpper/LowerCase use CharUpper/LowerBuff() = NormToUpper/Lower[] values
  end;

  // 4. create Language object from exe directory if not english
{$ifdef USEFORMCREATEHOOK}

  if aLanguage<>lngEnglish then
    Language := TLanguageFile.Create(aLanguage); // Language created only once
{$else}
  if Language<>nil then begin // save AlreadyTranslated[] forms for translation
    SetLength(Already,length(Language.AlreadyTranslated));
    move(Language.AlreadyTranslated[0],Already[0],length(Language.AlreadyTranslated)*4);
    FreeAndNil(Language);
  end;
  if aLanguage<>lngEnglish then
    Language := TLanguageFile.Create(aLanguage);
  for i := 0 to high(Already) do // translate available forms again
  try
    Language.FormTranslateOne(Already[i]);
  except // ignore any exception -> form.Free -> acces violation e.g.
    on Exception do;
  end;
{$endif USEFORMCREATEHOOK}
................................................................................
  // (we don't have to use critical section here, since call is thread safe)
{$ifndef LVCL}
  LoadResStringTranslate := GetText; // just set translation function
  CacheResCount := 0; // flush LoadResString() cache
{$endif}
end;

procedure SetCurrentLanguage(const value: RawUTF8); overload;
begin
  SetCurrentLanguage(LanguageAbrToIndex(value));
end;

{$ifdef USEFORMCREATEHOOK}

function i18nAddLanguageItems(MsgPath: TFileName; List: TStrings): integer;
var SR: TSearchRec;
    lng, index: TLanguages;
................................................................................
procedure LangInit;
// do redirection + init user default locale (from Win32 or registry)
var i: TLanguages;
    hKernel32: HMODULE;
begin
  // LanguageAbrInteger[]: to use fast IntegerScanIndex() in LanguageAbrToIndex()
  for i := low(i) to high(i) do
    LanguageAbrInteger[i] := PWord(pointer(LanguageAbr[i]))^;
  assert(LanguageAbrToIndex('En')=lngEnglish);
  assert(LanguageAbrToIndex('fR')=lngFrench);
  assert(LanguageAbrToIndex('xx')=LANGUAGE_NONE);
{$ifndef EXTRACTALLRESOURCES}
{$ifdef USEFORMCREATEHOOK}
  // get language from registry, if USEFORMCREATEHOOK
  i := i18nRegistryToLanguage; // from \Software\CompanyName\i18n\paramstr(0)
//i := LanguageAbrToIndex('FR'); // DEBUG: load FR.MSG
  if i<>LANGUAGE_NONE then
    SetCurrentLanguage(i) else
{$endif}
{$endif}
{$ifndef LVCL} // LVCL doesn't have any SysLocale defined
    SetCurrentLanguage(LCIDToLanguage(SysLocale.DefaultLCID));
{$endif}
  // LCID_US = $0409 US English = international settings
  hKernel32 := GetModuleHandle('kernel32');
  if (hKernel32 > 0) then
    isVista := GetProcAddress(hKernel32, 'GetLocaleInfoEx')<>nil;
{$ifdef USEFORMCREATEHOOK}
  if Language<>nil then
................................................................................
    result := '';
end;

{$ifndef USEFORMCREATEHOOK}
procedure TLanguageFile.FormTranslate(Forms: array of TCustomForm);
var f: integer;
begin

  for f := 0 to high(Forms) do begin
    AddInteger(AlreadyTranslated,PtrInt(Forms[f]),true);
    FormTranslateOne(Forms[f]);
  end;
end;
{$endif USEFORMCREATEHOOK}

procedure TLanguageFile.FormTranslateOne(aForm: TComponent);
{$ifndef LVCL}
................................................................................

procedure TLanguageFile.Translate(var English: string);
// case-sensitive (same as standard gettext)
var result: string;
begin
  result := FindMessage(Hash32(
    // resourcestring are expected to be in English, that is WinAnsi encoded
    // before being hashed
    {$ifdef UNICODE}WinAnsiConvert.UnicodeBufferToAnsi(pointer(English),length(English))
    {$else}English{$endif}));
  if result<>'' then
    English := result;
end;

procedure GetText(var Text: string);
// used for System.LoadResStringTranslate case-sensitive (same as standard gettext)
begin
................................................................................
begin
  if Language<>nil then begin
    result := Language.FindMessage(Hash32(English));
    if result<>'' then
      exit;
  end;
  {$ifdef UNICODE}
  result := WinAnsiToUnicodeString(pointer(English),length(English)); {$else}

  result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,pointer(English),length(English));
  {$endif}
end;

function S2U(const Text: string): RawUTF8;
begin
{$ifdef UNICODE}
  result := RawUnicodeToUtf8(PWideChar(pointer(Text)),length(Text));
{$else}
  result := CurrentAnsiConvert.AnsiBufferToRawUTF8(pointer(Text),length(Text));
{$endif}
end;

function U2S(const Text: RawUTF8): string;
begin
{$ifdef UNICODE}
  result := UTF8DecodeToUnicodeString(pointer(Text),length(Text));
{$else}
  result := CurrentAnsiConvert.UTF8BufferToAnsi(pointer(Text),length(Text));
{$endif}
end;

function Iso2S(Iso: TTimeLog): string;
begin
  if Iso=0 then
    result := '' else
  if Iso and (1 shl (6+6+5)-1)=0 then






>







 







>
>
>
>
|


>
>
>
>
|







 







>
>







 







|







 







|
|







 







>
>
>




>

>
>
>
>









|
>
|
<
|
>
|

>
>
>











|
>

>
>
>
>
>
>
|
<
<







 







|


|


>










|
>
|
|
>
|
|
|
>
>







 







>
|
|


|
<


|
|







 







|

|







 







|









|



|







 







>

|







 







|
<
<







 







|
>
|





|
|
|

|




|
|
|

|







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
...
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
...
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757

758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785


786
787
788
789
790
791
792
....
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
....
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327

1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
....
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
....
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
....
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
....
1895
1896
1897
1898
1899
1900
1901
1902


1903
1904
1905
1906
1907
1908
1909
....
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
    - BREAKING CHANGE: changed '�' into '~' character, for better ASCII support
      in text file - ALL EXISTING .MSG FILES SHALL BE MODIFIED IN CONSEQUENCE
      (by an automated search/replace in your favorite text editor)
    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI
    - fixed EXTRACTALLRESOURCES process for multi-platform Delphi versions
    - fixed Win64 compilation and execution issues (Delphi XE2+)
    - fixed Unicode issue in function i18nLanguageToRegistry()
    - added aForceEnglishIfNoMsgFile optional parameter for SetCurrentLanguage()

*)

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER

interface

................................................................................
  /// this message will be displayed on the screen when the user change the
  // current language, together with its english version
  SHaveToRestart = 'You have to restart the application to apply these language changes.';

{$else} { only called once in Initialization.LangInit: }

/// resets all translation and locale-specific variables via SetThreadLocale()
// - by default, if the supplied language does not have a corrrespondig .msg
// local file, it will fallback to lngEnlish for the whole application
// - you may set aForceEnglishIfNoMsgFile=false to change the application
// localization code, even if there is no matching .msg file 
procedure SetCurrentLanguage(aLanguage: TLanguages; aForceEnglishIfNoMsgFile: boolean=true); overload;

/// resets all translation and locale-specific variables via SetThreadLocale()
// - by default, if the supplied language does not have a corrrespondig .msg
// local file, it will fallback to lngEnlish for the whole application
// - you may set aForceEnglishIfNoMsgFile=false to change the application
// localization code, even if there is no matching .msg file 
procedure SetCurrentLanguage(const value: RawUTF8; aForceEnglishIfNoMsgFile: boolean=true); overload;
{$endif}
{$endif}

/// Return the language text, ready to be displayed (after translation if
// necessary)
// - e.g. LanguageName(lngEnglish)='English'
// - return "string" type, i.e. UnicodeString for Delphi 2009 and up
................................................................................
  // - use current language for comparison
  // - can be used for MBCS strings (with such code pages, it will use windows
  // slow but accurate API)
  i18nCompareText: TCompareFunction = nil;


type
  TCustomFormDynArray = array of TCustomForm;

  {{ class to load and handle translation files (fr.msg, de.msg, ja.msg.. e.g.)
   - This standard .msg text file contains all the program resources translated
    into any language.
   - Unicode characters (Chinese or Japanese) can be used.
   - The most important part of this file is the [Messages] section, which
    contain all the text to be displayed in NumericValue=Text pairs.
    The numeric value is a hash (i.e. unique identifier) of the Text.
................................................................................
    // then overridden by a DateTimeFmt= entry in the .msg file content
    DateTimeFmt: string;
    /// string used for displaying boolean values
    fBooleanToString: array[boolean] of string;
{$endif}
{$ifndef USEFORMCREATEHOOK}
    /// list of TForm sent to FormTranslate([....])
    AlreadyTranslated: TCustomFormDynArray;
{$else}
    /// set a language ID to change the UI into the registry
    // - TComboBox(Sender).Items.Objects[TComboBox(Sender).ItemIndex] is the
    // language ID
    // - TMenuItem(Sender).Tag is the language ID
    procedure LanguageClick(Sender: TObject);
{$endif USEFORMCREATEHOOK}
................................................................................
  {$ifdef WITHUXTHEME}
  UxTheme,
  {$endif}
{$endif}
  Controls, ExtCtrls, Graphics;

var
  // to speed up search in LanguageAbrToIndex():
  LanguageAbrWord: array[TLanguages] of word; // copy of LanguageAbr[]

const
  LANG_MACEDONIAN = $2f;
  LANG_DARI = $8c;
  LANG_PASHTO = $63;
  sPriLang: array[TLanguages] of byte =
   (LANG_HEBREW,LANG_GREEK,0,LANG_DARI,0,LANG_CATALAN,0,LANG_CZECH,0,0,0,
................................................................................
  case sPriLang[Language] of
    LANG_CHINESE: result := $0804; // Chinese (PRC) if not $0404
    else
    result := LANG_USER_DEFAULT or sPriLang[Language]; // Process Default Language ($0400)
  end; // leave Sort order to $0 = default
end;

var LastLCID: integer;
    LastLCIDLanguage: TLanguages = LANGUAGE_NONE;

function LCIDToLanguage(LCID: integer): TLanguages;
// compares sPriLang[] values with sysLocale.PriLangID to set current language
// return LanguageUS if this LCID is not known
var b: byte;
    lng: TLanguages;
begin
  if LCID=LastLCID then begin
    result := LastLCIDLanguage;
    exit;
  end;
  b := LCID and 255;
  case b of
    $1A: // ambigious PriLangID -> get it by full LCID
    case LCID of
      $141a, $201a: result := lngBosnian;
      $041a, $101a: result := lngCroatian;
      else          result := lngSerbian; // by default - don't call UN again
    end; // case SysLocale
  else begin
    result := lngEnglish;
    for lng := low(lng) to high(lng) do
      if b=sPriLang[lng] then begin

        result := lng;
        break;
      end;
  end;
  end;
  LastLCID := LCID;
  LastLCIDLanguage := Result;
end;


function LanguageAbrToIndex(const value: RawUTF8): TLanguages;
// LanguageAbrToIndex('GR')=1
begin
  if length(value)=2 then
    result := LanguageAbrToIndex(pointer(Value)) else
    result := LANGUAGE_NONE;
end;

function LanguageAbrToIndex(P: PAnsiChar): TLanguages; overload;
var ndx: integer;
begin
  if P=nil then
    ndx := -1 else
    ndx := WordScanIndex(@LanguageAbrWord,Length(LanguageAbrWord),
      NormToLowerByte[ord(P[0])]+NormToLowerByte[ord(P[1])] shl 8);
  if ndx<0 then
    result := LANGUAGE_NONE else
    result := TLanguages(ndx);


end;


const
  // default character set for a specific language (for GUI i18n)
  // list taken from http://www.webheadstart.org/xhtml/encoding
  // see also http://msdn2.microsoft.com/en-us/library/ms776260.aspx
................................................................................
begin
  if aLanguage=LANGUAGE_NONE then
    result := '' else
    result := PTypeInfo(TypeInfo(TLanguages))^.EnumBaseType^.GetCaption(aLanguage);
end;

{$ifndef NOI18N}
procedure SetCurrentLanguage(aLanguage: TLanguages; aForceEnglishIfNoMsgFile: boolean); overload;
{$ifndef USEFORMCREATEHOOK}
var i: integer;
    Already: TCustomFormDynArray; // to re-translate all forms
{$endif USEFORMCREATEHOOK}
var c: AnsiChar;
    LanguageForLanguageFile: TLanguages;
begin
  // 1. not already set to this value?
  if CurrentLanguage.Index=aLanguage then
    exit;
// default CurrentLanguage.Index=LANGUAGE_NONE -> force updated english locale if necessary
{$ifdef USEFORMCREATEHOOK}
  if CurrentLanguage.Index<>LANGUAGE_NONE then
    raise Exception.Create('lang unit: language must be set only once');
{$endif USEFORMCREATEHOOK}

  // 2. handle missing .msg file
  LanguageForLanguageFile := aLanguage;
  if LanguageForLanguageFile<>lngEnglish then
    if not FileExists(TLanguageFile.FileName(LanguageForLanguageFile)) then begin
      if aForceEnglishIfNoMsgFile then
        if CurrentLanguage.Index=lngEnglish then
          exit else
          aLanguage := lngEnglish;
      LanguageForLanguageFile := lngEnglish; // no .msg -> no translation 
    end;

  // 3. reset all Locale settings + AnsiCompare*() functions
  with CurrentLanguage do begin
    Fill(aLanguage); // init all CurrentLanguage fields for this language
{$ifndef LVCL}
    if GetThreadLocale<>LCID then // force locale settings if different
      if SetThreadLocale(LCID) then
................................................................................
      i18nCompareText := Win32CompareText;
    end;
    // AnsiUpper/LowerCase use CharUpper/LowerBuff() = NormToUpper/Lower[] values
  end;

  // 4. create Language object from exe directory if not english
{$ifdef USEFORMCREATEHOOK}
  FreeAndNil(Language);
  if LanguageForLanguageFile<>lngEnglish then
    Language := TLanguageFile.Create(LanguageForLanguageFile);
{$else}
  if Language<>nil then begin // save AlreadyTranslated[] forms for translation
    Already := Language.AlreadyTranslated;

    FreeAndNil(Language);
  end;
  if LanguageForLanguageFile<>lngEnglish then
    Language := TLanguageFile.Create(LanguageForLanguageFile);
  for i := 0 to high(Already) do // translate available forms again
  try
    Language.FormTranslateOne(Already[i]);
  except // ignore any exception -> form.Free -> acces violation e.g.
    on Exception do;
  end;
{$endif USEFORMCREATEHOOK}
................................................................................
  // (we don't have to use critical section here, since call is thread safe)
{$ifndef LVCL}
  LoadResStringTranslate := GetText; // just set translation function
  CacheResCount := 0; // flush LoadResString() cache
{$endif}
end;

procedure SetCurrentLanguage(const value: RawUTF8; aForceEnglishIfNoMsgFile: boolean); overload;
begin
  SetCurrentLanguage(LanguageAbrToIndex(value),aForceEnglishIfNoMsgFile);
end;

{$ifdef USEFORMCREATEHOOK}

function i18nAddLanguageItems(MsgPath: TFileName; List: TStrings): integer;
var SR: TSearchRec;
    lng, index: TLanguages;
................................................................................
procedure LangInit;
// do redirection + init user default locale (from Win32 or registry)
var i: TLanguages;
    hKernel32: HMODULE;
begin
  // LanguageAbrInteger[]: to use fast IntegerScanIndex() in LanguageAbrToIndex()
  for i := low(i) to high(i) do
    LanguageAbrWord[i] := PWord(pointer(LanguageAbr[i]))^;
  assert(LanguageAbrToIndex('En')=lngEnglish);
  assert(LanguageAbrToIndex('fR')=lngFrench);
  assert(LanguageAbrToIndex('xx')=LANGUAGE_NONE);
{$ifndef EXTRACTALLRESOURCES}
{$ifdef USEFORMCREATEHOOK}
  // get language from registry, if USEFORMCREATEHOOK
  i := i18nRegistryToLanguage; // from \Software\CompanyName\i18n\paramstr(0)
//i := LanguageAbrToIndex('FR'); // DEBUG: load FR.MSG
  if i<>LANGUAGE_NONE then
    SetCurrentLanguage(i,false) else
{$endif}
{$endif}
{$ifndef LVCL} // LVCL doesn't have any SysLocale defined
    SetCurrentLanguage(LCIDToLanguage(SysLocale.DefaultLCID),false);
{$endif}
  // LCID_US = $0409 US English = international settings
  hKernel32 := GetModuleHandle('kernel32');
  if (hKernel32 > 0) then
    isVista := GetProcAddress(hKernel32, 'GetLocaleInfoEx')<>nil;
{$ifdef USEFORMCREATEHOOK}
  if Language<>nil then
................................................................................
    result := '';
end;

{$ifndef USEFORMCREATEHOOK}
procedure TLanguageFile.FormTranslate(Forms: array of TCustomForm);
var f: integer;
begin
  SetLength(AlreadyTranslated,length(Forms));
  for f := 0 to high(Forms) do begin
    AlreadyTranslated[f] := Forms[f];
    FormTranslateOne(Forms[f]);
  end;
end;
{$endif USEFORMCREATEHOOK}

procedure TLanguageFile.FormTranslateOne(aForm: TComponent);
{$ifndef LVCL}
................................................................................

procedure TLanguageFile.Translate(var English: string);
// case-sensitive (same as standard gettext)
var result: string;
begin
  result := FindMessage(Hash32(
    // resourcestring are expected to be in English, that is WinAnsi encoded
    {$ifdef UNICODE}StringToWinAnsi{$endif}(English)));


  if result<>'' then
    English := result;
end;

procedure GetText(var Text: string);
// used for System.LoadResStringTranslate case-sensitive (same as standard gettext)
begin
................................................................................
begin
  if Language<>nil then begin
    result := Language.FindMessage(Hash32(English));
    if result<>'' then
      exit;
  end;
  {$ifdef UNICODE}
  result := WinAnsiToUnicodeString(English);
  {$else}
  result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,English);
  {$endif}
end;

function S2U(const Text: string): RawUTF8;
begin
  {$ifdef UNICODE}
  RawUnicodeToUtf8(PWideChar(pointer(Text)),length(Text),result);
  {$else}
  result := CurrentAnsiConvert.AnsiBufferToRawUTF8(pointer(Text),length(Text));
  {$endif}
end;

function U2S(const Text: RawUTF8): string;
begin
  {$ifdef UNICODE}
  UTF8DecodeToUnicodeString(pointer(Text),length(Text),result);
  {$else}
  result := CurrentAnsiConvert.UTF8BufferToAnsi(pointer(Text),length(Text));
  {$endif}
end;

function Iso2S(Iso: TTimeLog): string;
begin
  if Iso=0 then
    result := '' else
  if Iso and (1 shl (6+6+5)-1)=0 then

Changes to SynopseCommit.inc.

1
'1.18.950'
|
1
'1.18.951'