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, 16 Oct 2009 04:47:56 +0300
Dear Mr. Davidson / to whom it may concern:
Just had a little time to work on the project/problem (hope I'm not
the only one) and found out that:
Steps to reproduce pt. 16 and Actual Results pt. 4 should sound now:
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.
=> HOWEVER This is valid to only some applications (tested on 4 Apple
apps)
I.e.,
In Apple provided TextEdit.app, Stickies.app the behaviour is as
described above. Thus meaning that error is in "client
side" (NSSpellChecker implementation in those apps).
In Mail.app and Safari.app the suggestions are delivered on ctrl+click
even when only
spellServer:checkString:offset:types:options:orthography:wordCount:
is implemented leaving "old method" out.
Resumē:
Yes, the old method should be fully implemented anyway, as such
relatively wide used apps as OOo3, NeoO3 that takes advance of syswide
spellcheck, are calling old method.
Just want to point out the inconsistency of Apple apps in 10.6., where
the behaviour cannot be expected/predicted. Developing a spellserver
for osx means primary verifying that it works on systems bundled
software!, then iwork, iweb packages and then 3rd parties, and doing
so by following sdk documentation. As it seems, somebody doesn't
follow sdk documentation that has been written by "himself"... Exactly
this point of the story is the most frustrating.
Should I file bugreports against TextEdit, Stickies and who-nows-how-
more osx bundled apps? I understand that ~90% of osx users doesn't
have any relevance to this topic - there is no bug - as they belong to
the group of ApleSpell.service registred languages (my guess). The
other 9% are using probably OpenSpell on 10.6 now and do not cry that
automatic language option is not working (yet). The left 1% will be
affected by those couple custom spellchecker developers for osx out
there, that are/will face the issues. Before 10.6. (10.4&5) custom
spellserver was the only way for Latvian a.o. languages; now in 10.6.
I'm willing to extend it with numerous features, that again would
bring better and more specific solution for the language than OSX
provided "out of box" OpenSpell... please, allow me. :))
Many thanks in advance,
Reinis Adovics
On 09.10.2009., at 17:38, MacProjects wrote:
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
_______________________________________________
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