Sign in Join
Wiki copytemporary

spelling/es: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Utilizar hunspell con Lazarus
22
33En esta página vermos la forma de utilizar la librería hunspell con Lazarus. Veremos un esbozo, un modelo funcional pero incompleto. Es casi seguro que se necesitará hacer algunos cambios para propósitos específicos, pero con suerte esta página será un buen comienzo.
44
55En primer lugar, en el foro hay varias referencias a algún código que funcionará con la biblioteca hunspell. La unidad hunspell.pas se basa en gran medida en estos bloques de código. La mayoría no tiene información de licencia y se supone que son "de conocimiento común" y, por lo tanto, libres de cualquier restricción. He agregado un bit que soluciona el problema de encontrar la biblioteca y los archivos del diccionario. Y estableció una interfaz razonable.
66
77Además, el usuario rvk del foro ha creado una DLL de Windows de 64 bits, ya que parecía haber pocas alternativas para los usuarios de Windows
88
99## Sobre Hunspell
1010
1111Hunspell es un proyecto activo de código abierto, distribuido bajo la Licencia Pública de Mozilla. La biblioteca hunspell se utiliza en productos como Libra Office, Open Office y Firefox. Se puede hacer que funcione en Windows, Linux y Mac (y probablemente en muchas otras plataformas). Consulte las páginas específicas de la plataforma a continuación. Los diccionarios de Hunspell están fácilmente disponibles y es posible que ya estén instalados en muchas máquinas. Incluso si no puede acceder a la biblioteca de otra aplicación, puede usar su diccionario.
1212
1313Los diccionarios de Hunspell vienen como un par de archivos *.dic y *.aff. Por ejemplo, el diccionario australiano consta de en_AU.dic y en_AU.aff. La 'en' indica que es inglés y la 'AU' dice que es específicamente para Australia. Como hablante de inglés, observo que los diccionarios en_US siempre parecen estar instalados y agrego los australianos. No sé qué tan extendido está este patrón en los sistemas que no hablan inglés. Puede encontrar diccionarios aquí <https://github.com/LibreOffice/dictionaries> y también información relacionada aquí <https://wiki.documentfoundation.org/Development/Dictionaries>
1414
1515## Note about dictionaries
1616
1717In some cases in order to display the suggested words correctly the dictionaries must be encoded in UTF-8. If you have notice that some words are not shown correctly--for example, apple in Polish is jabłko but you get jab�ko--it means that dictionary pl_PL.aff and pl_PL.dic must be converted in UTF-8. To convert dictionaries into UTF-8 in Linux Debian (it should work in any Linux, converted dictionaries can be used in Windows-check also [this forum thread](http://forum.lazarus.freepascal.org/index.php/topic,44298.0.html)) follow below steps:
1818
19191\. Start terminal as root
2020
21-`  su`
21+```text
22+ su
23+```
2224
23252\. If not yet installed, install hunspell (hunspell-pl is for Polish dictionary--more at [Debian Hunspell package](https://packages.debian.org/search?keywords=hunspell))
2426
25-`  apt-get install hunspell`\
26-`  apt-get install hunspell-pl`
27+```text
28+ apt-get install hunspell
29+ apt-get install hunspell-pl
30+```
2731
28323\. Go to /usr/share/hunspell/ where dictionaries are kept and create folder dic
2933
30-`  cd /usr/share/hunspell/`\
31-`  mkdir dic`
34+```text
35+ cd /usr/share/hunspell/
36+ mkdir dic
37+```
3238
33394\. Convert for example dictionary pl_PL.aff and pl_PL.dic Use [ISO-8859-2](https://en.wikipedia.org/wiki/ISO/IEC_8859-2) (for Eastern Europe), [ISO-8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1) (or ISO-8859-15)for Western Europe, ISO-8859-5 for (Russian?), etc.
3440
35-`  iconv -f ISO-8859-2 -t UTF-8 /usr/share/hunspell/pl_PL.aff | sed 's/^SET ISO8859-2$/SET UTF-8/g' > dic/pl_PL.aff`\
36-`  iconv -f ISO-8859-2 -t UTF-8 /usr/share/hunspell/pl_PL.dic > dic/pl_PL.dic`
41+```text
42+ iconv -f ISO-8859-2 -t UTF-8 /usr/share/hunspell/pl_PL.aff | sed 's/^SET ISO8859-2$/SET UTF-8/g' > dic/pl_PL.aff
43+ iconv -f ISO-8859-2 -t UTF-8 /usr/share/hunspell/pl_PL.dic > dic/pl_PL.dic
44+```
3745
38465\. Copy your dic folder or dictionaries from dic folder to your application
3947
4048## Platform Specific
4149
4250### Linux
4351
4452Many Linux distributions will have Hunspell installed by default along with the appropriate language dictionaries. If not, its probably just a case of using the distribution's package manager. If all else fails, grab the source from the hunspell github site and build it yourself. Linux users are like that.
4553
4654To see if you do in fact have a hunspell library installed, try this command.
4755
48-` ldconfig -p | grep hunspell`
56+```text
57+ ldconfig -p | grep hunspell
58+```
4959
5060Similarly, you can probably find some dictionaries with
5161
52-` ls -l /usr/share/hunspell`
62+```text
63+ ls -l /usr/share/hunspell
64+```
5365
5466If that does not work, try
5567
56-` find /usr -name *.aff`
68+```text
69+ find /usr -name *.aff
70+```
5771
5872it will take a bit longer ..
5973
6074### Windows
6175
6276Installing the hunspell library on windows is more of an issue. There is apparently no pre compiled 'kit' available and most Windows apps that use Hunspell appear to bind it staticly thus no hunspell.dll left lying around for you to use. But just to be sure, try searching for `*hunspell*.dll`. The Hunspell github site lists a recipe to build one but it involves installing MSYS2 and is quite involved. The resulting DLL also needs a couple of gcc DLLs along as well.
6377
6478Fortunately user rvk on the Lazarus Forum has built us a nice statically (ie stand alone) linked DLL using Microsoft Visual Studio Community 2015. As such, you can use and distribute this DLL with your programme subject to the Mozilla Public License.
6579
6680You will find this DLL bundled with its license file in <https://github.com/davidbannon/hunspell4pas>, click 'DLL', click the dll file, and you will get a 'Download' button. Don't forget to also get the license file, it should be distributed, one way or another with your app.
6781
6882### Mac
6983
7084The author's Mac appears to have had the Hunspell library installed when Sierra was installed. But maybe, just maybe, it came along with Firefox. I'd like some feed back .... \[already installed on Mojave and Catalina\]
7185
7286> **Note:** See <https://opensource.apple.com/tarballs/hunspell/> for source download for v1.2.8.
7387
7488To see if you aleady have a hunspell library installed, try this command
7589
76-` find / 2>&1 | grep "\hunspell"`
90+```text
91+ find / 2>&1 | grep "\hunspell"
92+```
7793
7894It will run for some time, depending on how many files on your system. It will likely find several files including some in your XCode directory. However, end users will probably not have XCode installed. The one particularly interesting file to me was
7995
80-` /usr/lib/libhunspell-1.2.dylib`
96+```text
97+ /usr/lib/libhunspell-1.2.dylib
98+```
8199
82100Version 1.2 is a bit older than elsewhere but it worked fine. If you don't find a usable library, I suggest you install one using a package manager like MacPorts, Fink or brew. Next issue issue is you will need some dictionaries. Similar command,
83101
84-` find / 2>&1 | grep "\.aff"`
102+```text
103+ find / 2>&1 | grep "\.aff"
104+```
85105
86106again, slow, it searches your whole disk. I found
87107
88-` /Applications/Firefox.app/Contents/Resources/dictionaries/en-US.aff`
108+```text
109+ /Applications/Firefox.app/Contents/Resources/dictionaries/en-US.aff
110+```
89111
90112And a quick 'ls' assured me that there was a matching 'en-US.dic' files so all good. A more experienced Mac user might like to suggest better search strategies. Please !
91113
92114## The Hunspell Unit
93115
94116When this page was initially created (2017 ?) I made a serious mistake and used some code that did not have a clear license or history. It has been suggested that by doing so, I breached the Hunspell license as has anyone who has used it since then.
95117
96118To correct this situation, I have now released a new version of the Hunspell Wrapper that has a quite small part, just the function definitions, with the Hunspell (License: MPL 1.1/GPL 2.0/LGPL 2.1) and the Hunspell Unit (License: The Clear BSD License) in seperate files. From a coding point of view, its almost identical to what used to be here.
97119
98120Please see <https://github.com/davidbannon/hunspell4pas>
99121
100122I have removed the hunspell wrapper that used to live on this page. The one on Github above is a drop in replacement, you need hunspell.pas and hunspell.inc. The remainder of the file make a basic test/demo command line Lazarus project that replaces Demo 1.
101123
102124### Demo 2 in a Full GUI
103125
104126A Lazarus GUI demo makes a lot more sense and has been tested on Linux, Mac and Windows. But it is a bit harder to copy and paste.
105127
106128For this demo, you'll need a form with two TMemos, Memo1 and MemoMsg. A button, ButtonSpell, a Tlistbox, Listbox1. Make the following event handlers : FormCreate for the main form; a double click for the listbox and click on the Button.
107129
108130First you must create the hunspell object and see if it found a library its own way, here is an example in the FormCreate() method ....
109131
110132``` pascal
111133uses hunspell; // get hunspell.pas and hunspell.inc from https://github.com/davidbannon/hunspell4pas
112134var
113135 Form1: TForm1;
114136 Sp: THunspell;
115137 DictPath : AnsiString;
116138
117139procedure TForm1.FormCreate(Sender: TObject);
118140begin
119141 SetDefaultDicPath();
120142 Sp := THunspell.Create(True);
121143 if Sp.ErrorMessage = '' then begin
122144 MemoMsg.append('Library Loaded =' + Sp.LibraryFullName);
123145 ButtonSpell.enabled := CheckForDict();
124146 end else
125147 MemoMsg.append(SP.ErrorMessage);
126148end;
127149```
128150
129151In this example, we are writing some status message to MemoMsg, its an easy way to see whats happening. The ButtonSpell is NOT enabled until the dictionaries have been set. Wait for it ....
130152
131153We now need two methods, one reads a nominated directory looking for any likely dictionary files, the other manages the decisions. If we find just one dictionary set, use it, if we find none, complain. But if we find several, and thats most likely, we have to ask the user which dictionary (ie language) they wish to use.
132154
133155``` pascal
134156function TForm1.FindDictionary(const Dict : TStrings; const DPath : AnsiString) : boolean;
135157var
136158 Info : TSearchRec;
137159begin
138160 Dict.Clear;
139161 if FindFirst(AppendPathDelim(DPath) + '*.dic', faAnyFile and faDirectory, Info)=0 then begin
140162 repeat
141163 Dict.Add(Info.Name);
142164 until FindNext(Info) <> 0;
143165 end;
144166 FindClose(Info);
145167 Result := Dict.Count >= 1;
146168end;
147169
148170function TForm1.CheckForDict() : boolean;
149171begin
150172 Result := False;
151173 EditDictPath.Caption := DictPathAlt;
152174 if not FindDictionary(ListBox1.Items, DictPath) then
153175 MemoMsg.Append('ERROR - no dictionaries found in ' + DictPath);
154176 if ListBox1.Items.Count = 1 then begin // Exactly one returned.
155177 if not Sp.SetDictionary( AppendPathDelim(DictPath) +
156178 ListBox1.Items.Strings[0])
157179 then
158180 MemoMsg.Append('ERROR ' + SP.ErrorMessage)
159181 else
160182 MemoMsg.Append('Dictionary set to ' + DictPath +
161183 ListBox1.Items.Strings[0]);
162184 end;
163185 Result := SP.GoodToGo; // only true if count was exactly one or FindDict failed and nothing changed
164186end;
165187```
166188
167189Ah, you say, but where are we looking for the dictionaries ? Sadly, I don't have a good solution for that. Here is where I found mine -
168190
169191``` pascal
170192procedure TForm1.SetDefaultDicPath();
171193begin
172194 {$ifdef LINUX}
173195 DictPath := '/usr/share/hunspell/';
174196 {$ENDIF}
175197 {$ifdef WINDOWS}
176198 DictPath := ExtractFilePath(Application.ExeName);
177199 //DictPath := 'C:\Program Files\LibreOffice 5\share\extensions\dict-en\';
178200 {$ENDIF}
179201 {$ifdef DARWIN}
180202 DictPath := '/Applications/Firefox.app/Contents/Resources/dictionaries/';
181203 //DictPathAlt := ExtractFilePath(Application.ExeName);
182204 {$endif}
183205end;
184206```
185207
186208Maybe, if other users contribute where they found usable hunspell dictionaries, we can build a list for each platform. Or just take the easy way out and tell user to get some dictionaries and put them into the application directory on Windows and Mac. Your thoughts very welcome....
187209
188210So far, if there is exactly one dictionary set in the indicated directory, all good. But what if there are several ? Our list box has a list of them, if the user double clicks one, they trigger this method -
189211
190212``` pascal
191213procedure TForm1.ListBox1DblClick(Sender: TObject);
192214begin
193215 if ListBox1.ItemIndex > -1 then
194216 ButtonSpell.enabled := Sp.SetDictionary( AppendPathDelim(DictPath) +
195217 ListBox1.Items.Strings[ListBox1.ItemIndex]);
196218 if SP.ErrorMessage = '' then begin
197219 MemoMsg.Append('Good To Go =' + booltostr(Sp.GoodToGo, True));
198220 MemoMsg.Append('Dictionary set to ' + AppendPathDelim(DictPath) +
199221 ListBox1.Items.Strings[ListBox1.ItemIndex]);
200222 end else
201223 MemoMsg.append('ERROR ' + SP.ErrorMessage);
202224end;
203225```
204226
205227Assuming we now have everything "good to go" we can press the Spell button and trigger this -
206228
207229``` pascal
208230procedure TForm1.ButtonSpellClick(Sender: TObject);
209231begin
210232 if not Sp.Spell(Edit1.text) then begin
211233 Memo1.Lines.BeginUpdate;
212234 Sp.Suggest('badspeller', Memo1.lines);
213235 Memo1.Lines.EndUpdate;
214236 end else
215237 Memo1.Lines.Clear;
216238end;
217239```
218240
219241Memo1 now contains some suggestions for better ways to spell badspeller !
220242
221243Important ! Don't forget to free up our hunspeller object, memory leaks are evil !
222244
223245``` pascal
224246procedure TForm1.FormDestroy(Sender: TObject);
225247begin
226248 Sp.free;
227249 Sp := nil;
228250end;
229251```
230252
231253This module will do considerably more but its presented here in its most stripped down form for readability.
232254
233255*A note to people new to Lazarus, methods with "(Sender: TObject)" shown above cannot just be pasted into your source, use the Form's Object Inspector to create the events first, then paste my sample code into the method.*
234256
235257### Demo 3 - Lazspell - Tmemo
236258
237259In this example TMemo component was used.
238260
239261![](/wiki-images/b/b3/Lazspell.gif)
240262
241263Lazspell - an example of spelling checker - from <https://github.com/Raf20076/Lazspell>
242264
243265### Demo 4 - Lazspell 2-version 2 - TRichMemo
244266
245267In this example TRichMemo component was used.
246268
247269![](/wiki-images/8/8c/Lazspell-ver2.gif)
248270
249271Lazspell 2 version 2 - an example of spelling checker - from <https://github.com/Raf20076/Lazspell-2-version-2>
250272
251273### Demo 5 - Simple spelling checker
252274
2532751\. Start Lazarus Ide
254276
2552772\. Click Project -\> New Project -\> Choose -\> Application
256278
257-`  You have just created a new application. Now save it `
279+```text
280+ You have just created a new application. Now save it
281+```
258282
2592833\. Click File -\> Save as
260284
261-`  Choose a folder where your application will be saved`\
262-`  First, project1.lpi will be saved, then unit1.pas`
285+```text
286+ Choose a folder where your application will be saved
287+ First, project1.lpi will be saved, then unit1.pas
288+```
263289
2642904\. Place hunspell.pas and hunspell.inc (from <https://github.com/davidbannon/hunspell4pas>) in your folder.
265291
2662925\. Place libhunspell.dll in your application folder (you can download it from here <https://github.com/Raf20076/Lazspell> or <https://github.com/davidbannon/hunspell4pas>) any options or compile by yourself.
267293
2682946\. Place dictionary in your application folder, (two files) like pl_PL.aff, pl_PL.dic. Ditionaries must be encoded in UTF8. You can download them from here <https://github.com/Raf20076/Lazspell/tree/master/dict>
269295
2702967\. On Form1 place component TButton (Button1) from Standard Tab
271297
2722988\. On Form1 place component TMemo (Memo1) from Standard Tab
273299
2743009\. On Form1 place component TListbox (ListBox1) from Standard Tab (here misspell words will be shown)
275301
27630210\. Click once Button1 on Form1 then go to ObjectInspector, click Events tab, and double click next to OnClik event: it creates OnClick event and paste code (see the code of the whole application) starts from
277303
278304``` pascal
279305
280306{Check spelling}
281307 var
282308 i : Integer;
283309 MAX : Integer;
284310 FillInArrayWithWords:TStringArray;
285311 FillInString1: String;
286312 FillInString2: String;
287313```
288314
289315till
290316
291317``` pascal
292318
293319//If word is not in dictionary show it in Listbox as an error
294320```
295321
29632211\. Go to ObjectInspector and click Form1, then click Events tab then double click next to OnCreate it creates OnCreate event and paste code (see the code of the whole application)
297323
298324``` pascal
299325 SpellCheck := THunspell.Create(True);
300326 SpellCheck.SetDictionary('pl_PL.dic');//Load dictionary
301327 SpellCheck.GoodToGo := True;
302328```
303329
30433012\. Still in ObjectInspector click Form1,then click Events tab then double click next to OnDestroy it creates OnDestroy event and paste code (see the code of the whole application)
305331
306-` SpellCheck.free;`\
307-` SpellCheck := nil;`
332+``` pascal
333+ SpellCheck.free;
334+ SpellCheck := nil;
335+```
308336
30933713\. Then in your code place functions like ArrayValueCount etc
310338
311339See Below the code of the whole application to compare with your one.
312340
313341``` pascal
314342//SpellChecker by Raf20076, Poland 2019
315343
316344unit Unit1;
317345
318346{$mode objfpc}{$H+}
319347
320348interface
321349
322350uses
323351 Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
324352 LazFileUtils, LCLProc, LazUtils, LazUtf8;
325353
326354type
327355 TForm1 = class(TForm)
328356 Button1: TButton;
329357 Label1: TLabel;
330358 Label2: TLabel;
331359 ListBox1: TListBox;
332360 ListBox2: TListBox;
333361 Memo1: TMemo;
334362 procedure Button1Click(Sender: TObject);
335363 procedure FormCreate(Sender: TObject);
336364 procedure FormDestroy(Sender: TObject);
337365 end;
338366
339367var
340368 Form1: TForm1;
341369
342370implementation
343371
344372uses hunspell;//place hunspell.pas and hunspell .inc in your application folder
345373 //from https://github.com/davidbannon/hunspell4pas
346374
347375var
348376 SpellCheck: THunspell;
349377
350378{$R *.lfm}
351379
352380procedure TForm1.FormCreate(Sender: TObject);
353381begin
354382 SpellCheck := THunspell.Create(True);
355383 SpellCheck.SetDictionary('pl_PL.dic');//Load dictionary
356384 SpellCheck.GoodToGo := True;
357385end;
358386
359387{Extract words from string: non characters, spaces, carriagereturn aware}
360388
361389function ArrayValueCount(const InputArray: Array of string): Integer;
362390{Count elements in array}
363391var
364392 i:Integer;
365393begin
366394 result := 0;
367395 for i := low(InputArray) to high(InputArray) do
368396 if InputArray[i] <> ' ' then // 'between them one space'
369397 inc(result);
370398end;
371399
372400function StripOffNonCharacter(const aString: string): string;
373401{Remove non characters from string}
374402var
375403 a: Char;
376404begin
377405 Result := '';
378406 for a in aString do begin //below punctuation marks, numbers to remove from string
379407 if not CharInSet( a, ['.', ',', ';', ':', '!', '/',
380408 '?', '@', '#', '$', '%', '&', '*', '(', ')', '{',
381409 '}', '[', ']', '-', '\', '|', '<', '>', '''', '"', '^',
382410 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_', '+',
383411 '=', '~']) then //'„', '”'])these marks make error: Ordinal expression expected, need to be fixed
384412 begin
385413 Result := Result + a;
386414 end;
387415 end;
388416end;
389417
390418function ReplaceCarriageReturn(s: string) : string;
391419{Replace carriagereturn with one space}
392420var
393421 i: Integer;
394422begin
395423 Result:=s;
396424 for i := 1 to Length(Result) do
397425 if Result[i] in [#3..#13] then
398426 Result[i] := ' ';//'Between them one space'
399427end;
400428
401429procedure TForm1.Button1Click(Sender: TObject);
402430{Check spelling}
403431var
404432 i : Integer;
405433 MAX : Integer;
406434 FillInArrayWithWords:TStringArray;
407435 FillInString1: String;
408436 FillInString2: String;
409437begin
410438 ListBox1.clear;
411439 ListBox1.Items.Clear;
412440
413441 FillInString1 := ReplaceCarriageReturn(Memo1.Lines.Text);//take text from Memo1; replace carriage return
414442 //with one space (using ReplaceCarriageReturn function) and put into FillInString1
415443
416444 FillInString2 := StripOffNonCharacter(FillInString1);//remove all non characters from FillInString1
417445 //(using StripOffNonCharacter function) and put string without non charachters into FillInString2
418446
419447 FillInArrayWithWords := FillInString2.split(' '); //Split string into words with ' ' one space
420448 //(using .split) and put separate words into array FillInArrayWithWords
421449
422450 MAX := ArrayValueCount(FillInArrayWithWords); //Give how many elements are in array
423451 //(using function ArrayValueCount)
424452
425453 for i := 0 to MAX -1 do
426454 if not SpellCheck.Spell(FillInArrayWithWords[i]) then
427455 ListBox1.Items.add(FillInArrayWithWords[i]);
428456 //Take word from array and check in dictionary through hunspell (using SpellCheck.Spell function)
429457 //If word is not in dictionary show it in Listbox as an error
430458end;
431459
432460procedure TForm1.FormDestroy(Sender: TObject);
433461begin
434462 SpellCheck.free;
435463 SpellCheck := nil;
436464end;
437465
438466end.
439467```
440468
441469When you run your application and type some text in Memo and then press the button, the application will check if any errors are found, if found then shows them in ListBox. The main problem was how to remove carriage return and non characters and then how to split text into separate words. It's not an easy task. Therefore there are three functions to do this:ReplaceCarriageReturn function, StripOffNonCharacter function and FillInString2.split , function. The FillInString2.split function actually uses .split function from SysUtils. Hopefully the code of the whole application is self explanatory.
442470
443471## Further Reading and Links
444472
445473- <https://github.com/hunspell/hunspell>
446474- <https://github.com/davidbannon/hunspell4pas> - the (correctly licensed) hunspell object pascal unit.
447475- <https://github.com/Homebrew> - probably sensible way to get hunspell on your Mac if it's not already there.
448476- <https://github.com/tomboy-notes/tomboy-ng/releases> - Contains The 64bit Windows DLL in a tomboy-ng_win64_\<ver\>.zip
449477- <https://github.com/cutec-chris/hunspell>
450478- <https://forum.lazarus.freepascal.org/index.php/topic,50474.0.html> - FastHighliter code to use for highlighting misspelt words
451479- <https://github.com/Raf20076> - Lazspell is an example of simple spelling checker application written in Lazarus IDE.
452480- <https://forum.lazarus.freepascal.org/index.php/topic,46233.0.html>
453481- <https://forum.lazarus.freepascal.org/index.php/topic,51569.0.html>
454482- <https://forum.lazarus.freepascal.org/index.php?topic=44298.0>
455483
456484---
457485*From the Free Pascal wiki, page "spelling/es", as it stood on 2022-03-07 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history