Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
- Subject: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
- From: MacProjects <email@hidden>
- Date: Thu, 8 Oct 2009 23:35:17 +0300
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
word, when spelling options set to "Latvian only", doesn't call -
spellServer: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 called. 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
:@"labdien
",@"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 acceptable
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
acceptable 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