Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
- Subject: Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
- From: Douglas Davidson <email@hidden>
- Date: Thu, 08 Oct 2009 20:35:05 -0700
File a bug against NSSpellChecker and we will look into it.
Douglas Davidson
On Oct 8, 2009, at 1:35 PM, MacProjects <email@hidden> wrote:
Hello!
I'm maintaining system-wide Latvian spell checker for ~ 2 years. It
works as intended on 10.4 and 10.5.
Just made a dummy spellcheck for 10.6. checking out the new SDK.
Works great...
...except for the (only) new "Automatic by Language" option
introduced in 10.6.
Problem description (pardon if failed to keep it shorter) :
After installing (see Compiled service structure) the spell check,
opening i.e. TextEdit, I am able to check spelling for the
registered (here- Latvian) language by setting "Latvian" in
"Spelling and Grammar".
The words that are not in the language (in wordsAray) are
underlined, right-clicking them gives me a list of possible
corrections (suggestion1, suggestion2,...), pressing esc gives the
possible completions. I mean - everything works.
As I switch to "Automatic by language" in "Spelling and Grammar" a
weird behaviour occurs - it doesn't work as intended.
Text written in English, German a.o. Apple supported languages is
correctly checked.
Text written in Latvian is ... underlined as incorrect. However,
when right-clicking these underlined words or invoking completion I
get the suggestions that are delivered by my FooSpellCheck, that is,
NSSpellServer is calling methods
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestCompletionsForPartialWordRange:(NSRange)range inString:
(NSString *)string language:(NSString *)language;
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestGuessesForWord:(NSString *)word inLanguage:(NSString *)
language;
to my spellcheck!, however methods
// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender checkString:
(NSString *)stringToCheck offset:(NSUInteger)offset types:
(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)options
orthography:(NSOrthography *)orthography wordCount:(NSInteger *)
wordCount;
// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender
findMisspelledWordInString:(NSString *)stringToCheck language:
(NSString *)language wordCount:(NSInteger *)wordCount countOnly:
(BOOL)countOnly;
aren't! THEY SHOULD! Simply NSLog'ing the runtime shows this clearly.
Note: Yes, in sysprefs language&text everything is set/checked as
needed under Automatic by Language setup.
• So is this this a bug in Foundation frameworks NSSpellServer?
• Or is it just me?
• Or automatic by language is reserved for apple provided languages
only? (If so, they have bug in system preferences, as FooSpellCheck
provided Latvian language shows up there and user is able to select
it as one of langages for "automatic").
• If only New method is implemented then rightclicking misspelled wo
rd, when spelling options set to "Latvian only", doesn't call -spell
Server:suggestGuessesForWord:inLanguage:language, it is called ONLY
when checking spelling through Spelling and Grammar panel (cmd + : )
For OSX built-in languages right-clicking invokes list of possible
corrections. However, when "automatic" is selected, then list is cal
led. D'oh?
As I am going blind in this problem, tried things like changing
registered language id (lv, lv_LV, Latvian, Latviešu). I have tried
to run FooSpellCheck in 3 ways:
Implementing both or only the new or only the old method for
misspeled word search
// New method: Search for a misspelled word in a given string
// Old method: Search for a misspelled word in a given string
Have changed the vendor id and NSSpellChecker in Info.plist, a.o.
"shots in the dark".
Before putting out spellcheck for 10.6. I'd really liked to solve
this issue, or if this cannot be solved :) then I can clearly
comment this problem in my spellchecks documentation.
Any suggestions/directions?
Many thanks in advance,
Reinis Adovics
Code for foo spellchecker:
////////////////////////
// FooSpellCheck.h
////////////////////////
#import <Foundation/Foundation.h>
#import <Foundation/NSSpellServer.h>
@interface FooSpellCheckClass : NSObject <NSSpellServerDelegate> {
NSArray *wordsAray;
}
- (id <NSSpellServerDelegate>)init;
- (void) dealloc;
// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender checkString:
(NSString *)stringToCheck offset:(NSUInteger)offset types:
(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)options
orthography:(NSOrthography *)orthography wordCount:(NSInteger *)
wordCount;
// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender
findMisspelledWordInString:(NSString *)stringToCheck language:
(NSString *)language wordCount:(NSInteger *)wordCount countOnly:
(BOOL)countOnly;
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestCompletionsForPartialWordRange:(NSRange)range inString:
(NSString *)string language:(NSString *)language;
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestGuessesForWord:(NSString *)word inLanguage:(NSString *)
language;
- (void)spellServer:(NSSpellServer *)sender didLearnWord:(NSString *)
word inLanguage:(NSString *)language;
- (void)spellServer:(NSSpellServer *)sender didForgetWord:(NSString
*)word inLanguage:(NSString *)language;
- (BOOL)isWordCorrect:(NSString *)word;
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString:
(NSString *)string language:(NSString *)language details:(NSArray **)
outDetails;
@end
////////////////////////
// FooSpellCheck.m
////////////////////////
#import "FooSpellCheck.h"
@implementation FooSpellCheckClass
- (id <NSSpellServerDelegate>)init
{
self = [super init];
if ( self ) {
// set up array containing all correct Latvian words, to
check against
wordsAray = [NSArray
arrayWithObjects:
@"l
abdien"
,@"kaut",@"kad",@"nezinu",@"nepareizs",@"draugs",@"interese",nil];
}
return self;
}
- (void) dealloc
{
[super dealloc];
}
// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender
checkString:(NSString *)stringToCheck
offset:(NSUInteger)offset
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
orthography:(NSOrthography *)orthography
wordCount:(NSInteger *)wordCount
{
NSLog(@"FooSpellCheck. New (10.6. method) called. String to
check: %@\n",stringToCheck);
NSScanner *stringToCheckScanner = [NSScanner
scannerWithString:stringToCheck]; // create NSScanner object to scan
stringToCheck
NSCharacterSet *wordCharSet = [NSCharacterSet
alphanumericCharacterSet]; // set allowed charsets for words -
letters and numbers
*wordCount = [[stringToCheck componentsSeparatedByString:@" "]
count]; // get number of words in stringToCheck
NSMutableArray *returnArray = [NSMutableArray array]; // create
return array
while (![stringToCheckScanner isAtEnd]) // while scanner is not
at end
{
[stringToCheckScanner scanUpToCharactersFromSet:wordCharSet
intoString:nil]; // scans the string until a character from
wordCharSet character set is encountered, send accumulating
characters into nil
if (![stringToCheckScanner isAtEnd]) // if scanner at this
point is not at end (or characters from the set to be skipped
remaining != TRUE), we have found a word
{
NSString *wordToCheck; //create string object for word
[stringToCheckScanner scanCharactersFromSet:wordCharSet
intoString:&wordToCheck]; // scan the stringToCheck as long as
characters from wordCharSet are encountered
// and accumulate characters into wordToCheck
// if word is in dictionary (wordsAray) or or word is in
user dictionary
if ([self isWordCorrect:wordToCheck] || ([sender
isWordInUserDictionaries:wordToCheck caseSensitive:YES]))
{
continue;
}
else
{
[returnArray addObject:[NSTextCheckingResult
spellCheckingResultWithRange:NSMakeRange(offset +
[stringToCheckScanner scanLocation] - [wordToCheck length],
[wordToCheck length])]];
}
}
}
return returnArray;
}
// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender
findMisspelledWordInString:(NSString *)stringToCheck language:
(NSString *)language wordCount:(NSInteger *)wordCount countOnly:
(BOOL)countOnly
{
NSLog(@"FooSpellCheck. Old (10.4.,5. method) called. String to
check: %@\n",stringToCheck);
NSScanner *stringToCheckScanner = [NSScanner
scannerWithString:stringToCheck]; // create NSScanner object to scan
stringToCheck
NSCharacterSet *wordCharSet = [NSCharacterSet
alphanumericCharacterSet]; // set allowed charsets for words -
letters and numbers
if (!countOnly) { // if !countOnly, then we check spelling
while (![stringToCheckScanner isAtEnd]) // while scanner is
not at end
{
[stringToCheckScanner
scanUpToCharactersFromSet:wordCharSet intoString:nil]; // scans the
string until a character from wordCharSet character set is
encountered, send accumulating characters into nil
if (![stringToCheckScanner isAtEnd]) // if scanner at
this point is not at end (or characters from the set to be skipped
remaining != TRUE), we have found a word
{
NSString *wordToCheck; //create string object for word
[stringToCheckScanner
scanCharactersFromSet:wordCharSet intoString:&wordToCheck]; //
scan the stringToCheck as long as characters from wordCharSet are
encountered
// and accumulate characters into wordToCheck
// if word is in dictionary (wordsAray) or or word is
in user dictionary
if ([self isWordCorrect:wordToCheck] || ([sender
isWordInUserDictionaries:wordToCheck caseSensitive:YES]))
{
continue;
}
else
{
return NSMakeRange ([stringToCheckScanner
scanLocation] - [wordToCheck length], [wordToCheck length]);
}
}
}
}
else
{ // else we count only the words in the string object
if (wordCount) *wordCount = [[stringToCheck
componentsSeparatedByString:@" "] count]; // get number of words in
stringToCheck
}
return NSMakeRange (NSNotFound, 0); // if our scanner failed to
return range, then simply return {0,0} range
}
// Possible word completions, based on a partially completed string
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestCompletionsForPartialWordRange:(NSRange)range inString:
(NSString *)string language:(NSString *)language
{
NSLog(@"FooSpellCheck. Word completions asked for: %@\n",[string
substringWithRange:range]);
// Return a simple array
return [NSArray arrayWithObjects:@"completion1", @"completion2",
@"completion3", @"completion3", nil];
}
// Suggest guesses for the correct spelling of the given misspelled
word
- (NSArray *)spellServer:(NSSpellServer *)sender
suggestGuessesForWord:(NSString *)word inLanguage:(NSString *)language
{
NSLog(@"FooSpellCheck. Word suggestions asked for: %@\n",word);
// Return a simple array
return [NSArray arrayWithObjects:@"suggestion1", @"suggestion2",
@"suggestion3", @"suggestion4", nil];
}
// User has added the specified word to the user’s list of acceptabl
e words in the specified language.
- (void)spellServer:(NSSpellServer *)sender didLearnWord:(NSString *)
word inLanguage:(NSString *)language
{
// do nothing
}
// User has removed the specified word from the user’s list of accep
table words in the specified language
- (void)spellServer:(NSSpellServer *)sender didForgetWord:(NSString
*)word inLanguage:(NSString *)language
{
// do nothing
}
// Search for a grammar in a given string
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString:
(NSString *)string language:(NSString *)language details:(NSArray **)
outDetails
{
// no gramatical issues found
NSArray* myGrammaticalIssues = [NSArray array];
*outDetails = myGrammaticalIssues;
return NSMakeRange (NSNotFound, 0);
}
- (BOOL)isWordCorrect:(NSString *)word
{
for(unsigned int i=0;i<[wordsAray count];i++) {
if ([[wordsAray objectAtIndex:i] caseInsensitiveCompare:word]
== (NSComparisonResult)NSOrderedSame)
return TRUE;
}
return FALSE;
}
@end
////////////////////////
// main.m
////////////////////////
#import <Foundation/Foundation.h>
#import "FooSpellCheck.h"
int main()
{
NSAutoreleasePool *autoreleasepool= [[NSAutoreleasePool alloc]
init];
NSSpellServer *mySpellServer = [[[NSSpellServer alloc] init]
autorelease];
NSLog(@"New NSSpellServer instance starting.\n");
if ([mySpellServer registerLanguage:@"lv" byVendor:@"Apple"]) //
Vendor "Whatever" can be used, NSSpellChecker in info plist has to
be the same as vendor
{
NSLog(@"Latvian language registred.\n");
[mySpellServer setDelegate:[[[FooSpellCheckClass alloc] init]
autorelease]];
NSLog(@"Spell server delegate FooSpellCheck allocated.\n");
[mySpellServer run];
fprintf(stderr, "Unexpected death of spellchecker
FooSpellCheck!\n");
} else {
fprintf(stderr, "NSSpellServer unable to register Latvian
language.\n");
}
[autoreleasepool release];
return 0;
}
////////////////////////
// Info.plist
////////////////////////
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd
">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>FooSpellCheck</string>
<key>CFBundleGetInfoString</key>
<string>FooSpellCheck</string>
<key>CFBundleIconFile</key>
<string>FooSpellCheck.icns</string>
<key>CFBundleIdentifier</key>
<string>my.company.FooSpellCheck</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>FooSpellCheck</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.0</string>
<key>CFBundleSignature</key>
<string>krko</string>
<key>CFBundleVersion</key>
<string>3.0</string>
<key>LSBackgroundOnly</key>
<true/>
<key>NSPrincipalClass</key>
<string>FooSpellCheckClass</string>
<key>NSServices</key>
<array>
<dict>
<key>NSExecutable</key>
<string>FooSpellCheck</string>
<key>NSLanguages</key>
<array>
<string>lv</string>
</array>
<key>NSPortName</key>
<string>FooSpellCheck</string>
<key>NSSpellChecker</key>
<string>Apple</string>
</dict>
</array>
</dict>
</plist>
Compiled service structure:
Compiled using only 10.6 SDK, target os 10.6, Intel 32/64bit
Located at /Library/Services/ (chown root:wheel)
FooSpellCheck.service
|
Contents
|
Info.plist
MacOS
|
FooSpellCheck
restarting lsregister with "lsregister -kill -r -f -domain system -
domain local -domain user"
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
////////////////////////////////////////////////////////////////////
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden