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: MacProjects <email@hidden>
- Date: Fri, 9 Oct 2009 17:38:02 +0300
Just submitted in bug reporter.
Bug ID# 7290111
Problem Report Title: NSSpellServer: OSX 10.6. Error in automatic
language detection.
Product: Mac OSX
Version/Build Number: 10B504
Classification: Serious Bug
Is It Reproducible?: Always
NSSpellServer: OSX 10.6. Error in automatic language detection for any
3rd party spellcheck including OpenSpell
Summary:
After creating a new spelling checker (service) that’s available to
any application and adding it to "Automatic by language" setup in
System Preferences
1) NSSpellServer fails to use this spellchecker (registered language
(s)) when "Automatic by language" spelling is used.
2) When spelling with "Automatic by language" option NSSpellServer
calls only some of the necessary methods to the service.
3) Spellcheck is run only after user selects its registered language
in an applications Spelling options, then user has to switch back for
"Automatic by language".
4) Implementing –
spellServer:checkString:offset:types:options:orthography:wordCount:
invokes unexpected behaviour when right-clicking misspelled word.
Steps to Reproduce:
// Any custom spellcheck
1) Set in System Preferences : Language & Text spelling to "Automatic
by language"
2) Write, build and install a new spellcheck under /Library/Servces
4) Login/out (update dynamic services) for the new service to register.
5) Set the newly registered language delivered by the spell check as
one of the languages for "Automatic by language" in System
Preferences : Language & Text setup list.
6) Open any application that makes use of NSSpellChecker, i.e.,
TextEdit.
7) Write partly correct, partly incorrect text in the new language.
8) Try to spellcheck it. It fails to suggest words in intended language.
9) Look for spellcheck service in currently running processes. It
isn't listed. But it should be run automatically as the registered
language (service) is set in "Automatic by language" list in system
preferences meaning OSX is providing user ability to check for custom
languages/spellchecks that are added to "automatic" list.
10) Open up applications (TextEdit) Edit : Spelling and Grammar : Show
Spelling and Grammar panel.
11) Select the newly registered language in languages list.
12) $ top or Activity Monitor reports that service is finally run.
13) Check text (write it partly correct, partly incorrect) in TextEdit
for spelling issues.
14) Misspelled words are underlined.
15) Suggestions for partial world are shown correctly.
16) If spellcheck service (NSSpellServerDelegate) implements method
a) only –
spellServer:findMisspelledWordInString:language:wordCount:countOnly:
• right-clicking misspelled word gives a list of possible corrections;
• Show Spelling and Grammar panel gives a list of possible corrections;
b) both –
spellServer:findMisspelledWordInString:language:wordCount:countOnly:
and –
spellServer:checkString:offset:types:options:orthography:wordCount:
• right-clicking misspelled word gives a list of possible corrections;
• Show Spelling and Grammar panel gives a list of possible corrections;
c) only –
spellServer:checkString:offset:types:options:orthography:wordCount:
• right-clicking misspelled word DOESN'T give a list of possible
corrections;
• Show Spelling and Grammar panel gives a list of possible corrections;
17) Return to Show Spelling and Grammar panel and select "Automatic
by language" in languages list.
18) Try to check for multiple languages (including the new one) that
are set in Language & Text "Automatic by language" list.
19) The words in the new language are underlined as incorrect, as if
application (NSSpellServer) couldn't detect the right language.
20) Debugging/ logging the new spellcheck service shows that neither –
spellServer:findMisspelledWordInString:language:wordCount:countOnly:
or –
spellServer:checkString:offset:types:options:orthography:wordCount:
methods are called to NSSpellServer delegate at this point.
20) Right-click (or using Spelling and Grammar panel) for possible
corrections for words (both - correct and incorrect ones, it doesn't
matter as all are underlined) written in the new language.
21) The corrections that show up are supplied by the right spellcheck
service - the new one
22) Debugging/ logging the new spellcheck service shows that –
spellServer:suggestGuessesForWord:inLanguage: method IS called to
NSSpellServer delegate at this point.
20) Press esc key for possible completions for words (both - correct
and incorrect ones, it doesn't matter as all are underlined) written
in the new language.
21) The completions that show up are supplied by the right spellcheck
service - the new one
22) Debugging/ logging the new spellcheck service shows that –
spellServer:suggestCompletionsForPartialWordRange:inString:language:
method IS called to NSSpellServer delegate at this point.
---
// OpenSpell.service
23) Download a dic and aff file for some hunspell dictionary
24) Put it under ~/Library/Spelling
25) Set the new hunspell language (which is accordingly registered by
OpenSpell.servce delivered by Apple) as one of the languages for
"Automatic by language" in System Preferences : Language & Text setup
list.
26) Repeat steps 6 - 16. OpenSpell also doesn't automatically launch
itself in "Automatic by language" situation, even when its delegated
languages are put in "Automatic by language" setup list, as well as it
fails to bring usability in "Automatic by language" situation in
general.
Expected Results:
After installing spelling checker in system and setting its registered
language(s) as one for "Automatic by language"
1) spell checker service should launch (if not) on opening any
application that makes use of NSSpellChecker so that user instantly
could check spelling for mix of languages
2) NSSpellServer should completely detect what language is used,
meaning, it correctly underlines misspelled words, gives completions,
suggestions.
2) user should be able to check spelling, get suggestions, completions
a.o. expected results when using spellcheck.
Actual Results:
See together with Steps to Reproduce
After installing spelling checker in system and setting its registered
language(s) as one for "Automatic by language"
1) spell checker service did not launch on opening any application
that makes use of NSSpellChecker. User has to go to Show Spelling and
Grammar panel, select desired language so that service launches, then
again select "Automatic by language".
2) In "Automatic by language" environment even correct words for the
new language are underlined as –
spellServer:findMisspelledWordInString:language:wordCount:countOnly:
or –
spellServer:checkString:offset:types:options:orthography:wordCount:
are not called, NSSpellServer fails to address the right service.
3) However clicking those underlined words suggestions, completions
are provided from the right spelling checker, that is, NSSpellServer
addresses the right service.
4) If the "new" method introduced in 10.6 –
spellServer:checkString:offset:types:options:orthography:wordCount: is
called instead of "old" one –
spellServer:findMisspelledWordInString:language:wordCount:countOnly:
then suggestions for completions (-
spellServer:suggestGuessesForWord:word inLanguage:) IS NOT called when
right-clicking misspelled word. Suggestions however show up in how
Spelling and Grammar panel.
5) That means that the developer that "must" include -
spellServer:suggestGuessesForWord:word inLanguage: method to bring
this f-ction to users cannot use the "new" method!
That could be avoided in future by altering –
spellServer:checkString:offset:types:options:orthography:wordCount:
The return for this method could hold an NSTextCheckingResult by some
type "a la" NSTextCheckingTypeSuggestions which could have constructor
+ (NSTextCheckingResult *)spellCheckingResultWithRange:(NSRange)range
forSuggestions:(NSArray *)suggestions
Regression:
Tested on multiple macs (including intel core duo, intel core 2 duo,
macbook, macbookpro, imac) running OSX 10.6.1. both after clean
install and upgrade install from 10.6.
Spell checker built for OS 10.6.x only, using 10.6 SDK.
Notes:
Adding source for FooSpellChecker.source that registers Latvian
language with dictionary ("hard coded") containing several words to
reproduce the behaviour described above
////////////////////////
// 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 (and key for NSSpellChecker in info.plist) "Whatever" can be
used, don't help solving problem {
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;
}
////////////////////////
// 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 dictionary for the language, 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. Unite (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
////////////////////////
// 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></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>
///////////
Attaching source files in zip file.
Regards and hoping for the best,
Reinis Adovics
On 09.10.2009., at 06:35, Douglas Davidson wrote:
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.
_______________________________________________
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