Difference between revisions of "spelling/ru"
(→Mac) |
|||
Line 56: | Line 56: | ||
Более опытный пользователь Mac может предложить лучшие стратегии поиска. Пожалуйста! | Более опытный пользователь Mac может предложить лучшие стратегии поиска. Пожалуйста! | ||
− | == | + | == Модуль Hunspell == |
− | === Demo 1 | + | === Demo 1 простая командная строка === |
− | + | Вот очень простое демо командной строки о том, как использовать hunspell.pas. К сожалению, это конкретное демо подходит только для Linux, как описано ниже. Сохраните этот блок кода как testhun.pas и сохраните hunspell.pas (ниже) в каталоге и введите следующую команду: | |
<code>fpc -Fu/usr/share/lazarus/1.8.0/components/lazutils/lib/x86_64-linux -Fu. testhun.pas</code> | <code>fpc -Fu/usr/share/lazarus/1.8.0/components/lazutils/lib/x86_64-linux -Fu. testhun.pas</code> | ||
− | + | Если вы используете 32-битную версию Linux или ваш Lazarus установлен «где-то еще», вам нужно настроить параметр в -Fu | |
<syntaxhighlight> | <syntaxhighlight> | ||
Line 92: | Line 92: | ||
Sts.Free; | Sts.Free; | ||
end else | end else | ||
− | writeln('ERROR - Dictionary not loaded.'); | + | writeln('ERROR - Dictionary not loaded.');//[прим.перев.]: ОШИБКА - словари не найдены |
− | end else writeln('ERROR - Library not loaded.'); | + | end else writeln('ERROR - Library not loaded.');//[прим.перев.]: ОШИБКА - библиотека не найдена |
Spell.Free; | Spell.Free; | ||
end. | end. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Почему именно эта демонстрационная версия [для] Linux? Модуль hunspell предназначен для приложений с графическим интерфейсом, он использует модуль под названием Forms, который не имеет смысла в приложении командной строки. В Windows и Mac методы Forms, Application.ExeName используется, чтобы определить, где находится бинарный файл, если вы поместили туда библиотеку hunspell (в Linux она имеет предопределенное место для существования). | |
− | === Demo 2 | + | === Demo 2 в полном графическом интерфейсе === |
− | + | Демо графического интерфейса Lazarus имеет больше смысла и была протестирована на Linux, Mac и Windows. Но это немного сложнее, [чем] скопировать и вставить. | |
− | + | Для этой демо вам понадобится форма с двумя TMemo: Memo1 и MemoMsg. Кнопка ButtonSpell, Tlistbox Listbox1. Сделайте следующие обработчики событий: FormCreate для главной формы; дважды щелкните по Listbox1 и [дважды] кликните по ButtonSpell. | |
− | + | Сначала вы должны создать объект hunspell и посмотреть, нашел ли он свою библиотеку, вот пример метода FormCreate () .... | |
<syntaxhighlight> | <syntaxhighlight> | ||
uses hunspell; | uses hunspell; | ||
Line 126: | Line 126: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | В этом примере мы пишем сообщение о статусе в MemoMsg, это простой способ увидеть, что происходит. ButtonSpell НЕ включен, пока не будут установлены словари. Ожидаем этого .... | |
− | + | Теперь нам нужны два метода: один читает назначенный каталог и ищет возможные файлы словарей, другой управляет решениями. Если мы найдем только один словарный набор, используем его, если мы не найдем - пожалуемся. Но если мы найдем несколько, и это наиболее вероятно, мы должны спросить пользователя, какой словарь (то есть язык) он хочет использовать. | |
<syntaxhighlight> | <syntaxhighlight> | ||
Line 150: | Line 150: | ||
EditDictPath.Caption := DictPathAlt; | EditDictPath.Caption := DictPathAlt; | ||
if not FindDictionary(ListBox1.Items, DictPath) then | if not FindDictionary(ListBox1.Items, DictPath) then | ||
− | MemoMsg.Append('ERROR - no dictionaries found in ' + DictPath); | + | MemoMsg.Append('ERROR - no dictionaries found in ' + DictPath);//[прим.перев.]: словари в DictPath не найдены |
− | if ListBox1.Items.Count = 1 then begin // | + | if ListBox1.Items.Count = 1 then begin // Один [словарь] вернулся точно. |
if not Sp.SetDictionary(AppendPathDelim(DictPath) + ListBox1.Items.Strings[0]) then | if not Sp.SetDictionary(AppendPathDelim(DictPath) + ListBox1.Items.Strings[0]) then | ||
MemoMsg.Append('ERROR ' + SP.ErrorMessage) | MemoMsg.Append('ERROR ' + SP.ErrorMessage) | ||
else | else | ||
− | MemoMsg.Append('Dictionary set to ' + DictPath + ListBox1.Items.Strings[0]); | + | MemoMsg.Append('Dictionary set to ' + DictPath + ListBox1.Items.Strings[0]);// [прим.перев.]: словарь (первый из списка ListBox1) установлен в DictPath |
end; | end; | ||
− | Result := SP.GoodToGo; // | + | Result := SP.GoodToGo; // если count был точно один или FindDict не вернул ничего и ничего не изменилось |
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Ах, вы спросите, но где нам искать словари? К сожалению, у меня нет хорошего решения для этого. Вот где я нашел свой - | |
<syntaxhighlight> | <syntaxhighlight> | ||
procedure TForm1.SetDefaultDicPath(); | procedure TForm1.SetDefaultDicPath(); | ||
Line 179: | Line 179: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Возможно, если другие пользователи сообщат, где они нашли пригодные для использования словари Hunspell, мы можем составить список для каждой платформы. Или просто выберите легкий путь и попросите пользователя найти несколько словарей и поместить их в каталог приложений на Windows и Mac. Ваши мысли очень приветствуются .... | |
− | + | Пока, если в указанном каталоге есть ровно один словарь, все хорошо. Но что, если их несколько? Наш ListBox1 содержит их список, если пользователь дважды щелкнет один из них, он вызовет этот метод - | |
<syntaxhighlight> | <syntaxhighlight> | ||
Line 196: | Line 196: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Предполагая, что у нас теперь есть все, что нужно, мы можем нажать кнопку ButtonSpell и вызвать это - | |
<syntaxhighlight> | <syntaxhighlight> | ||
Line 210: | Line 210: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Memo1 | + | Memo1 теперь содержит несколько советов о лучших способах написания [слова] неграмотному! |
− | + | Важно! Не забудьте освободить наш объект hunspeller, утечки памяти - зло! | |
<syntaxhighlight> | <syntaxhighlight> | ||
procedure TForm1.FormDestroy(Sender: TObject); | procedure TForm1.FormDestroy(Sender: TObject); | ||
Line 221: | Line 221: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Этот модуль делает значительно больше, но он представлен здесь в его наиболее урезанной форме для удобства чтения. | |
− | + | {{Note| для новичков в Lazarus, методы с "(Sender: TObject)", показанные выше, не могут быть просто вставлены в ваш исходник, сначала используйте инспектор объектов формы, чтобы создать события, а затем вставьте мой пример кода в метод.}} | |
=== Actual Code === | === Actual Code === |
Revision as of 22:06, 14 January 2019
│ English (en) │ español (es) │ русский (ru) │
Использование hunspell с Lazarus
Эта страница актуальна по состоянию на август 2018 года, но все меняется .....
Эта страница посвящена использованию библиотеки hunspell с Lazarus. Он описывает модель, которая работает, вроде как. Вам почти наверняка понадобится внести некоторые изменения для ваших конкретных целей, но, надеюсь, эта страница послужит вам хорошим началом.
Во-первых, на форуме есть несколько ссылок на некоторый код, который будет работать с библиотекой hunspell. Модуль hunspell.pas в значительной степени основан на этих блоках кода. У большинства нет информации о лицензии, и делается предположение, что она является «общеизвестной» и, следовательно, свободна от каких-либо ограничений. Я добавил немного, что решает проблему поиска файлов библиотеки и словаря. И установил разумный интерфейс.
Кроме того, пользователь rvk с форума создал Windows 64-битную DLL, так как для пользователей Windows не было никакой альтернативы.
О библиотеке Hunspell
Hunspell - это активный проект с открытым исходным кодом, распространяемый по открытой лицензии Mozilla. Библиотека hunspell используется в таких продуктах, как Libra Office, Open Office и Firefox. Его можно заставить работать на Windows, Linux и Mac (и, возможно, на кучах других платформ). См. [ссылки на] платформоспецифичные страницы ниже. Словари Hunspell легко доступны и, возможно, уже установлены на многих машинах. Даже если вы не можете получить доступ к библиотеке другого приложения, вы можете использовать его словарь.
Словари Hunspell поставляются в виде пары файлов *.dic и *.aff. Например, австралийский словарь состоит из en_AU.dic и en_AU.aff. [Префикс] 'en' обозначает его английский, а [суффикс] 'AU' говорит о его [специфичности] специально для Австралии. Как говорящий по-английски, я отмечаю, что словари en_US, кажется, всегда установлены, и я добавляю австралийские. Я не знаю, насколько распространен этот шаблон в не-англоязычных системах.
Платформозависимость
Linux
Во многих дистрибутивах Linux по умолчанию установлен Hunspell вместе с соответствующими языковыми словарями. Если нет, то, вероятно, это просто случай использования менеджера пакетов дистрибутива. Если ничего не помогает, возьмите исходник с сайта hunspell github и создайте его самостоятельно. Пользователи Linux любят это.
Чтобы проверить, установлена ли у вас библиотека hunspell, попробуйте эту команду - ldconfig -p | grep hunspell
. Точно так же вы можете найти некоторые словари с помощью ls -l /usr/share/hunspell
. Если это не сработает, попробуйте find /usr -name *.aff
, это займет немного больше времени.
Windows
Установка библиотеки hunspell на Windows является более серьезной проблемой. По-видимому, нет предварительно скомпилированного 'комплекта', и большинство приложений Windows, которые используют Hunspell, похоже, статически связывают его, поэтому не осталось никаких hunspell.dll для использования. Но просто, чтобы быть уверенным, попробуйте поискать *hunspell*.dll
. На сайте Hunspell github приведен рецепт его создания, но он включает установку MSYS2 и довольно сложен. Получающаяся DLL также нуждается вдобавок в паре gcc DLL.
К счастью, пользователь rvk на форуме Lazarus создал нам хорошую статически (т.е. автономно) связанную DLL-библиотеку с использованием Microsoft Visual Studio Community 2015. Таким образом, вы можете использовать и распространять эту DLL-библиотеку вместе с вашей программой, подпадающей под действие публичной лицензии Mozilla.
Вы найдете эту DLL в комплекте с 64-битной (предварительной) версией tomboy-ng, просто скачайте zip-файл, распакуйте и выбросьте (как печально) бинарный файл tomboy-ng. Смотрите https://github.com/tomboy-notes/tomboy-ng/releases
Прим.перев.: Вы также можете посмотреть исходники интерфейса Hunspell для Lazarus здесь: https://github.com/cutec-chris/hunspell
Mac
На Mac'е автора, по-видимому, была установлена библиотека Hunspell при установке Sierra. Но, может быть, просто возможно, это пришло вместе с Firefox. Я хотел бы получить обратную связь ....
Чтобы проверить, установлена ли у вас библиотека hunspell, попробуйте эту команду - find / 2>&1 | grep "\hunspell"
, она будет выполняться некоторое время, в зависимости от того, сколько файлов в вашей системе. Скорее всего, она найдет несколько файлов, включая некоторые, в вашем каталоге XCode. Однако конечным пользователям, вероятно, не будет установлен XCode. Один особенно интересный файл для меня был /usr/lib/libhunspell-1.2.dylib
. Версия 1.2 немного старше, чем где-либо, но работала нормально.
Если вы не можете найти пригодную для использования библиотеку, я предлагаю вам установить ее с помощью brew, см. ссылку ниже.
Следующая проблема - вам понадобятся словари. Аналогичная команда, find / 2>&1 | grep "\.aff"
, опять же, медленная, она ищет по всему вашему диску. Я нашел [команду] /Applications/Firefox.app/Contents/Resources/dictionaries/en-US.aff
. И быстрый 'ls' заверил меня, что есть соответствующие файлы en-US.dic, так что все хорошо.
Более опытный пользователь Mac может предложить лучшие стратегии поиска. Пожалуйста!
Модуль Hunspell
Demo 1 простая командная строка
Вот очень простое демо командной строки о том, как использовать hunspell.pas. К сожалению, это конкретное демо подходит только для Linux, как описано ниже. Сохраните этот блок кода как testhun.pas и сохраните hunspell.pas (ниже) в каталоге и введите следующую команду:
fpc -Fu/usr/share/lazarus/1.8.0/components/lazutils/lib/x86_64-linux -Fu. testhun.pas
Если вы используете 32-битную версию Linux или ваш Lazarus установлен «где-то еще», вам нужно настроить параметр в -Fu
program testhun;
{$mode objfpc}{$H+}
uses
Classes, hunspell, sysutils;
var
Spell : THunspell;
Sts : TStringList;
I : integer;
begin
Spell := THunspell.Create();
if Spell.ErrorMessage = '' then begin
if Spell.SetDictionary('/usr/share/hunspell/en_US.dic') then begin
writeln('speller ' + booltostr(Spell.Spell('speller'), True));
writeln('badspeller ' + booltostr(Spell.Spell('badspeller'), True));
Sts := TStringList.Create();
Spell.Suggest('badspeller', Sts);
for i := 0 to Sts.Count -1 do
writeln(' ' + Sts.Strings[I]);
Sts.Free;
end else
writeln('ERROR - Dictionary not loaded.');//[прим.перев.]: ОШИБКА - словари не найдены
end else writeln('ERROR - Library not loaded.');//[прим.перев.]: ОШИБКА - библиотека не найдена
Spell.Free;
end.
Почему именно эта демонстрационная версия [для] Linux? Модуль hunspell предназначен для приложений с графическим интерфейсом, он использует модуль под названием Forms, который не имеет смысла в приложении командной строки. В Windows и Mac методы Forms, Application.ExeName используется, чтобы определить, где находится бинарный файл, если вы поместили туда библиотеку hunspell (в Linux она имеет предопределенное место для существования).
Demo 2 в полном графическом интерфейсе
Демо графического интерфейса Lazarus имеет больше смысла и была протестирована на Linux, Mac и Windows. Но это немного сложнее, [чем] скопировать и вставить.
Для этой демо вам понадобится форма с двумя TMemo: Memo1 и MemoMsg. Кнопка ButtonSpell, Tlistbox Listbox1. Сделайте следующие обработчики событий: FormCreate для главной формы; дважды щелкните по Listbox1 и [дважды] кликните по ButtonSpell.
Сначала вы должны создать объект hunspell и посмотреть, нашел ли он свою библиотеку, вот пример метода FormCreate () ....
uses hunspell;
var
Form1: TForm1;
Sp: THunspell;
DictPath : AnsiString;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetDefaultDicPath();
Sp := THunspell.Create();
if Sp.ErrorMessage = '' then begin
MemoMsg.append('Library Loaded =' + Sp.LibraryFullName);
ButtonSpell.enabled := CheckForDict();
end else
MemoMsg.append(SP.ErrorMessage);
end;
В этом примере мы пишем сообщение о статусе в MemoMsg, это простой способ увидеть, что происходит. ButtonSpell НЕ включен, пока не будут установлены словари. Ожидаем этого ....
Теперь нам нужны два метода: один читает назначенный каталог и ищет возможные файлы словарей, другой управляет решениями. Если мы найдем только один словарный набор, используем его, если мы не найдем - пожалуемся. Но если мы найдем несколько, и это наиболее вероятно, мы должны спросить пользователя, какой словарь (то есть язык) он хочет использовать.
function TForm1.FindDictionary(const Dict : TStrings; const DPath : AnsiString) : boolean;
var
Info : TSearchRec;
begin
Dict.Clear;
if FindFirst(AppendPathDelim(DPath) + '*.dic', faAnyFile and faDirectory, Info)=0 then begin
repeat
Dict.Add(Info.Name);
until FindNext(Info) <> 0;
end;
FindClose(Info);
Result := Dict.Count >= 1;
end;
function TForm1.CheckForDict() : boolean;
begin
Result := False;
EditDictPath.Caption := DictPathAlt;
if not FindDictionary(ListBox1.Items, DictPath) then
MemoMsg.Append('ERROR - no dictionaries found in ' + DictPath);//[прим.перев.]: словари в DictPath не найдены
if ListBox1.Items.Count = 1 then begin // Один [словарь] вернулся точно.
if not Sp.SetDictionary(AppendPathDelim(DictPath) + ListBox1.Items.Strings[0]) then
MemoMsg.Append('ERROR ' + SP.ErrorMessage)
else
MemoMsg.Append('Dictionary set to ' + DictPath + ListBox1.Items.Strings[0]);// [прим.перев.]: словарь (первый из списка ListBox1) установлен в DictPath
end;
Result := SP.GoodToGo; // если count был точно один или FindDict не вернул ничего и ничего не изменилось
end;
Ах, вы спросите, но где нам искать словари? К сожалению, у меня нет хорошего решения для этого. Вот где я нашел свой -
procedure TForm1.SetDefaultDicPath();
begin
{$ifdef LINUX}
DictPath := '/usr/share/hunspell/';
{$ENDIF}
{$ifdef WINDOWS}
DictPath := ExtractFilePath(Application.ExeName);
//DictPath := 'C:\Program Files\LibreOffice 5\share\extensions\dict-en\';
{$ENDIF}
{$ifdef DARWIN}
DictPath := '/Applications/Firefox.app/Contents/Resources/dictionaries/';
//DictPathAlt := ExtractFilePath(Application.ExeName);
{$endif}
end;
Возможно, если другие пользователи сообщат, где они нашли пригодные для использования словари Hunspell, мы можем составить список для каждой платформы. Или просто выберите легкий путь и попросите пользователя найти несколько словарей и поместить их в каталог приложений на Windows и Mac. Ваши мысли очень приветствуются ....
Пока, если в указанном каталоге есть ровно один словарь, все хорошо. Но что, если их несколько? Наш ListBox1 содержит их список, если пользователь дважды щелкнет один из них, он вызовет этот метод -
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
if ListBox1.ItemIndex > -1 then
ButtonSpell.enabled := Sp.SetDictionary( AppendPathDelim(DictPath) + ListBox1.Items.Strings[ListBox1.ItemIndex]);
if SP.ErrorMessage = '' then begin
MemoMsg.Append('Good To Go =' + booltostr(Sp.GoodToGo, True));
MemoMsg.Append('Dictionary set to ' + AppendPathDelim(DictPath) + ListBox1.Items.Strings[ListBox1.ItemIndex]);
end else
MemoMsg.append('ERROR ' + SP.ErrorMessage);
end;
Предполагая, что у нас теперь есть все, что нужно, мы можем нажать кнопку ButtonSpell и вызвать это -
procedure TForm1.ButtonSpellClick(Sender: TObject);
begin
if not Sp.Spell(Edit1.text) then begin
Memo1.Lines.BeginUpdate;
Sp.Suggest('badspeller', Memo1.lines);
Memo1.Lines.EndUpdate;
end else
Memo1.Lines.Clear;
end;
Memo1 теперь содержит несколько советов о лучших способах написания [слова] неграмотному!
Важно! Не забудьте освободить наш объект hunspeller, утечки памяти - зло!
procedure TForm1.FormDestroy(Sender: TObject);
begin
Sp.free;
Sp := nil;
end;
Этот модуль делает значительно больше, но он представлен здесь в его наиболее урезанной форме для удобства чтения.
Actual Code
(sorry, it really is too long to paste into and out of a wiki page but that seems my only option)
{$MODE objfpc}{$H+}
unit hunspell;
{ Hunspell interface.
Based on code that seems to appear in lots of places in the Lazarus Forum
and elsewhere.
With additions and corrections by dbannon to make it a little easier to use.
As such, its assumed to be free to use by anyone for any purpose.
}
{ A Unit to connect to the hunspell library and check some spelling.
First, create the class, it will try and find a library to load.
Check ErrorMessage.
Then call SetDictionary(), with a full filename of the dictionary to use.
If GoodToGo is true, you can call Spell() and Suggests()
otherwise, look in ErrorString for what went wrong.
Look in FindLibrary() for default locations of Library.
}
interface
uses Classes, dynlibs;
type
THunspell_create = function(aff_file: PChar; dict_file: PChar): Pointer; cdecl;
THunspell_destroy = procedure(spell: Pointer); cdecl;
THunspell_spell = function(spell: Pointer; word: PChar): Boolean; cdecl;
THunspell_suggest = function(spell: Pointer; out slst: PPChar; word: PChar): Integer; cdecl;
THunspell_analyze = function(spell: Pointer; var slst: PPChar; word: PChar): Integer; cdecl;
THunspell_stem = function(spell: Pointer; var slst: PPChar; word: PChar): Integer; cdecl;
THunspell_free_list = procedure(spell: Pointer; var slst: PPChar; n: integer); cdecl;
THunspell_get_dic_encoding = function(spell: Pointer): PChar; cdecl;
THunspell_add = function(spell: Pointer; word: PChar): Integer; cdecl;
THunspell_remove = function(spell: Pointer; word: PChar): Integer; cdecl;
{ THunspell }
THunspell = class
private
Speller: Pointer;
{ Loads indicated library, returns False and sets ErrorMessage if something wrong }
function LoadHunspellLibrary(LibraryName: AnsiString): Boolean;
public
{ set to True if speller is ready to accept requests }
GoodToGo : boolean;
{ empty if OK, contains an error message if something goes wrong }
ErrorMessage : ANSIString;
{ Will have a full name to library if correctly loaded at create }
LibraryFullName : string;
{ Will have a "first guess" as to where dictionaries are, poke another name in
and call FindDictionary() if default did not work }
constructor Create();
destructor Destroy; override;
{ Returns True if word spelt correctly }
function Spell(Word: string): boolean;
{ Returns with List full of suggestions how to spell Word }
procedure Suggest(Word: string; List: TStrings);
{ untested }
procedure Add(Word: string);
{ untested }
procedure Remove(Word: string);
{ returns a full library name or '' if it cannot find anything suitable }
function FindLibrary(out FullName : AnsiString) : boolean;
{ returns true if it successfully set the indicated dictionary }
function SetDictionary(const FullDictName: string) : boolean;
function SetNewLibrary(const LibName : string) : boolean;
end;
var Hunspell_create: THunspell_create;
var Hunspell_destroy: THunspell_destroy;
var Hunspell_spell: Thunspell_spell;
var Hunspell_suggest: Thunspell_suggest;
var Hunspell_analyze: Thunspell_analyze;
var Hunspell_stem: Thunspell_stem;
var Hunspell_get_dic_encoding: Thunspell_get_dic_encoding;
var Hunspell_add: THunspell_add;
var Hunspell_free_list: THunspell_free_list;
var Hunspell_remove: THunspell_remove;
var HunLibLoaded: Boolean = False;
var HunLibHandle: THandle;
implementation
uses LazUTF8, SysUtils, {$ifdef linux}Process, {$else} Forms, {$endif} LazFileUtils;
// Forms needed so we can call Application.~
{ THunspell }
function THunspell.LoadHunspellLibrary(libraryName: Ansistring): Boolean;
begin
Result := false;
HunLibHandle := LoadLibrary(PAnsiChar(libraryName));
if HunLibHandle = NilHandle then
ErrorMessage := 'Failed to load library ' + libraryName
else begin
Result := True;
Hunspell_create := THunspell_create(GetProcAddress(HunLibHandle, 'Hunspell_create'));
if not Assigned(Hunspell_create) then Result := False;
Hunspell_destroy := Thunspell_destroy(GetProcAddress(HunLibHandle, 'Hunspell_destroy'));
if not Assigned(Hunspell_destroy) then Result := False;
Hunspell_spell := THunspell_spell(GetProcAddress(HunLibHandle, 'Hunspell_spell'));
if not Assigned(Hunspell_spell) then Result := False;
Hunspell_suggest := THunspell_suggest(GetProcAddress(HunLibHandle, 'Hunspell_suggest'));
if not Assigned(Hunspell_suggest) then Result := False;
Hunspell_analyze := THunspell_analyze(GetProcAddress(HunLibHandle, 'Hunspell_analyze')); // not used here
if not Assigned(Hunspell_analyze) then Result := False;
Hunspell_stem := THunspell_stem(GetProcAddress(HunLibHandle, 'Hunspell_stem')); // not used here
if not Assigned(Hunspell_stem) then Result := False;
Hunspell_get_dic_encoding := THunspell_get_dic_encoding(GetProcAddress(HunLibHandle, 'Hunspell_get_dic_encoding')); // not used here
if not Assigned(Hunspell_get_dic_encoding) then Result := False;
Hunspell_free_list := THunspell_free_list(GetProcAddress(HunLibHandle, 'Hunspell_free_list'));
if not Assigned(Hunspell_free_list) then Result := False;
Hunspell_add := THunspell_add(GetProcAddress(HunLibHandle, 'Hunspell_add'));
if not Assigned(Hunspell_add) then Result := False;
Hunspell_remove := THunspell_remove(GetProcAddress(HunLibHandle, 'Hunspell_remove'));
if not Assigned(Hunspell_remove) then Result := False;
HunLibLoaded := Result;
end;
if ErrorMessage = '' then
if not Result then ErrorMessage := 'Failed to find functions in ' + LibraryName;
end;
constructor THunspell.Create();
begin
ErrorMessage := '';
if Not FindLibrary(LibraryFullName) then begin
ErrorMessage := 'Cannot find Hunspell library';
exit();
end;
LoadHunspellLibrary(LibraryFullName); // will flag any errors it finds
Speller := nil; // we are not GoodToGo yet, need a dictionary ....
end;
destructor THunspell.Destroy;
begin
if (HunLibHandle <> 0) and HunLibLoaded then begin
if Speller<>nil then hunspell_destroy(Speller);
Speller:=nil;
if HunLibHandle <> 0 then FreeLibrary(HunLibHandle);
HunLibLoaded := false;
end;
inherited Destroy;
end;
function THunspell.Spell(Word: string): boolean;
begin
Result := hunspell_spell(Speller, PChar(Word))
end;
procedure THunspell.Suggest(Word: string; List: TStrings);
var i, len: Integer;
SugList, Words: PPChar;
begin
List.clear;
try
len := hunspell_suggest(Speller, SugList, PChar(Word));
Words := SugList;
for i := 1 to len do begin
List.Add(Words^);
Inc(PtrInt(Words), sizeOf(Pointer));
end;
finally
Hunspell_free_list(Speller, SugList, len);
end;
end;
procedure THunspell.Add(Word: string);
begin
Hunspell_add(Speller, Pchar(Word));
end;
procedure THunspell.Remove(Word: string);
begin
Hunspell_remove(Speller, Pchar(Word));
end;
function THunspell.FindLibrary(out FullName : ANSIString):boolean;
var
{$ifdef LINUX} I : integer = 1; {$endif}
Info : TSearchRec;
Mask : ANSIString;
begin
Result := False;
{$IFDEF LINUX}
// Assumes ldconfig always returns same format, better than searching several dirs
if RunCommand('/bin/bash',['-c','ldconfig -p | grep hunspell'], FullName) then begin
while UTF8Pos(' ', FullName, I) <> 0 do inc(I);
if I=1 then exit();
UTF8Delete(FullName, 1, I-1);
UTF8Delete(FullName, UTF8Pos(#10, FullName, 1), 1);
Result := True;
end;
exit();
{$ENDIF}
{$IFDEF WINDOWS} // Look for a dll in application home dir.
Mask := '*hunspell*.dll';
FullName := ExtractFilePath(Application.ExeName);
{$endif}
{$ifdef DARWIN}
Mask := 'libhunspell*';
FullName := '/usr/lib/';
{$endif}
if FindFirst(FullName + Mask, faAnyFile and faDirectory, Info)=0 then begin
FullName := FullName + Info.name;
Result := True;
end;
FindClose(Info);
end;
function THunspell.SetDictionary(const FullDictName: string) : boolean;
var
FullAff : string;
begin
FullAff := FullDictName;
UTF8Delete(FullAff, UTF8Length(FullAff) - 2, 3);
FullAff := FullAff + 'aff';
if Speller <> Nil then
hunspell_destroy(Speller);
Speller := hunspell_create(PChar(FullAff), PChar(FullDictName));
GoodToGo := Speller <> Nil;
if not GoodToGo then
ErrorMessage := 'Failed to set Dictionary ' + FullDictName;
Result := GoodToGo;
end;
function THunspell.SetNewLibrary(const LibName: string): boolean;
begin
LibraryFullName := LibName;
Result := LoadHunspellLibrary(LibraryFullName);
end;
end.
Note that this Unit uses LazUTF8, LazFileUtils and Forms. If you wish to use it as a simple command line application, you might add LCL to Required Packages in the Project Inspector or drop back to the FPC versions of Pos() etc but at the expense of UTF8 compatibility. And you cannot use Forms to provide Application.ExeName.
Further Reading and Links
https://github.com/hunspell/hunspell
https://github.com/Homebrew - probably sensible way to get hunspell on your mac if its not already there.
https://github.com/tomboy-notes/tomboy-ng/releases - Contains The 64bit Windows DLL in a tomboy-ng_win64_<ver>.zip