'From etoys2.2 of 27 September 2007 [latest update: #1715] on 19 October 2007 at 4:46:41 pm'! "Change Set: cleanupMOSupport1-tak Date: 19 October 2007 Author: Takashi Yamamiya The first path to cleanup NaturalLanguageTranslator structure. - remove NaturalLanguageTranslatorWrapper. - rename NaturalLanguageTranslator to InternalTranslator. - remove an inst var named 'id' in InternalTranslator. - rename a class var named 'CachedTranslators' to 'Translators'. "! TestCase subclass: #GetTextTranslatorTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'System-Localization'! Smalltalk renameClassNamed: #NaturalLanguageTranslator as: #InternalTranslator! Object subclass: #InternalTranslator instanceVariableNames: 'id generics contexts ' classVariableNames: 'AllKnownPhrases CachedTranslations ' poolDictionaries: '' category: 'System-Localization'! !InternalTranslator commentStamp: 'tak 10/19/2007 11:12' prior: 0! An InternalTranslator is used a translation dictionary in a image. You can use it without any external translation file. Structure: generics Dictionary -- msgid -> msgstr ! TestCase subclass: #LanguageEditorTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Multilingual-Editor'! Object subclass: #MOFile instanceVariableNames: 'localeID fileName isLittleEndian magic revision nStrings originalTableOffset translatedTableOffset hashTableSize hashTableOffset hashTable originalStrings translatedStrings translations' classVariableNames: '' poolDictionaries: '' category: 'System-Localization'! Object subclass: #NaturalLanguageTranslator2 instanceVariableNames: 'id ' classVariableNames: 'CachedTranslators Initialized Translators ' poolDictionaries: '' category: 'System-Localization'! NaturalLanguageTranslator2 subclass: #GetTextTranslator instanceVariableNames: 'moFiles' classVariableNames: 'LocaleDirsForDomain UserDefaultLocaleDirs' poolDictionaries: '' category: 'System-Localization'! !ChangeSet methodsFor: 'fileIn/Out' stamp: 'tak 10/3/2007 13:32'! checkForSUnit | testClasses changeTypes | testClasses := self changedClasses select: [:each | each inheritsFrom: TestCase]. ^ testClasses select: [:each | changeTypes := self classChangeAt: each name. ((changeTypes includes: #add) or: [changeTypes includes: #change]) not]! ! !ChangeSet methodsFor: 'fileIn/Out' stamp: 'tak 10/3/2007 13:36'! fileOut "File out the receiver, to a file whose name is a function of the change-set name and either of the date & time or chosen to have a unique numeric tag, depending on the preference 'changeSetVersionNumbers'" | slips nameToUse internalStream | self checkForConversionMethods. ChangeSet promptForDefaultChangeSetDirectoryIfNecessary. nameToUse := Preferences changeSetVersionNumbers ifTrue: [self defaultChangeSetDirectory nextNameFor: self name extension: FileStream cs] ifFalse: [self name , FileDirectory dot , Utilities dateTimeSuffix, FileDirectory dot , FileStream cs]. (Preferences warningForMacOSFileNameLength and: [nameToUse size > 30]) ifTrue: [nameToUse := FillInTheBlank request: (nameToUse , '\has ' , nameToUse size asString , ' letters - too long for Mac OS.\Suggested replacement is:') withCRs initialAnswer: (nameToUse contractTo: 30). nameToUse = '' ifTrue: [^ self]]. nameToUse := self defaultChangeSetDirectory fullNameFor: nameToUse. Cursor write showWhile: [ internalStream _ WriteStream on: (String new: 10000). internalStream header; timeStamp. self fileOutPreambleOn: internalStream. self fileOutOn: internalStream. self fileOutPostscriptOn: internalStream. internalStream trailer. FileStream writeSourceCodeFrom: internalStream baseName: (nameToUse copyFrom: 1 to: nameToUse size - 3) isSt: false useHtml: false. ]. Preferences checkForSlips ifFalse: [^ self]. slips := self checkForSlips. (slips size > 0 and: [(PopUpMenu withCaption: 'Methods in this fileOut have halts or references to the Transcript or other ''slips'' in them. Would you like to browse them?' chooseFrom: 'Ignore\Browse slips') = 2]) ifTrue: [self systemNavigation browseMessageList: slips name: 'Possible slips in ' , name]. self checkForSUnit isEmpty not ifTrue: [self inform: 'This fileOut includes unit test but not its class definition: ', self checkForSUnit asArray printString].! ! !GetTextExporter2 class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! coverageStatus "self coverageStatus" | keys diff | keys := self keys. diff := InternalTranslator allKnownPhrases keys difference: keys. Transcript cr; show: 'Detected keywords by GetTextExporter2: ' , keys size printString. Transcript cr; show: 'All known phrases in InternalTranslator: ' , InternalTranslator allKnownPhrases size printString. Transcript cr; show: 'Coverage: ' , (keys size / InternalTranslator allKnownPhrases size * 100.0) printString , '%'. diff inspect! ! !GetTextExporter2 class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! exportAll "GetTextExporter2 exportAll" self new exportTemplate. InternalTranslator availableLanguageLocaleIDs do: [:each | self new exportTranslator: each translator]! ! !GetTextExporter2 class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! verifyExport "Same as #verifyMsgID: but it writes / reads .po files actually" "GetTextExporter2 verifyExport" "InternalTranslator removeLocaleID: (LocaleID isoString: 'test-US')" | src dst localeID | localeID := LocaleID isoString: 'test-US'. self verifyMsgID: localeID. src := localeID translator. GetTextExporter2 new exportTranslator: src. InternalTranslator removeLocaleID: localeID. dst := localeID translator. GetTextImporter import: dst allDirectory: FileDirectory default! ! !GetTextExporter2 class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! verifyMsgID: localeID "GetTextExporter2 verifyMsgID: (LocaleID isoString: 'test-US')" "InternalTranslator removeLocaleID: (LocaleID isoString: 'test-US')" "Test gettext keyword extract function without file I/O. A language named will be made. And all possible translated words are shown with extra X charactor like 'XwordX' in the language." | src | InternalTranslator removeLocaleID: localeID. src := localeID translator. self keys do: [:key | src generics at: key put: 'X' , key , 'X']! ! !GetTextImporter methodsFor: 'parsing' stamp: 'tak 10/18/2007 21:15'! storeTranslation | key | key := self formatString: msgId. msgId isEmpty ifFalse: [InternalTranslator registerPhrase: key. msgStr isEmpty ifFalse: [language rawPhrase: key translation: (self formatString: msgStr)]]. self initialize! ! !GetTextImporter class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! cleanUpUnnecessaryPhrases | keys refuse replaceBlock reader writer char result | "GetTextImporter cleanUpUnnecessaryPhrases" "" "Collect wrong phrases" keys := InternalTranslator allKnownPhrases copy keys. refuse := Set new. "replaceBlock value: 'te\\nst'." replaceBlock := [:aString | reader := aString readStream. writer := '' writeStream. [reader atEnd] whileFalse: [char := reader next. (char = $\ and: [reader peek = $\]) ifFalse: [writer nextPut: char]]. writer contents]. keys do: [:each | result := replaceBlock value: each. (result ~= each and: [keys includes: result]) ifTrue: [refuse add: each]. result := GetTextImporter new formatString: each. (result ~= each and: [keys includes: result]) ifTrue: [refuse add: each]]. "" "Remove from translated" InternalTranslator cachedTranslations do: [:each | refuse do: [:key | each translations removeKey: key ifAbsent: []]]. "" "Remove from untranslated" refuse do: [:key | InternalTranslator allKnownPhrases removeKey: key ifAbsent: []]! ! !GetTextImporter class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:14'! importAll "GetTextImporter importAll" "Import all gettext files on po/. Only registered language is imported" InternalTranslator cachedTranslations do: [:translator | self import: translator allDirectory: FileDirectory default]! ! !GetTextTranslatorTest methodsFor: 'testing' stamp: 'tak 10/18/2007 21:15'! testTranslateAll | translator legacy trn1 trn2| GetTextTranslator availableLocaleIDs do: [:localeID | Transcript show: 'switching to ', (localeID isoString); cr. translator _ GetTextTranslator newForLocaleID: localeID. legacy _ InternalTranslator localeID: localeID. legacy translations keys do: [:each | trn1 _ translator translate: each. trn2 _ legacy translate: each. trn1 = each ifFalse: [ "skip phrases not translated yet" "ignore enclosing blanks for now" trn1 _ trn1 withBlanksTrimmed. trn2 _ trn2 withBlanksTrimmed. self assert: (trn1 = trn2). ]. ]. ].! ! !InternalTranslator methodsFor: 'accessing' stamp: 'tak 10/19/2007 13:14'! localeID ^id ifNil: [LocaleID isoString: 'en']! ! !InternalTranslator class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:20'! mergeLegacyTranslators self availableLanguageLocaleIDs do: [:localeID | (NaturalLanguageTranslator2 translators includesKey: localeID) ifFalse: [NaturalLanguageTranslator2 translators at: localeID put: (self newLocaleID: localeID)]]! ! !InternalTranslator class methodsFor: 'class initialization' stamp: 'tak 10/18/2007 21:14'! initialize "InternalTranslator initialize" FileList registerFileReader: self. Smalltalk addToStartUpList: InternalTranslator after: FileDirectory. ! ! !LanguageEditor methodsFor: 'gui methods' stamp: 'tak 10/18/2007 21:15'! newTranslations "private - try to apply the translations as much as possible all over the image" | result newID | result := FillInTheBlank request: 'New locale ID string?' initialAnswer: Locale current determineLocaleID isoString. result isEmpty ifTrue: ["Do nothing" ^ self]. newID := LocaleID isoString: result. InternalTranslator newLocaleID: (LocaleID isoString: result). self class openOn: newID! ! !LanguageEditor class methodsFor: 'instance creation' stamp: 'tak 10/18/2007 21:13'! on: localeID "answer an instance of the receiver on aLanguage" (InternalTranslator availableLanguageLocaleIDs includes: localeID) ifFalse: [self error: ('Translator for {1} is not found' translated format: {localeID})]. ^ self new initializeOn: (InternalTranslator availableForLocaleID: localeID)! ! !LanguageEditor class methodsFor: 'opening' stamp: 'tak 10/18/2007 21:13'! open "open the receiver on any language" " LanguageEditor open. " | menu | menu := MenuMorph new defaultTarget: self. menu addTitle: 'Language Editor for...' translated. "" (InternalTranslator availableLanguageLocaleIDs asSortedCollection: [:x :y | x asString <= y asString]) do: [:eachLanguage | "" menu add: eachLanguage name target: self selector: #openOn: argument: eachLanguage]. "" menu popUpInWorld! ! !LanguageEditorTest methodsFor: 'testing' stamp: 'tak 10/18/2007 21:15'! testFileName "self debug: #testFileName" | exporter | exporter := GetTextExporter new. exporter language: InternalTranslator default. self assert: exporter defaultFileName = 'en.po'! ! !LanguageEditorTest methodsFor: 'running' stamp: 'tak 10/18/2007 21:15'! tearDown InternalTranslator removeLocaleID: (LocaleID isoString: self idString)! ! !LanguageEditorTest methodsFor: 'running' stamp: 'tak 10/18/2007 21:15'! translator ^ InternalTranslator newLocaleID: (LocaleID isoString: self idString)! ! !LanguageEditorTest class methodsFor: 'utilities' stamp: 'tak 10/18/2007 21:13'! verifyExportImport "LanguageEditorTest verifyExportImport" | isoName resultName original result | isoName := 'es'. resultName := isoName , '_test'. original := (LocaleID isoString: isoName) translator. GetTextExporter new export: original. FileDirectory default rename: isoName , '.po' toBe: resultName , '.po'. [result := InternalTranslator newLocaleID: (LocaleID isoString: resultName). GetTextImporter new import: result. self assert: original translations = result translations] ensure: [InternalTranslator removeLocaleID: (LocaleID isoString: resultName)]! ! !Locale class methodsFor: 'accessing' stamp: 'tak 10/18/2007 21:51'! switchTo: locale gently: gentlyFlag "Locale switchTo: (Locale isoLanguage: 'de')" | availableID | availableID := (NaturalLanguageTranslator2 availableForLocaleID: locale localeID) localeID. Current localeID = availableID ifFalse: [Previous _ Current. CurrentPlatform := Current := Locale localeID: availableID. gentlyFlag ifTrue: [self localeChangedGently] ifFalse: [self localeChanged]]! ! !Locale class methodsFor: 'private' stamp: 'tak 10/18/2007 21:14'! initKnownLocales | locales | locales := Dictionary new. "Init the locales for which we have translations" InternalTranslator availableLanguageLocaleIDs do: [:id | locales at: id put: (Locale new localeID: id)]. ^locales! ! !LocaleID methodsFor: 'accessing' stamp: 'tak 10/18/2007 21:15'! translator ^ InternalTranslator localeID: self ! ! !MOFile methodsFor: 'experimental' stamp: 'tak 10/18/2007 21:15'! testSearchByDictionary InternalTranslator allKnownPhrases do: [:each | self searchByDictionary: each ]. ! ! !MOFile methodsFor: 'experimental' stamp: 'tak 10/18/2007 21:15'! testSearchByHash InternalTranslator allKnownPhrases do: [:each | self searchByHash: each ]. ! ! !NaturalLanguageTranslator2 class methodsFor: 'class initialization' stamp: 'tak 10/18/2007 21:14'! initialize Smalltalk addToStartUpList: NaturalLanguageTranslator2 after: InternalTranslator. ! ! !NaturalLanguageTranslator2 class methodsFor: 'class initialization' stamp: 'tak 10/18/2007 21:35'! startUp: resuming resuming ifFalse: [^ self]. self resetCaches. GetTextTranslator loadAvailableTranslators. InternalTranslator mergeLegacyTranslators. self localeChanged: LocaleID current.! ! !NaturalLanguageTranslator2 class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:20'! availableForLocaleID: localeID "Answer available locale ID. If translator is not found for correct locale ID, then isoLanguage is attempted for the key." ^ self translators at: localeID ifAbsent: [localeID hasParent ifTrue: [self translators at: localeID parent ifAbsent: [self default]] ifFalse: [self default]]! ! !NaturalLanguageTranslator2 class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:22'! availableLanguageLocaleIDs "Return the locale ids for the currently available languages. Meaning those which either internally or externally have translations available." "NaturalLanguageTranslator2 availableLanguageLocaleIDs" ^ self translators values collect:[:each | each localeID]! ! !NaturalLanguageTranslator2 class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:21'! removeLocaleID: localeID "self removeLocaleID: (LocaleID isoString: 'ja-kids')" ^ self translators removeKey: localeID ifAbsent: []! ! !NaturalLanguageTranslator2 class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:18'! resetCaches Translators := nil.! ! !NaturalLanguageTranslator2 class methodsFor: 'accessing' stamp: 'tak 10/19/2007 11:15'! translators ^ Translators ifNil: [Translators := Dictionary new] ! ! !GetTextTranslator class methodsFor: 'accessing translator' stamp: 'tak 10/19/2007 11:19'! loadAvailableTranslators self availableLocaleIDs do: [ :localeID | NaturalLanguageTranslator2 translators at: localeID put: (self newForLocaleID: localeID). ]! ! !SMLanguageInstaller methodsFor: 'services' stamp: 'tak 10/18/2007 21:15'! install "This service should bring the package to the client, unpack it if necessary and install it into the image. The package is notified of the installation." self cache; unpack. [InternalTranslator mergeTranslationFileNamed: unpackedFileName] ensure: [packageRelease noteInstalled]! ! NaturalLanguageTranslator2 class removeSelector: #cachedTranslators! NaturalLanguageTranslator2 class removeSelector: #importLegacyTranslators! NaturalLanguageTranslator2 initialize! NaturalLanguageTranslator2 class removeSelector: #initialized! Object subclass: #NaturalLanguageTranslator2 instanceVariableNames: 'id' classVariableNames: 'Translators' poolDictionaries: '' category: 'System-Localization'! InternalTranslator initialize! Object subclass: #InternalTranslator instanceVariableNames: 'generics' classVariableNames: 'AllKnownPhrases CachedTranslations' poolDictionaries: '' category: 'System-Localization'! Smalltalk removeClassNamed: #NaturalLanguageTranslatorWrapper!