Difference between revisions of "automatic translation of forms/es"

From Lazarus wiki
(Vamos a ver si así salen bien los códigos)
m (Text replace - "delphi>" to "syntaxhighlight>")
Line 5: Line 5:
 
1) Deben agregar a su proyecto este archivo llamado Traductor.pas (o como quieran llamarlo)
 
1) Deben agregar a su proyecto este archivo llamado Traductor.pas (o como quieran llamarlo)
  
<delphi>
+
<syntaxhighlight>
 
unit Traductor;
 
unit Traductor;
  
Line 288: Line 288:
  
 
end.
 
end.
</delphi>
+
</syntaxhighlight>
  
 
2) Luego deben activar la traducción automática del Lazarus en Opciones de Proyecto -> i18n. Donde deben indicar que la carpeta donde se almacenarán las traducciones se llame "Languages". Claro que deben crear esa carpeta luego.
 
2) Luego deben activar la traducción automática del Lazarus en Opciones de Proyecto -> i18n. Donde deben indicar que la carpeta donde se almacenarán las traducciones se llame "Languages". Claro que deben crear esa carpeta luego.
Line 312: Line 312:
 
2) Crear dentro de la unidad una sección "resourcestrings" y colocar dentro los mensajes a utilizar. Ej.:
 
2) Crear dentro de la unidad una sección "resourcestrings" y colocar dentro los mensajes a utilizar. Ej.:
  
<delphi>
+
<syntaxhighlight>
 
unit Constantes;
 
unit Constantes;
  
Line 325: Line 325:
  
 
end.
 
end.
</delphi>
+
</syntaxhighlight>
  
 
3) Para utilizar estas constantes, sólo deben utilizarse como si fueran constantes normales. Ej.:
 
3) Para utilizar estas constantes, sólo deben utilizarse como si fueran constantes normales. Ej.:
  
<delphi>
+
<syntaxhighlight>
 
   MessageDlg(SSaludo, mtInformation, [mbOk], 0);
 
   MessageDlg(SSaludo, mtInformation, [mbOk], 0);
 
   MessageDlg(SDespedida, mtInformation, [mbOk], 0);
 
   MessageDlg(SDespedida, mtInformation, [mbOk], 0);
</delphi>
+
</syntaxhighlight>
  
 
4) Como penultimo paso deben indicar que desean traducir esta unidad, para lo cual deben agregar un código de inicialización a la unidad creada:
 
4) Como penultimo paso deben indicar que desean traducir esta unidad, para lo cual deben agregar un código de inicialización a la unidad creada:
  
<delphi>
+
<syntaxhighlight>
 
unit Constantes;
 
unit Constantes;
  
Line 359: Line 359:
  
 
end.
 
end.
</delphi>
+
</syntaxhighlight>
  
 
4) Como último paso deben agregar esta unidad "Constantes" a su archivo lpr, tomando el recaudo de colocarlo luego de la unidad "Traductor", porque de no ser así "TranslateResourceStrings" dará un "Access Violation".
 
4) Como último paso deben agregar esta unidad "Constantes" a su archivo lpr, tomando el recaudo de colocarlo luego de la unidad "Traductor", porque de no ser así "TranslateResourceStrings" dará un "Access Violation".
  
<delphi>
+
<syntaxhighlight>
 
   ...
 
   ...
 
   Traductor, // Tiene que estar antes que Constantes
 
   Traductor, // Tiene que estar antes que Constantes
 
   Constantes,
 
   Constantes,
 
   ...
 
   ...
</delphi>
+
</syntaxhighlight>
  
 
5) Ya está, disfruten.....
 
5) Ya está, disfruten.....

Revision as of 16:04, 24 March 2012

COMO UTILIZAR LA TRADUCCION AUTOMATICA SIN TANTO DOLOR!

Sigan estos sencillos pasos:

1) Deben agregar a su proyecto este archivo llamado Traductor.pas (o como quieran llamarlo)

unit Traductor;

{ 
  Original of V.I.Volchenko and Lazarus Developers Team
  Modified by Oscar Flor (Delphino) from DefaultTranstator.pas to use po files.
  Enhancements will be very appreciated
}

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Translations, Controls, typinfo, FileUtil,
  LCLProc;

type

  { TDefaultTranslator }

  TDefaultTranslator = class(TAbstractTranslator)
  private
    FPOFile: TPOFile;
  public
    constructor Create(POFileName: string);
    destructor Destroy; override;

    procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent;
      PropInfo: PPropInfo; var Content: string); override;
    procedure TranslateUnitResourceStrings(UnitName : string);

    property POFile : TPOFile read FPOFile;
  end;

implementation

function FindLocaleFileName: string;
var
  Lang, T: string;
  i: integer;

  function GetLocaleFileName(const LangID: string): string;
  var
    LangShortID: string;
  begin
    if LangID <> EmptyStr then
    begin

      //ParamStrUTF8(0) is said not to work properly in linux, but I've tested it
      Result := ExtractFilePath(ParamStrUTF8(0)) + LangID +
        DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'languages' + DirectorySeparator + LangID +
        DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator
        + LangID + DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator
        + LangID + DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      {$IFDEF UNIX}
      //In unix-like systems we can try to search for global locale
      Result := '/usr/share/locale/' + LangID + '/LC_MESSAGES/' +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;
      {$ENDIF}

      //Let us search for reducted files
      LangShortID := copy(LangID, 1, 2);

      //At first, check all was checked
      Result      := ExtractFilePath(ParamStrUTF8(0)) + LangShortID +
        DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'languages' + DirectorySeparator +
        LangShortID + DirectorySeparator + ChangeFileExt(
        ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator
        + LangShortID + DirectorySeparator + ChangeFileExt(
        ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator
        + LangID + DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;

      //Full language in file name - this will be default for the project
      //We need more carefull handling, as it MAY result in incorrect filename
      try
        Result := ExtractFilePath(ParamStrUTF8(0)) + ChangeFileExt(
          ExtractFileName(ParamStrUTF8(0)), '.' + LangID) + '.po';

        if FileExistsUTF8(Result) then
          Exit;

        //Common location (like in Lazarus)
        Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator +
          ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangID) + '.po';

        if FileExistsUTF8(Result) then
          Exit;

        Result := ExtractFilePath(ParamStrUTF8(0)) + 'languages' +
          DirectorySeparator + ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangID) + '.po';

        if FileExistsUTF8(Result) then
          Exit;
      except
        Result := EmptyStr;//Or do something else (useless)
      end; // try

      {$IFDEF UNIX}
      Result := '/usr/share/locale/' + LangShortID + '/LC_MESSAGES/' +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.po');

      if FileExistsUTF8(Result) then
        Exit;
      {$ENDIF}

      Result := ExtractFilePath(ParamStrUTF8(0)) + ChangeFileExt(
        ExtractFileName(ParamStrUTF8(0)), '.' + LangShortID) + '.po';

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'locale' + DirectorySeparator +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangShortID) + '.po';

      if FileExistsUTF8(Result) then
        Exit;

      Result := ExtractFilePath(ParamStrUTF8(0)) + 'languages' + DirectorySeparator +
        ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangShortID) + '.po';

      if FileExistsUTF8(Result) then
        Exit;
    end;

    Result := EmptyStr;
  end;

begin
  Result := EmptyStr;
  T:=EmptyStr;

  //Win32 user may decide to override locale with LANG variable.
  Lang   := GetEnvironmentVariableUTF8('LANG');

  if Lang = '' then
  begin
    for i := 1 to Paramcount - 1 do
      if (ParamStrUTF8(i) = '--LANG') or (ParamStrUTF8(i) = '-l') or
        (ParamStrUTF8(i) = '--lang') then
        Lang := ParamStrUTF8(i + 1);
  end; // if

  if Lang = EmptyStr then
    LCLGetLanguageIDs(Lang, T);

  Result := GetLocaleFileName(Lang);
  if Result <> EmptyStr then
    Exit;

  Result := ChangeFileExt(ParamStrUTF8(0), '.po');
  if FileExistsUTF8(Result) then
    Exit;

  Result := EmptyStr;
end;

{ TDefaultTranslator }

constructor TDefaultTranslator.Create(POFileName: string);
begin
  inherited Create;
  FPOFile := TPOFile.Create(UTF8ToSys(POFileName));
end;

destructor TDefaultTranslator.Destroy;
begin
  FPOFile.Free;

  //If someone will use this class incorrectly, it can be destroyed
  //before Reader destroying. It is a very bad thing, but in THIS situation
  //in this case is impossible. May be, in future we can overcome this difficulty
  inherited Destroy;
end;

procedure TDefaultTranslator.TranslateStringProperty(Sender: TObject;
  const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
  s: string;
  Section: string;
  Component: TComponent;
begin
  if not Assigned(FPOFile) then
    Exit;

  if not Assigned(PropInfo) then
    Exit;

  // do not translate at design time
  if Instance is TComponent then
    if csDesigning in (Instance as TComponent).ComponentState then
      Exit;

  if (UpperCase(PropInfo^.PropType^.Name) <> 'TTRANSLATESTRING') then
    Exit;

  s := FPOFile.Translate('', Content);

  if s = '' then
  begin
    Component := Instance as TComponent;

    if Component.Owner <> nil then
      Section := UpperCase(Component.Owner.Name) + '.';

    Section := 'T' + Section + UpperCase(Component.Name) + '.' +
      UpperCase(PropInfo^.Name);

    s := FPOFile.Translate('', Section + #4 + Content);
  end;

  if s <> EmptyStr then
    Content := s;
end;

procedure TDefaultTranslator.TranslateUnitResourceStrings(UnitName: string);
begin
  Translations.TranslateUnitResourceStrings(UnitName, FPOFile);
end;

var
  lcfn: string;

initialization

  //It is safe to place code here as no form is initialized before unit
  //initialization made
  //We are to search for all
  try
    lcfn := FindLocaleFileName;
  except
    lcfn := EmptyStr;
  end; // try

  LRSTranslator := TDefaultTranslator.Create(lcfn);

finalization

  LRSTranslator.Free;

end.

2) Luego deben activar la traducción automática del Lazarus en Opciones de Proyecto -> i18n. Donde deben indicar que la carpeta donde se almacenarán las traducciones se llame "Languages". Claro que deben crear esa carpeta luego.

3) Agregen "Traductor.pas" a la lista de "uses" de su archivo ".lpr".

4) Abran todos los formularios que tengan y háganle algún cambio pequeño como para que deban ser guardados nuevamente.

5) Compilen su aplicación.

6) Esto debió haber creado un archivo en la carpeta "Languages" con el mismo nombre que su aplicación pero con extensión ".po".

7) Utilizando alguna herramienta de traducción de archivos .po (yo utilicé POEdit), deben generar los otros archivos de idiomas que deseen soportar. Ya saben "save as...". Los archivos deben tener la siguiente forma "<nombre de aplicación>.<idioma>.po". Ej.: MiAplicacion.es.po (para español), MiAplicacion.en.po (para inglés). Claro que deben ingresar en cada archivo y traducir los textos que se encuentren en los ítems llamados "msgstr" tomando como referencia lo escrito en "msgid".

8) En este lugar la próxima vez que ejecuten su aplicación ya deberían aparecer los textos traducidos, tomando como base el idioma de su sistema operativo. Si quiere probar los otros idiomas debe escribir en la línea de comando "--LANG <idioma>" indicando el idioma que desee probar. Ej.: MiAplicación.exe --LANG en (Para inglés).

PARA TRADUCIR CONSTANTES DE CARACTERES QUE NO SE ENCUENTRAN EN LOS FORMULARIOS

Esta técnica explicada sólo funciona para traducir los formularios, los mensajes escritos en los programas deben ser traducidos de otra forma, la cual sería:

1) Crear una nueva unidad que contendrá las constantes de caracteres para mensajes. Ej.: Constantes.

2) Crear dentro de la unidad una sección "resourcestrings" y colocar dentro los mensajes a utilizar. Ej.:

unit Constantes;

interface

resourcestrings

  SSaludo = 'Hola que hacés';
  SDespedida = 'Un placer haber hablado contigo, saludos a la flía.';

implementation

end.

3) Para utilizar estas constantes, sólo deben utilizarse como si fueran constantes normales. Ej.:

  MessageDlg(SSaludo, mtInformation, [mbOk], 0);
  MessageDlg(SDespedida, mtInformation, [mbOk], 0);

4) Como penultimo paso deben indicar que desean traducir esta unidad, para lo cual deben agregar un código de inicialización a la unidad creada:

unit Constantes;

interface

resourcestrings

  SSaludo = 'Hola que hacés';
  SDespedida = 'Un placer haber hablado contigo, saludos a la flía.';

implementation

{ Traduce los resourcestrings }
procedure TranslateResourceStrings;
begin
  TDefaultTranslator(LRSTranslator).TranslateUnitResourceStrings('Constantes');
end;

initialization

  TranslateResourceStrings

end.

4) Como último paso deben agregar esta unidad "Constantes" a su archivo lpr, tomando el recaudo de colocarlo luego de la unidad "Traductor", porque de no ser así "TranslateResourceStrings" dará un "Access Violation".

  ...
  Traductor, // Tiene que estar antes que Constantes
  Constantes,
  ...

5) Ya está, disfruten.....