• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


  • Follow-Ups:
    • Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
      • From: MacProjects <email@hidden>
References: 
 >Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer. (From: MacProjects <email@hidden>)

  • Prev by Date: Re: CoreData async fetch request
  • Next by Date: [iPhone} Exception After Creating First Object
  • Previous by thread: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
  • Next by thread: Re: Automatic language detection - a bug (again) or what. Snow Foundation NSSpellServer.
  • Index(es):
    • Date
    • Thread