* Show how to translate resource strings

This commit is contained in:
michael 2019-07-15 12:58:03 +00:00
parent 0b21ea3b26
commit a99f117940
13 changed files with 1150 additions and 0 deletions

13
demo/translate/dutch.json Normal file
View File

@ -0,0 +1,13 @@
{
"program" : {
"URLTitle":"Vertaling met resourcestrings - URL API"
},
"mystrings": {
"Button":"Vertaal deze pagina",
"Header":"Vertaling met resourcestrings",
"Paragraph":"Deze tekst wordt vertaald.",
"TranslateDirect":"De directe API wordt gebruikt voor dit voorbeeld.",
"TranslateJSON":"Een JSON object wordt gebruikt voor dit voorbeeld.",
"TranslateURL":"Een URL wordt gebruikt voor dit voorbeeld."
}
}

View File

@ -0,0 +1,18 @@
unit mystrings;
{$mode objfpc}
interface
Resourcestring
Header = 'Translation using resource strings';
Paragraph = 'This text will be translated.';
TranslateDirect = 'The direct API is used for this example.';
TranslateJSON = 'A JSON object is used for this example.';
TranslateURL = 'A URL is used for this example.';
Button = 'Translate this page';
implementation
end.

View File

@ -0,0 +1,259 @@
{
This file is part of the Pas2JS run time library.
Copyright (c) 2019 by Michael Van Canneyt
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit rstranslate;
{$mode objfpc}
interface
uses
SysUtils, JS, web;
Type
{ TResourceTranslator }
TLoadFailEvent = Reference to Procedure (Sender : TObject; aCode : Integer; aError : String);
TOnTranslatedEvent = Reference to Procedure (Sender : TObject; aURL : String);
TResourceTranslator = Class
Private
FOnLoadFail: TLoadFailEvent;
FOnURLTranslated: TOnTranslatedEvent;
class var FInstance : TResourceTranslator;
function GetUnitResources(const aUnit: string): TJSOBject;
Protected
procedure ResetTranslation(aUnitResources: TJSObject; AString: String); virtual;
procedure Translate(aUnitResources: TJSObject; AString, aTranslation: String); virtual;
Public
Class Function Instance : TResourceTranslator;
Procedure Translate(Const aUnit,aString,aTranslation : String); overload;
Procedure Translate(Const aTranslations : TJSOBject); overload;
Procedure ResetTranslation(Const aUnit : String; const aString : String = ''); overload;
procedure Translate(const aURL: string; aOnTranslated : TOnTranslatedEvent = Nil); overload;
Property OnLoadFail : TLoadFailEvent Read FOnLoadFail Write FOnLoadFail;
Property OnURLTranslated : TOnTranslatedEvent Read FOnURLTranslated Write FOnURLTranslated;
end;
Function ResourceTranslator : TResourceTranslator;
Procedure Translate(Const aURL : String; aOnTranslated : TOnTranslatedEvent = Nil);
Procedure Translate(Const aUnit,aString,aTranslation : String);
Procedure Translate(Const aTranslations : TJSOBject);
Procedure ResetTranslation(Const aUnit : String; Const aString : String = '');
implementation
Const
SCurrent = 'current';
var
Pas : TJSObject; external name 'pas';
{ TResourceTranslator }
procedure TResourceTranslator.Translate(aUnitResources: TJSObject; AString, aTranslation: String);
Var
res: JSValue;
begin
res:=aUnitResources[aString];
if Assigned(Res) then
TJSOBject(Res)[SCurrent]:=aTranslation;
end;
procedure TResourceTranslator.ResetTranslation(aUnitResources: TJSObject; AString: String);
Var
res: JSValue;
begin
res:=aUnitResources[aString];
if Assigned(Res) then
TJSOBject(Res)[SCurrent]:=undefined;
end;
function TResourceTranslator.GetUnitResources(const aUnit: string): TJSOBject;
var
jsMod,res : JSValue;
begin
Result:=Nil;
jsMod:=Pas[aUnit];
if assigned(jsMod) then
begin
res:=(TJSObject(jsMod)['$resourcestrings']);
if Assigned(res) then
Result:=TJSObject(res);
end;
end;
procedure TResourceTranslator.Translate(const aTranslations: TJSOBject);
var
aUnitName,aStringName : String;
aUnit,Res : TJSObject;
V : JSValue;
begin
for aUnitName in TJSObject.getOwnPropertyNames(aTranslations) do
if IsValidIdent(aUnitName,True) and isObject(aTranslations[aUnitName]) then
begin
aUnit:=TJSObject(aTranslations[aUnitName]);
Res:=GetUnitResources(aUnitName);
if Assigned(Res) then
For aStringName in TJSObject.getOwnPropertyNames(aUnit) do
begin
V:=aUnit[aStringName];
if IsString(V) then
Translate(Res,aStringName,String(V));
end;
end;
end;
procedure TResourceTranslator.Translate(const aUnit, aString, aTranslation: String);
Var
jsmod : TJSObject;
begin
jsMod:=GetUnitResources(aUnit);
if Assigned(jsMod) then
Translate(jsMod, aString,aTranslation);
end;
procedure TResourceTranslator.ResetTranslation(const aUnit: String; const aString: String);
Var
jsmod : TJSObject;
S : String;
begin
jsMod:=GetUnitResources(aUnit);
if Assigned(jsMod) then
if (aString<>'') then
ResetTranslation(jsMod, aString)
else
for S in TJSOBject.getOwnPropertyNames(TJSObject(jsMod)) do
ResetTranslation(jsMod, S);
end;
Type
{ TURLTranslator }
TURLTranslator = Class
Private
FTranslator : TResourceTranslator;
FURL : String;
FXHR : TJSXMLHttpRequest;
FOnTranslated : TOnTranslatedEvent;
procedure DoStateChange;
Public
Constructor Create(aUrl : String; ATranslator : TResourceTranslator;aOnTranslated : TOnTranslatedEvent);
Procedure Translate;
end;
{ TURLTranslator }
procedure TURLTranslator.DoStateChange;
Var
O : TJSObject;
begin
if (FXHR.ReadyState=TJSXMLHttpRequest.DONE) then
if ((FXHR.Status div 100)=2) then
begin
try
O:=TJSJSON.parseObject(FXHR.responseText);
FTranslator.Translate(O);
if Assigned(FOnTranslated) then
FOnTranslated(FTranslator,FURL);
if Assigned(FTranslator.OnURLTranslated) then
FTranslator.OnURLTranslated(FTranslator,FURL);
except
if Assigned(FTranslator.OnLoadFail) then
FTranslator.OnLoadFail(FTranslator,0,'Invalid JSON')
end
end
else
if Assigned(FTranslator.OnLoadFail) then
FTranslator.OnLoadFail(FTranslator,FXHR.Status,FXHR.StatusText);
end;
constructor TURLTranslator.Create(aUrl: String; ATranslator: TResourceTranslator;aOnTranslated : TOnTranslatedEvent);
begin
FURL:=aURL;
FTranslator:=ATranslator;
FOnTranslated:=aOnTranslated;
end;
procedure TURLTranslator.Translate;
begin
FXHR:=TJSXMLHttpRequest.new;
FXHR.open('GET',fURL);
FXHR.onreadystatechange:=@DoStateChange;
FXHR.responseType:='text';
FXHR.send;
end;
procedure TResourceTranslator.Translate(const aURL : string;aOnTranslated : TOnTranslatedEvent = Nil);
begin
With TURLTranslator.Create(aURL,Self,aOnTranslated) do
Translate;
end;
class function TResourceTranslator.Instance: TResourceTranslator;
begin
if FInstance=Nil then
FInstance:=TResourceTranslator.Create;
Result:=FInstance;
end;
{ ---------------------------------------------------------------------
Procedural access
---------------------------------------------------------------------}
Procedure Translate(Const aURL : String; aOnTranslated : TOnTranslatedEvent = Nil);
begin
TResourceTranslator.Instance.Translate(aURL,aOnTranslated);
end;
Procedure Translate(Const aTranslations : TJSOBject);
begin
TResourceTranslator.Instance.Translate(aTranslations);
end;
procedure Translate(const aUnit, aString, aTranslation: String);
begin
TResourceTranslator.Instance.Translate(aUnit,aString,aTranslation);
end;
Procedure ResetTranslation(Const aUnit : String; Const aString : String = '');
begin
TResourceTranslator.Instance.ResetTranslation(aUnit,Astring);
end;
Function ResourceTranslator : TResourceTranslator;
begin
Result:=TResourceTranslator.Instance;
end;
end.

View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Translation using resource strings - Basic API</title>
<script src="translate_basic.js"></script>
</head>
<body>
<h2 id="translate-header"></h2>
<p id="translate-text"></p>
<button id="btn-translate" class="btn btn-primary"></button>
<script>
rtl.run();
</script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<Runnable Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="translate_basic"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="4">
<Item0 Name="MaintainHTML" Value="1"/>
<Item1 Name="PasJSHTMLFile" Value="project1.html"/>
<Item2 Name="PasJSPort" Value="0"/>
<Item3 Name="PasJSWebBrowserProject" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<Units>
<Unit>
<Filename Value="translate_basic.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="translate_basic.html"/>
<IsPartOfProject Value="True"/>
<CustomData Count="1">
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="mystrings.pp"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target FileExt=".js">
<Filename Value="translate_basic"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="js"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<AllowLabel Value="False"/>
<CPPInline Value="False"/>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="browser"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<UseLineInfoUnit Value="False"/>
</Debugging>
</Linking>
<Other>
<CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
<CompilerPath Value="$(pas2js)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,91 @@
program translate_basic;
{$mode objfpc}
uses
JS, Web, mystrings, rstranslate;
Resourcestring
BasicTitle = 'Translation using resource strings - Basic API';
Type
{ TTranslateApp }
TTranslateApp = Class
FHeader : TJSHTMLELement;
FPar : TJSHTMLELement;
FButton : TJSHTMLButtonELement;
FIsDutch : Boolean;
Constructor Create;
Function GetEl(const aName : string) : TJSHTMLELement;
Procedure Bind;
Procedure SetTexts;
private
function DoTranslation(aEvent: TJSMouseEvent): boolean;
procedure TranslateStrings;
end;
{ TTranslateApp }
constructor TTranslateApp.Create;
begin
Bind;
end;
function TTranslateApp.GetEl(const aName: string): TJSHTMLELement;
begin
Result:=TJSHTMLELement(Document.getElementById(aName));
end;
procedure TTranslateApp.Bind;
begin
FHeader:=GetEl('translate-header');
FPar:=GetEl('translate-text');
FButton:=TJSHTMLButtonELement(GetEl('btn-translate'));
FButton.onclick:=@DoTranslation;
end;
procedure TTranslateApp.SetTexts;
begin
FHeader.InnerHTML:=Header;
FButton.InnerHTML:=Button;
FPar.InnerHtml:=Paragraph+' '+TranslateDirect;
document.title:=BasicTitle;
// window. title:=BasicTitle;
end;
procedure TTranslateApp.TranslateStrings;
begin
FIsDutch:=Not FIsDutch;
if FIsDutch then
begin
Translate('program','BasicTitle','Vertaling met resourcestrings - directe API');
Translate('mystrings','Button','Vertaal deze pagina');
Translate('mystrings','Header','Vertaling met resourcestrings');
Translate('mystrings','Paragraph','Deze tekst wordt vertaald.');
Translate('mystrings','TranslateDirect','De directe API wordt gebruikt voor dit voorbeeld.');
Translate('mystrings','TranslateJSON','Een JSON object wordt gebruikt voor dit voorbeeld.');
Translate('mystrings','TranslateURL','Een URL wordt gebruikt voor dit voorbeeld.');
end
else
begin
// Single string of a module
ResetTranslation('program','BasicTitle');
// All strings in a module
ResetTranslation('mystrings');
end;
end;
function TTranslateApp.DoTranslation(aEvent: TJSMouseEvent): boolean;
begin
TranslateStrings;
SetTexts;
Result:=True;
end;
begin
With TTranslateApp.Create do
SetTexts;
end.

View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Translation using resource strings - Object API</title>
<script src="translate_object.js"></script>
</head>
<body>
<h2 id="translate-header"></h2>
<p id="translate-text"></p>
<button id="btn-translate" class="btn btn-primary"></button>
<script>
rtl.run();
</script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<Runnable Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="translate_object"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="4">
<Item0 Name="MaintainHTML" Value="1"/>
<Item1 Name="PasJSHTMLFile" Value="translate_object.html"/>
<Item2 Name="PasJSPort" Value="0"/>
<Item3 Name="PasJSWebBrowserProject" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<Units>
<Unit>
<Filename Value="translate_object.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="translate_object.html"/>
<IsPartOfProject Value="True"/>
<CustomData Count="1">
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="mystrings.pp"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target FileExt=".js">
<Filename Value="translate_object"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="js"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<AllowLabel Value="False"/>
<CPPInline Value="False"/>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="browser"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<UseLineInfoUnit Value="False"/>
</Debugging>
</Linking>
<Other>
<CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
<CompilerPath Value="$(pas2js)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,97 @@
program translate_object;
{$mode objfpc}
uses
JS, Web, mystrings, rstranslate;
Resourcestring
ObjectTitle = 'Translation using resource strings - Object API';
Type
{ TTranslateApp }
TTranslateApp = Class
FDutch : TJSObject;
FHeader : TJSHTMLELement;
FPar : TJSHTMLELement;
FButton : TJSHTMLButtonELement;
FIsDutch : Boolean;
Constructor Create;
Function GetEl(const aName : string) : TJSHTMLELement;
Procedure Bind;
Procedure SetTexts;
private
function DoTranslation(aEvent: TJSMouseEvent): boolean;
procedure TranslateStrings;
end;
{ TTranslateApp }
constructor TTranslateApp.Create;
begin
Bind;
FDutch:=New([
'program',new([
'BasicTitle','Vertaling met resourcestrings - directe API'
]),
'mystrings', new([
'Button','Vertaal deze pagina',
'Header','Vertaling met resourcestrings',
'Paragraph','Deze tekst wordt vertaald.',
'TranslateDirect','De directe API wordt gebruikt voor dit voorbeeld.',
'TranslateJSON','Een JSON object wordt gebruikt voor dit voorbeeld.',
'TranslateURL','Een URL wordt gebruikt voor dit voorbeeld.'
])
]);
end;
function TTranslateApp.GetEl(const aName: string): TJSHTMLELement;
begin
Result:=TJSHTMLELement(Document.getElementById(aName));
end;
procedure TTranslateApp.Bind;
begin
FHeader:=GetEl('translate-header');
FPar:=GetEl('translate-text');
FButton:=TJSHTMLButtonELement(GetEl('btn-translate'));
FButton.onclick:=@DoTranslation;
end;
procedure TTranslateApp.SetTexts;
begin
FHeader.InnerHTML:=Header;
FButton.InnerHTML:=Button;
FPar.InnerHtml:=Paragraph+' '+TranslateJSON;
document.title:=ObjectTitle;
end;
procedure TTranslateApp.TranslateStrings;
begin
FIsDutch:=Not FIsDutch;
if FIsDutch then
Translate(FDutch)
else
begin
// Single string of a module
ResetTranslation('program','BasicTitle');
// All strings in a module
ResetTranslation('mystrings');
end;
end;
function TTranslateApp.DoTranslation(aEvent: TJSMouseEvent): boolean;
begin
TranslateStrings;
SetTexts;
Result:=True;
end;
begin
With TTranslateApp.Create do
SetTexts;
end.

View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Translation using resource strings - URL API</title>
<script src="translate_url.js"></script>
</head>
<body>
<h2 id="translate-header"></h2>
<p id="translate-text"></p>
<button id="btn-translate" class="btn btn-primary"></button>
<script>
rtl.run();
</script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="12"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<Runnable Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<Title Value="translate_url"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<CustomData Count="4">
<Item0 Name="MaintainHTML" Value="1"/>
<Item1 Name="PasJSHTMLFile" Value="translate_url.html"/>
<Item2 Name="PasJSPort" Value="0"/>
<Item3 Name="PasJSWebBrowserProject" Value="1"/>
</CustomData>
<BuildModes>
<Item Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<Units>
<Unit>
<Filename Value="translate_url.lpr"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit>
<Filename Value="translate_url.html"/>
<IsPartOfProject Value="True"/>
<CustomData Count="1">
<Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
</CustomData>
</Unit>
<Unit>
<Filename Value="mystrings.pp"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target FileExt=".js">
<Filename Value="translate_url"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="js"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>
<AllowLabel Value="False"/>
<CPPInline Value="False"/>
<UseAnsiStrings Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<TargetOS Value="browser"/>
</CodeGeneration>
<Linking>
<Debugging>
<GenerateDebugInfo Value="False"/>
<UseLineInfoUnit Value="False"/>
</Debugging>
</Linking>
<Other>
<CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
<CompilerPath Value="$(pas2js)"/>
</Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,89 @@
program translate_url;
{$mode objfpc}
uses
JS, Web, rstranslate, mystrings;
Resourcestring
URLTitle = 'Translation using resource strings - URL API';
Type
{ TTranslateApp }
TTranslateApp = Class
FHeader : TJSHTMLELement;
FPar : TJSHTMLELement;
FButton : TJSHTMLButtonELement;
FIsDutch : Boolean;
Constructor Create;
Function GetEl(const aName : string) : TJSHTMLELement;
Procedure Bind;
Procedure SetTexts;
private
procedure DoneTranslation(Sender: TObject; aURL: String);
function DoTranslation(aEvent: TJSMouseEvent): boolean;
procedure TranslateStrings;
end;
{ TTranslateApp }
constructor TTranslateApp.Create;
begin
Bind;
end;
function TTranslateApp.GetEl(const aName: string): TJSHTMLELement;
begin
Result:=TJSHTMLELement(Document.getElementById(aName));
end;
procedure TTranslateApp.Bind;
begin
FHeader:=GetEl('translate-header');
FPar:=GetEl('translate-text');
FButton:=TJSHTMLButtonELement(GetEl('btn-translate'));
FButton.onclick:=@DoTranslation;
end;
procedure TTranslateApp.SetTexts;
begin
FHeader.InnerHTML:=Header;
FButton.InnerHTML:=Button;
FPar.InnerHtml:=Paragraph+' '+TranslateURL;
document.title:=URLTitle;
end;
procedure TTranslateApp.DoneTranslation(Sender : TObject; aURL : String);
begin
SetTexts;
end;
procedure TTranslateApp.TranslateStrings;
begin
FIsDutch:=Not FIsDutch;
if FIsDutch then
Translate('dutch.json',@DoneTranslation)
else
begin
// Single string of a module
ResetTranslation('program','URLTitle');
// All strings in a module
ResetTranslation('mystrings');
end;
end;
function TTranslateApp.DoTranslation(aEvent: TJSMouseEvent): boolean;
begin
TranslateStrings;
SetTexts;
Result:=True;
end;
begin
With TTranslateApp.Create do
SetTexts;
end.

247
packages/rtl/rstranslate.pp Normal file
View File

@ -0,0 +1,247 @@
unit rstranslate;
{$mode objfpc}
interface
uses
SysUtils, JS, web;
Type
{ TResourceTranslator }
TLoadFailEvent = Reference to Procedure (Sender : TObject; aCode : Integer; aError : String);
TOnTranslatedEvent = Reference to Procedure (Sender : TObject; aURL : String);
TResourceTranslator = Class
Private
FOnLoadFail: TLoadFailEvent;
FOnURLTranslated: TOnTranslatedEvent;
class var FInstance : TResourceTranslator;
function GetUnitResources(const aUnit: string): TJSOBject;
Protected
procedure ResetTranslation(aUnitResources: TJSObject; AString: String); virtual;
procedure Translate(aUnitResources: TJSObject; AString, aTranslation: String); virtual;
Public
Class Function Instance : TResourceTranslator;
Procedure Translate(Const aUnit,aString,aTranslation : String); overload;
Procedure Translate(Const aTranslations : TJSOBject); overload;
Procedure ResetTranslation(Const aUnit : String; const aString : String = ''); overload;
procedure Translate(const aURL: string; aOnTranslated : TOnTranslatedEvent = Nil); overload;
Property OnLoadFail : TLoadFailEvent Read FOnLoadFail Write FOnLoadFail;
Property OnURLTranslated : TOnTranslatedEvent Read FOnURLTranslated Write FOnURLTranslated;
end;
Function ResourceTranslator : TResourceTranslator;
Procedure Translate(Const aURL : String; aOnTranslated : TOnTranslatedEvent = Nil);
Procedure Translate(Const aUnit,aString,aTranslation : String);
Procedure Translate(Const aTranslations : TJSOBject);
Procedure ResetTranslation(Const aUnit : String; Const aString : String = '');
implementation
Const
SCurrent = 'current';
var
Pas : TJSObject; external name 'pas';
{ TResourceTranslator }
procedure TResourceTranslator.Translate(aUnitResources: TJSObject; AString, aTranslation: String);
Var
res: JSValue;
begin
res:=aUnitResources[aString];
if Assigned(Res) then
TJSOBject(Res)[SCurrent]:=aTranslation;
end;
procedure TResourceTranslator.ResetTranslation(aUnitResources: TJSObject; AString: String);
Var
res: JSValue;
begin
res:=aUnitResources[aString];
if Assigned(Res) then
TJSOBject(Res)[SCurrent]:=undefined;
end;
function TResourceTranslator.GetUnitResources(const aUnit: string): TJSOBject;
var
jsMod,res : JSValue;
begin
Result:=Nil;
jsMod:=Pas[aUnit];
if assigned(jsMod) then
begin
res:=(TJSObject(jsMod)['$resourcestrings']);
if Assigned(res) then
Result:=TJSObject(res);
end;
end;
procedure TResourceTranslator.Translate(const aTranslations: TJSOBject);
var
aUnitName,aStringName : String;
aUnit,Res : TJSObject;
V : JSValue;
begin
for aUnitName in TJSObject.getOwnPropertyNames(aTranslations) do
if IsValidIdent(aUnitName,True) and isObject(aTranslations[aUnitName]) then
begin
aUnit:=TJSObject(aTranslations[aUnitName]);
Res:=GetUnitResources(aUnitName);
if Assigned(Res) then
For aStringName in TJSObject.getOwnPropertyNames(aUnit) do
begin
V:=aUnit[aStringName];
if IsString(V) then
Translate(Res,aStringName,String(V));
end;
end;
end;
procedure TResourceTranslator.Translate(const aUnit, aString, aTranslation: String);
Var
jsmod : TJSObject;
begin
jsMod:=GetUnitResources(aUnit);
if Assigned(jsMod) then
Translate(jsMod, aString,aTranslation);
end;
procedure TResourceTranslator.ResetTranslation(const aUnit: String; const aString: String);
Var
jsmod : TJSObject;
S : String;
begin
jsMod:=GetUnitResources(aUnit);
if Assigned(jsMod) then
if (aString<>'') then
ResetTranslation(jsMod, aString)
else
for S in TJSOBject.getOwnPropertyNames(TJSObject(jsMod)) do
ResetTranslation(jsMod, S);
end;
Type
{ TURLTranslator }
TURLTranslator = Class
Private
FTranslator : TResourceTranslator;
FURL : String;
FXHR : TJSXMLHttpRequest;
FOnTranslated : TOnTranslatedEvent;
procedure DoStateChange;
Public
Constructor Create(aUrl : String; ATranslator : TResourceTranslator;aOnTranslated : TOnTranslatedEvent);
Procedure Translate;
end;
{ TURLTranslator }
procedure TURLTranslator.DoStateChange;
Var
O : TJSObject;
begin
if (FXHR.ReadyState=TJSXMLHttpRequest.DONE) then
if ((FXHR.Status div 100)=2) then
begin
try
O:=TJSJSON.parseObject(FXHR.responseText);
FTranslator.Translate(O);
if Assigned(FOnTranslated) then
FOnTranslated(FTranslator,FURL);
if Assigned(FTranslator.OnURLTranslated) then
FTranslator.OnURLTranslated(FTranslator,FURL);
except
if Assigned(FTranslator.OnLoadFail) then
FTranslator.OnLoadFail(FTranslator,0,'Invalid JSON')
end
end
else
if Assigned(FTranslator.OnLoadFail) then
FTranslator.OnLoadFail(FTranslator,FXHR.Status,FXHR.StatusText);
end;
constructor TURLTranslator.Create(aUrl: String; ATranslator: TResourceTranslator;aOnTranslated : TOnTranslatedEvent);
begin
FURL:=aURL;
FTranslator:=ATranslator;
FOnTranslated:=aOnTranslated;
end;
procedure TURLTranslator.Translate;
begin
FXHR:=TJSXMLHttpRequest.new;
FXHR.open('GET',fURL);
FXHR.onreadystatechange:=@DoStateChange;
FXHR.responseType:='text';
FXHR.send;
end;
procedure TResourceTranslator.Translate(const aURL : string;aOnTranslated : TOnTranslatedEvent = Nil);
begin
With TURLTranslator.Create(aURL,Self,aOnTranslated) do
Translate;
end;
class function TResourceTranslator.Instance: TResourceTranslator;
begin
if FInstance=Nil then
FInstance:=TResourceTranslator.Create;
Result:=FInstance;
end;
{ ---------------------------------------------------------------------
Procedural access
---------------------------------------------------------------------}
Procedure Translate(Const aURL : String; aOnTranslated : TOnTranslatedEvent = Nil);
begin
TResourceTranslator.Instance.Translate(aURL,aOnTranslated);
end;
Procedure Translate(Const aTranslations : TJSOBject);
begin
TResourceTranslator.Instance.Translate(aTranslations);
end;
procedure Translate(const aUnit, aString, aTranslation: String);
begin
TResourceTranslator.Instance.Translate(aUnit,aString,aTranslation);
end;
Procedure ResetTranslation(Const aUnit : String; Const aString : String = '');
begin
TResourceTranslator.Instance.ResetTranslation(aUnit,Astring);
end;
Function ResourceTranslator : TResourceTranslator;
begin
Result:=TResourceTranslator.Instance;
end;
end.