[squeak-dev] Squeak 4.5: GetText-nice.34.mcz

commits at source.squeak.org commits at source.squeak.org
Fri Jan 24 20:02:03 UTC 2014


Chris Muller uploaded a new version of GetText to project Squeak 4.5:
http://source.squeak.org/squeak45/GetText-nice.34.mcz

==================== Summary ====================

Name: GetText-nice.34
Author: nice
Time: 25 December 2013, 11:02:39.404 pm
UUID: 4d432f8e-55be-428a-9138-63dd1738035e
Ancestors: GetText-fbs.33

Change a few print:(aFloat roundTo: 0.01) into nextPutAll:(aFloat printShowingMaxDecimalPlaces: 2)

==================== Snapshot ====================

SystemOrganization addCategory: #'GetText-Editor'!

Object subclass: #GetTextExporter
	instanceVariableNames: 'stream'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GetText-Editor'!

!GetTextExporter commentStamp: '<historical>' prior: 0!
Export translations to gettext format divided into categories.

"Export gettext template files"
GetTextExporter new exportTemplate.

"Export translation files for current locale"
GetTextExporter new exportTranslator: (InternalTranslator newLocaleID: LocaleID current).

"Export all gettext template and po files."
GetTextExporter exportAll.

!

----- Method: GetTextExporter class>>coverageStatus (in category 'utilities') -----
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!

----- Method: GetTextExporter class>>exportAll (in category 'utilities') -----
exportAll
	"GetTextExporter2 exportAll"
	self new exportTemplate.
	InternalTranslator availableLanguageLocaleIDs
		do: [:each | self new exportTranslator: each translator]!

----- Method: GetTextExporter class>>exportTemplate (in category 'utilities') -----
exportTemplate
	"GetTextExporter2 exportTemplate"
	self new exportTemplate.!

----- Method: GetTextExporter class>>keys (in category 'utilities') -----
keys
	| categories |
	categories := Dictionary new.
	self new appendTranslations: categories.
	^ categories values
		inject: Set new
		into: [:set :next | set addAll: next keys;
				 yourself]!

----- Method: GetTextExporter class>>listAllHelp (in category 'utilities') -----
listAllHelp
	"self listAllHelp"
	| spec specs oCatalog flap flapSelectors allKeys oCatalogHelp flapHelp |
	oCatalog := Dictionary new.
	Morph withAllSubclasses
		do: [:aClass | (aClass class includesSelector: #descriptionForPartsBin)
				ifTrue: [spec := aClass descriptionForPartsBin.
					oCatalog at: spec formalName put: spec documentation]].
	Morph withAllSubclasses
		do: [:aClass | (aClass class includesSelector: #supplementaryPartsDescriptions)
				ifTrue: [specs := aClass supplementaryPartsDescriptions.
					specs
						do: [:each | oCatalog at: each formalName put: each documentation]]].
	flap := Dictionary new.
	flapSelectors := #(#defaultsQuadsDefiningPlugInSuppliesFlap #defaultsQuadsDefiningStackToolsFlap #defaultsQuadsDefiningSuppliesFlap #defaultsQuadsDefiningToolsFlap #defaultsQuadsDefiningWidgetsFlap #defaultsQuadsDefiningScriptingFlap ).
	flapSelectors
		do: [:selector | 
			specs := Flaps perform: selector.
			specs
				do: [:each | flap at: each third put: each fourth]].
	allKeys := oCatalog keys intersection: flap keys.
	allKeys asArray sort
		do: [:each | 
			oCatalogHelp := oCatalog
						at: each
						ifAbsent: [''].
			flapHelp := flap
						at: each
						ifAbsent: [''].
			oCatalogHelp = flapHelp
				ifFalse: [Transcript cr; show: 'Name: ' , each.
					Transcript cr; show: 'O: ' , oCatalogHelp.
					Transcript cr; show: 'F: ' , flapHelp.
					Transcript cr.

]]!

----- Method: GetTextExporter class>>verifyExport (in category 'utilities') -----
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.
	self new exportTranslator: src.
	InternalTranslator removeLocaleID: localeID.
	dst := localeID translator.
	GetTextImporter import: dst allDirectory: FileDirectory default!

----- Method: GetTextExporter class>>verifyMsgID: (in category 'utilities') -----
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 <langName> 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']!

----- Method: GetTextExporter>>appendStringReceivers:into: (in category 'private') -----
appendStringReceivers: aSymbol into: domains
	| literals references domainName methodReference keywords found |
	
	found := TranslatedReceiverFinder new stringReceiversWithContext: aSymbol.
	found do: [ :assoc |
		methodReference := assoc key.
		keywords := assoc value.
		domainName := (PackageOrganizer default packageOfMethod: methodReference ifNone: [nil]).
		domainName := domainName isNil ifTrue: [TextDomainManager defaultDomain] ifFalse: [domainName name].
		literals := domains at: domainName ifAbsentPut: [Dictionary new].
		keywords do: [ :literal |
			references := literals at: literal ifAbsentPut: [OrderedCollection new].
			references add: methodReference.
		].
	]. 

!

----- Method: GetTextExporter>>appendTranslations: (in category 'exporting') -----
appendTranslations: domains 
	self appendStringReceivers: #translated into: domains.
	self appendStringReceivers: #translatedNoop into: domains.
	self appendVocabularies: domains.
!

----- Method: GetTextExporter>>appendVocabularies: (in category 'private') -----
appendVocabularies: domains
	| literalsForDomain references domainName methodReference |
	
	EToyVocabulary allPhrasesWithContextToTranslate do: [ :r |
		methodReference :=  (MethodReference class: (r second) selector: (r third)).
		"domainName := self getTextDomainForPackage: (PackageOrganizer default packageOfMethod: methodReference)".
		domainName := 'Etoys-Tiles'.
		literalsForDomain := domains at: domainName ifAbsentPut: [Dictionary new].
		r fourth do: [ :literal |
			references := literalsForDomain at: literal ifAbsentPut: [OrderedCollection new].
			references add: methodReference.
		].
	]. 
	!

----- Method: GetTextExporter>>createExtraInformation (in category 'private') -----
createExtraInformation
	| extras |
	extras := OrderedCollection new.
	#(
		'Language name as you''d like it to appear in the Languages menu' 'Language-Name'
		'Directionality of language' 'Language-Direction'
		) pairsDo: [:first :second |
			extras add: (Array with: '' with: first with: second).
	].
	^ extras!

----- Method: GetTextExporter>>createHeaders (in category 'private-headers') -----
createHeaders
	| headers |
	headers := OrderedCollection new.
	headers add: 'Project-Id-Version' -> 'eToys'.
	headers add: 'POT-Creation-Date' -> self currentDateAndTime.
	headers add: 'PO-Revision-Date' -> self currentDateAndTime.
	headers add: 'Last-Translator' -> ''.
	headers add: 'Language-Team' -> ''.
	headers add: 'MIME-Version' -> '1.0'.
	headers add: 'Content-Type' -> ('text/plain; charset=', stream converter class encodingNames first).
	headers add: 'Content-Transfer-Encoding' -> '8bit'.
	^ headers!

----- Method: GetTextExporter>>currentDateAndTime (in category 'private') -----
currentDateAndTime
	^ String
		streamContents: [:aStream | 
			aStream nextPutAll: Date today yyyymmdd;
				space.
			Time now
				print24: true
				showSeconds: false
				on: aStream.
			aStream nextPutAll: '-0000']!

----- Method: GetTextExporter>>dirNameCategory:translator: (in category 'exporting') -----
dirNameCategory: category translator: translator 
	"Answer a file name for the category. Make one if it is not exist yet.
	Make template file name if translator is nil"
	"self new dirNameCategory: 'Morphic-Scripting Support' translator:
	NaturalLanguageTranslator current"
	"self new dirNameCategory: 'Morphic-Scripting Support' translator: nil"
	| safeCategory fileName dirName pathName |
	safeCategory := category copyReplaceAll: ' ' with: ':='.
	fileName := translator
				ifNil: [safeCategory , '.pot']
				ifNotNil: [translator localeID posixName , '.po'].
	dirName := (safeCategory findTokens: '-')
				inject: 'po'
				into: [:aString :next | aString , FileDirectory slash , next].
	pathName := dirName , FileDirectory slash , fileName.
	(FileDirectory default directoryNamed: dirName) assureExistence.
	^ pathName!

----- Method: GetTextExporter>>dirNameDomain:translator: (in category 'exporting') -----
dirNameDomain: domain translator: translator 
	"Answer a file name for the domain. Make one if it is not exist yet.
	Make template file name if translator is nil"
	"self new dirNameDomain: 'etoys' translator:
	NaturalLanguageTranslator current"
	"self new dirNameDomain: 'etoys' translator: nil"
	| fileName dirName pathName |
	"safeCategory := category copyReplaceAll: ' ' with: ':='."
	fileName := domain, 
						(translator
							ifNil: ['.pot']
							ifNotNil: ['.po']).
	dirName := 'po', FileDirectory slash, 
								(translator 
										ifNil: ['templates'] 
										ifNotNil: [translator localeID posixName]). 
	pathName := dirName , FileDirectory slash , fileName.
	(FileDirectory default directoryNamed: dirName) assureExistence.
	^ pathName!

----- Method: GetTextExporter>>export:translator:domain: (in category 'private') -----
export: literals translator: translator domain: domainName 
	| fileName |
	"Export a gettext file in a category. literals is a dictionary of keyword -> #(MethodReference...) in the textDomain."
	fileName := self dirNameDomain: domainName translator: translator.
	[stream := FileStream forceNewFileNamed: fileName.
	stream lineEndConvention: #lf.
	stream converter: UTF8TextConverter new.
	self exportHeader: domainName.
	domainName = TextDomainManager defaultDomain
	ifTrue: [self exportInformation: self createExtraInformation].
	self exportBody: literals translator: translator]
		ensure: [stream close]!

----- Method: GetTextExporter>>exportBody:translator: (in category 'file out') -----
exportBody: literals translator: translator 
	"Export a gettext file body. literals is a dictionary of keyword ->
	#(MethodReference...) in the textDomain."
	"Build {sortKey. comment. msgid } to optimize sorting (getting category is
	too slow).
	If there are two or more methods for a mgsid, only first method
	(alphabetical) is used for sorting."
	| sorted msgid sortedMethods category sortKey comment triplets commentUnderLined |
	triplets := literals associations
				collect: [:assoc | 
					msgid := assoc key.
					sortedMethods := assoc value asArray sort.
					category := (Smalltalk at: sortedMethods first classSymbol) category asString.
					sortKey := category , ',' , sortedMethods first printString , ',' , msgid.
					comment := (sortedMethods
								collect: [:each | each actualClass asString , '>>' , each methodSymbol asString])
								inject: category
								into: [:result :methodName | result , ',' , methodName].
					"Replace white spaces to := because gettext tool might
					replace a space to a new line some times, and it makes
					difficult to take a diff."
					commentUnderLined := comment copyReplaceAll: ' ' with: ':='.
					Array
						with: sortKey
						with: commentUnderLined
						with: msgid].
	"Sort and output the words"
	sorted := triplets
				sort: [:a :b | a first <= b first].
	sorted
		do: [:triplet | 
			comment := triplet second.
			msgid := triplet third.
			self exportRecordHeader: comment.
			self
				exportPhrase: msgid
				translation: (self translationFor: msgid in: translator)]!

----- Method: GetTextExporter>>exportHeader (in category 'private-headers') -----
exportHeader
	self exportTag: 'msgid' msg: ''.
	self exportTag: 'msgstr' msg: ''.
	self createHeaders
		do: [:each | self exportHeaderLineKey: each key value: each value].
	stream cr; cr!

----- Method: GetTextExporter>>exportHeader: (in category 'private') -----
exportHeader: domainName
	| headers |
	self exportTag: 'msgid' msg: ''.
	self exportTag: 'msgstr' msg: ''.
	headers := self createHeaders.
	headers add: 'X-Etoys-Domain' -> domainName.
	headers do: [:each | self exportHeaderLineKey: each key value: each value].
	stream cr; cr!

----- Method: GetTextExporter>>exportHeaderLineKey:value: (in category 'private') -----
exportHeaderLineKey: keyString value: valueString 
	stream nextPut: $";
		 nextPutAll: keyString;
		 nextPut: $:;
		 space;
		 nextPutAll: valueString;
		 nextPutAll: '\n';
		 nextPut: $";
		 cr.!

----- Method: GetTextExporter>>exportInformation: (in category 'private') -----
exportInformation: anOrderedCollection
	anOrderedCollection do: [:each |
		self exportRecordHeader: each second.
		self exportPhrase: each third translation: ''].
	stream cr.!

----- Method: GetTextExporter>>exportPhrase:translation: (in category 'private') -----
exportPhrase: phraseString translation: translationString 
	| normalizedTrans tmp transStartsWithCR transEndsWithCR|
	phraseString isEmpty
		ifTrue: [^ self].
	self exportTag: 'msgid' msg: phraseString.
	translationString size = 0 ifTrue: [
		normalizedTrans := ''
	] ifFalse: [
		transEndsWithCR := translationString last = (Character cr).
		phraseString last = (Character cr) ifTrue: [
			transEndsWithCR ifTrue: [
				normalizedTrans := translationString
			] ifFalse: [
				normalizedTrans :=  translationString , String cr
			]
		] ifFalse: [
			transEndsWithCR ifTrue: [
				normalizedTrans := translationString allButLast
			] ifFalse: [
				normalizedTrans := translationString
			]
		].
		transStartsWithCR := normalizedTrans first = (Character cr).
		phraseString first = (Character cr) ifTrue: [
			transStartsWithCR ifFalse: [
				tmp := (Character cr asString) , normalizedTrans.
				normalizedTrans := tmp.
			]
		] ifFalse: [
			transStartsWithCR ifTrue: [
				normalizedTrans := normalizedTrans allButFirst
			]
		]
	].
	self exportTag: 'msgstr' msg: normalizedTrans.
	stream cr!

----- Method: GetTextExporter>>exportRecordHeader: (in category 'private') -----
exportRecordHeader: context
	stream 
		nextPutAll: '#: ';
		nextPutAll: context;
		cr.!

----- Method: GetTextExporter>>exportTag:msg: (in category 'private') -----
exportTag: tag msg: aString 
	stream nextPutAll: tag.
	stream space.
	aString lineIndicesDo: [:start :endWithoutDelimiters :end |
		| line |
		line := (end = endWithoutDelimiters)
			ifTrue: [aString copyFrom: start to: endWithoutDelimiters]
			ifFalse: [(aString at: endWithoutDelimiters + 1) = Character cr
				ifTrue: [aString copyFrom: start to: endWithoutDelimiters + 1]
				ifFalse: [(aString copyFrom: start to: endWithoutDelimiters) copyWith: Character cr]].
		stream
			nextPut: $";
			nextPutAll: (self formatString: line);
			nextPut: $";
			cr].!

----- Method: GetTextExporter>>exportTemplate (in category 'exporting') -----
exportTemplate
	"GetTextExporter2 new exportTemplate"
	self exportTranslator: nil!

----- Method: GetTextExporter>>exportTranslator: (in category 'exporting') -----
exportTranslator: translator 
	"Export translation files. the file extention is 'po', or 'pot' if translator is nil "
	"GetTextExporter2 new exportTranslator: NaturalLanguageTranslator current "
	| domains |
	domains := Dictionary new.
	self appendTranslations: domains.
	domains
		keysAndValuesDo: [:domainName :value |
			self
				export: value
				translator: translator
				domain: domainName]!

----- Method: GetTextExporter>>formatReplacements (in category 'private') -----
formatReplacements
	| replacements |
	replacements := OrderedCollection new.
	replacements add: '\' -> '\\'.
	replacements add: String cr -> '\n'.
	replacements add: String tab -> '\t'.
	replacements add: '"' -> '\"'.
	^ replacements!

----- Method: GetTextExporter>>formatString: (in category 'private') -----
formatString: aString 
	| result |
	result := aString.
	self formatReplacements
		do: [:each | result := result copyReplaceAll: each key with: each value].
	^ result!

----- Method: GetTextExporter>>getTextDomainForPackage: (in category 'as yet unclassified') -----
getTextDomainForPackage: aPackageInfo 
	^TextDomainManager domainForPackage: aPackageInfo!

----- Method: GetTextExporter>>stream (in category 'accessing') -----
stream
	^ stream!

----- Method: GetTextExporter>>stream: (in category 'accessing') -----
stream: aStream
	stream := aStream!

----- Method: GetTextExporter>>translationFor:in: (in category 'private') -----
translationFor: aKey in: translator
	| translation |
	translator ifNil: [^ ''].
	TextDomainManager allKnownDomains do: [:domain |
		translation := translator translate: aKey inDomain: domain.
		aKey = translation ifFalse: [^translation]
	].
	^ aKey!

Object subclass: #GetTextInterchange
	instanceVariableNames: 'language stream'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GetText-Editor'!

GetTextInterchange subclass: #GetTextImporter
	instanceVariableNames: 'msgId msgStr state'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GetText-Editor'!

!GetTextImporter commentStamp: 'tak 10/24/2007 11:23' prior: 0!
GetTextImporter load gettext po file into a InternalTranslator.

GetTextImporter new importID: (LocaleID isoString: 'lang-name') fileNamed: 'lang.po'
!

----- Method: GetTextImporter class>>cleanUpUnnecessaryPhrases (in category 'utilities') -----
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: []]!

----- Method: GetTextImporter class>>import:allDirectory: (in category 'utilities') -----
import: translator allDirectory: aFileDirectory 
	"self import: NaturalLanguageTranslator current allDirectory:
	FileDirectory default"
	| fileName targetFile |
	fileName := translator localeID posixName , '.po'.
	(FileDirectory default directoryNamed: 'po')
		withAllSubdirectoriesCollect: [:each | (each fileExists: fileName)
				ifTrue: [targetFile := each fullNameFor: fileName.
					self new import: translator fileNamed: targetFile]]!

----- Method: GetTextImporter class>>importAll (in category 'utilities') -----
importAll
	"GetTextImporter importAll"
	"Import all gettext files on po/. Only registered language is imported"
	InternalTranslator cachedTranslations
		do: [:translator | self import: translator allDirectory: FileDirectory default]!

----- Method: GetTextImporter>>appendId: (in category 'parsing') -----
appendId: aString 
	msgId := msgId , aString!

----- Method: GetTextImporter>>appendStr: (in category 'parsing') -----
appendStr: aString 
	msgStr := msgStr , aString!

----- Method: GetTextImporter>>formatString: (in category 'private') -----
formatString: aString 
"
	self assert: (GetTextImporter new formatString: 'test') = 'test'.
	self assert: (GetTextImporter new formatString: 'te\nst') = ('te', String cr, 'st').
	self assert: (GetTextImporter new formatString: 'te\\nst') = ('te\nst').
	self assert: (GetTextImporter new formatString: 'te\\st') = ('te\st').
	self assert: (GetTextImporter new formatString: 'te\st') = ('te\st').
"
	| reader writer char |
	reader := aString readStream.
	writer := '' writeStream.
	[reader atEnd]
		whileFalse: [char := reader next.
			(char = $\
					and: [reader atEnd not])
				ifTrue: [char := reader next.
					char caseOf: {
						[$n] -> [writer nextPut: Character cr].
						[$t] -> [writer nextPut: Character tab].
						[$"] -> [writer nextPut: $"].
						[$\] -> [writer nextPut: $\]}
						 otherwise: [writer nextPutAll: {$\. char}]]
				ifFalse: [writer nextPut: char]].
	^ writer contents!

----- Method: GetTextImporter>>import: (in category 'importing') -----
import: aLanguage 
	^ self import: aLanguage fileNamed:  aLanguage localeID posixName , '.po'!

----- Method: GetTextImporter>>import:fileNamed: (in category 'importing') -----
import: aLanguage fileNamed: fileName
	self importID: aLanguage localeID fileNamed: fileName!

----- Method: GetTextImporter>>importID:fileNamed: (in category 'importing') -----
importID: localeID fileNamed: fileName
	| currentPlatform |
	language := InternalTranslator newLocaleID: localeID.
	currentPlatform := Locale currentPlatform.
	[Locale
		currentPlatform: (Locale localeID: localeID).
	[stream := FileStream readOnlyFileNamed: fileName.
	stream text.
	self parse]
		ensure: [stream notNil
				ifTrue: [stream close]]]
		ensure: [Locale currentPlatform: currentPlatform].
	NaturalLanguageTranslator privateStartUp "Actually it is not private no more...".!

----- Method: GetTextImporter>>initialize (in category 'initialize-release') -----
initialize
	msgId := ''.
	msgStr := ''.
	state := nil!

----- Method: GetTextImporter>>parse (in category 'parsing') -----
parse
	| size |
	size := (stream isKindOf: FileStream)
				ifTrue: [stream size]
				ifFalse: [1].
	ProgressInitiationException
		display: 'Importing phrases from a gettext file.'
		during: [:bar | [stream atEnd]
				whileFalse: [| line | 
					line := stream upTo: Character linefeed.
					self
						parseLine: ((line endsWith: String cr)
								ifTrue: [line allButLast]
								ifFalse: [line]).
					bar value: stream position / size]].
	self storeTranslation!

----- Method: GetTextImporter>>parseLine: (in category 'parsing') -----
parseLine: lineString 
	(lineString beginsWith: '"Content-Type:')
		ifTrue: [self setContentType: lineString.
			^ self].
	(lineString beginsWith: '#')
		ifTrue: ["do nothing"
			^ self].
	lineString = ''
		ifTrue: [^ self storeTranslation].
	(lineString beginsWith: 'msgid')
		ifTrue: [state := #appendId:.
			self parseMsg: lineString.
			^ self].
	(lineString beginsWith: 'msgstr')
		ifTrue: [state := #appendStr:.
			self parseMsg: lineString.
			^ self].
	self parseMsg: lineString!

----- Method: GetTextImporter>>parseMsg: (in category 'parsing') -----
parseMsg: lineString 
	| begin end msg |
	begin := lineString indexOf: $".
	end := lineString lastIndexOf: $".
	msg := begin + 1 <= (end - 1)
				ifTrue: [lineString copyFrom: begin + 1 to: end - 1]
				ifFalse: [''].
	state
		ifNotNil: [self perform: state with: msg].
	^ msg!

----- Method: GetTextImporter>>setContentType: (in category 'parsing') -----
setContentType: lineString 
	"self new setContentType: 'Content-Type: text/plain; charset=utf-8'"
	| reader charSet |
	reader := lineString readStream.
	reader upTo: $=.
	charSet := reader upTo: $\.
	stream
		converter: (TextConverter newForEncoding: charSet)!

----- Method: GetTextImporter>>storeTranslation (in category 'parsing') -----
storeTranslation
	| key |
	key := self formatString: msgId.
	msgId isEmpty
		ifFalse: [InternalTranslator registerPhrase: key.
			msgStr isEmpty
				ifFalse: [language
						rawPhrase: key
						translation: (self formatString: msgStr)]].
	self initialize!

----- Method: GetTextInterchange>>defaultFileName (in category 'private') -----
defaultFileName
	^ language localeID posixName , '.po'!

----- Method: GetTextInterchange>>language: (in category 'accessing') -----
language: translator
	language := translator!

----- Method: GetTextInterchange>>stream (in category 'accessing') -----
stream
	^ stream!

----- Method: GetTextInterchange>>stream: (in category 'accessing') -----
stream: aStream
	stream := aStream!

SystemWindow subclass: #LanguageEditor
	instanceVariableNames: 'translator translations untranslated selectedTranslation selectedTranslations selectedUntranslated translationsList untranslatedList translationText translationsFilter untranslatedFilter newerKeys'
	classVariableNames: 'CheckMethods'
	poolDictionaries: ''
	category: 'GetText-Editor'!

!LanguageEditor commentStamp: 'dgd 11/16/2003 15:02' prior: 0!
Editor for Babel's languages.

Open it from

	World Menu >> open... >> Language Editor			(to open on default language)
	World Menu >> open... >> Language Editor for...	(to choose the language)

Or click:

	LanguageEditor openOnDefault.
	LanguageEditor open.

See http://swiki.agro.uba.ar/small_land/191 for documentation
!

----- Method: LanguageEditor class>>checkMethods (in category 'private') -----
checkMethods
	^CheckMethods ifNil: [CheckMethods := self initCheckMethods]!

----- Method: LanguageEditor class>>ensureVisibilityOfWindow: (in category 'private') -----
ensureVisibilityOfWindow: aWindow 
	"private - activate the window"
	| |
	aWindow expand.
	aWindow comeToFront.
	""
	aWindow
		right: (aWindow right min: World right).
	aWindow
		bottom: (aWindow bottom min: World bottom).
	aWindow
		left: (aWindow left max: World left).
	aWindow
		top: (aWindow top max: World top).
	""
	aWindow flash; flash!

----- Method: LanguageEditor class>>initCheckMethods (in category 'initialize-release') -----
initCheckMethods
	"LanguageEditor initCheckMethods"

	| registry |
	registry := Dictionary new.
	registry
		at: 'es' put: #checkSpanishPhrase:translation:;
		yourself.
	^registry!

----- Method: LanguageEditor class>>initialize (in category 'initialize-release') -----
initialize
	"initialize the receiver"
	(TheWorldMenu respondsTo: #registerOpenCommand:)
		ifTrue: [""
			TheWorldMenu registerOpenCommand: {'Language Editor' translated. {self. #openOnDefault}}.
			TheWorldMenu registerOpenCommand: {'Language Editor for...' translated. {self. #open}}]!

----- Method: LanguageEditor class>>on: (in category 'instance creation') -----
on: localeID 
	"answer an instance of the receiver on aLanguage"
	^ self new
		initializeOn: (InternalTranslator cachedTranslations
				at: localeID
				ifAbsent: [self
						error: ('Translator for {1} is not found' translated format: {localeID})])!

----- Method: LanguageEditor class>>open (in category 'opening') -----
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!

----- Method: LanguageEditor class>>openOn: (in category 'instance creation') -----
openOn: aLanguage 
	"open an instance on aLanguage"
	World submorphs
		do: [:each | ""
			((each isKindOf: LanguageEditor)
					and: [each translator == aLanguage])
				ifTrue: [""
					self ensureVisibilityOfWindow: each.
					^ self]].
	""
	^ (self on: aLanguage) openInWorld!

----- Method: LanguageEditor class>>openOnDefault (in category 'opening') -----
openOnDefault
	"open the receiver on the default language"
	self openOn: LocaleID current!

----- Method: LanguageEditor class>>unload (in category 'initialize-release') -----
unload
	"the receiver is being unloaded"
	(TheWorldMenu respondsTo: #registerOpenCommand:)
		ifTrue: [""
			TheWorldMenu unregisterOpenCommand: 'Language Editor'.
			TheWorldMenu unregisterOpenCommand: 'Language Editor for...'] !

----- Method: LanguageEditor>>addTranslation (in category 'gui methods') -----
addTranslation
	"translate a phrase"
	| phrase |
	phrase := UIManager default
				request: 'enter the original:' translated
				initialAnswer: ''.

	(phrase isNil
			or: [phrase = ''])
		ifTrue: [
			self beep.
			^ self].

	self translatePhrase: phrase!

----- Method: LanguageEditor>>applyTranslations (in category 'gui methods') -----
applyTranslations
	"private - try to apply the translations as much as possible all  
	over the image"
	Project current updateLocaleDependents!

----- Method: LanguageEditor>>asHtml: (in category 'reporting') -----
asHtml: aString 
	| stream |
	stream := String new writeStream.

	aString
		do: [:each | 
			each caseOf: {
				[Character cr] -> [stream nextPutAll: '<br>'].
				[$&] -> [stream nextPutAll: '&amp;'].
				[$<] -> [stream nextPutAll: '&lt;'].
				[$>] -> [stream nextPutAll: '&gt;'].
				[$*] -> [stream nextPutAll: '&star;'].
				[$@] -> [stream nextPutAll: '&at;']}
				 otherwise: [stream nextPut: each]].

	^ stream contents!

----- Method: LanguageEditor>>browseMethodsWithTranslation (in category 'gui methods') -----
browseMethodsWithTranslation
	| translation |
	self selectedTranslation isZero
		ifTrue: [""
			self beep.
			self inform: 'select the translation to look for' translated.
			^ self].
	""
	translation := self translations at: self selectedTranslation.
	self systemNavigation browseMethodsWithLiteral: translation!

----- Method: LanguageEditor>>browseMethodsWithUntranslated (in category 'gui methods') -----
browseMethodsWithUntranslated
	| untrans |
	self selectedUntranslated isZero
		ifTrue: [""
			self beep.
			self inform: 'select the untrans phrase to look for' translated.
			^ self].
	""
	untrans := self untranslated at: self selectedUntranslated.
	SystemNavigation default browseMethodsWithLiteral: untrans.
!

----- Method: LanguageEditor>>check (in category 'private') -----
check
	"check the translations and answer a collection with the results"
	| results counter phrasesCount checkMethod |
	results := OrderedCollection new.
	untranslated := self untranslated.
	phrasesCount := self translations size + self untranslated size.
	counter := 0.
	checkMethod := self class checkMethods at: self translator localeID printString ifAbsent: [^results].
	
	self translations
		keysAndValuesDo: [:phrase :translation | 
			| result | 
			result := self perform: checkMethod with: phrase with: translation.
			(result notNil
					and: [result notEmpty])
				ifTrue: [results add: {phrase. translation. result}].
		
			counter := counter + 1.
			(counter isDivisibleBy: 50)
				ifTrue: [| percent | 
					percent := counter / phrasesCount * 100 printShowingMaxDecimalPlaces: 2.
					Transcript
						show: ('- checked {1} phrases of {2} ({3}%)...' translated format: {counter. phrasesCount. percent});
						 cr]].

	self untranslated
		do: [:phrase | 
			| result | 
			result := self checkUntranslatedPhrase: phrase.
			(result notNil
					and: [result notEmpty])
				ifTrue: [results add: {phrase. nil. result}].
		
			counter := counter + 1.
			(counter isDivisibleBy: 50)
				ifTrue: [| percent | 
					percent := counter / phrasesCount * 100 printShowingMaxDecimalPlaces: 2.
					Transcript
						show: ('- checked {1} phrases of {2} ({3}%)...' translated format: {counter. phrasesCount. percent});
						 cr]].

	^ results!

----- Method: LanguageEditor>>checkPhrase:translation: (in category 'private') -----
checkPhrase: phraseString translation: translationString
	^nil!

----- Method: LanguageEditor>>checkSpanishPhrase:translation: (in category 'private') -----
checkSpanishPhrase: phraseString translation: translationString 
	"check the translation and aswer a string with a comment or a 
	nil meaning no-comments"
	| superResult |
	superResult := self checkPhrase: phraseString translation: translationString.
	superResult isNil
		ifFalse: [^ superResult].
	"For some reason, MCInstaller couldn't read Spanish character. "
	"((translationString includes: $?)
	and: [(translationString includes: $ø) not]) 
		ifTrue: [^ 'øOlvidÛ el signo de pregunta?'].
	((translationString includes: $!!)
	and: [(translationString includes: $°) not])
		ifTrue: [^ 'øOlvidÛ el signo de admiraciÛn?'].
	"
	^ nil
!

----- Method: LanguageEditor>>checkUntranslatedPhrase: (in category 'private') -----
checkUntranslatedPhrase: phraseString 
	"check the phrase an aswer a string with a comment or a nil  
	meaning no-comments"

	(self translations includes: phraseString)
		ifTrue: [^ 'possible double-translation' translated].

	^ nil!

----- Method: LanguageEditor>>codeSelectedTranslation (in category 'gui methods') -----
codeSelectedTranslation
	| keys code |
	keys := selectedTranslations
				collect: [:key | self translations at: key].
	code := String
				streamContents: [:aStream | self translator fileOutOn: aStream keys: keys withBOM: false].
	(StringHolder new contents: code)
		openLabel: 'exported codes' translated!

----- Method: LanguageEditor>>codeSelectedTranslationAsMimeString (in category 'gui methods') -----
codeSelectedTranslationAsMimeString
	| keys code tmpStream s2 gzs cont |
	keys := selectedTranslations
				collect: [:key | self translations at: key].
	code := String
				streamContents: [:aStream | self translator fileOutOn: aStream keys: keys withBOM: false].

	tmpStream := MultiByteBinaryOrTextStream on: ''.
	tmpStream converter: UTF8TextConverter new.
	tmpStream nextPutAll: code.
	s2 := RWBinaryOrTextStream on: ''.
	gzs := GZipWriteStream on: s2.
	tmpStream reset.
	gzs nextPutAll: (tmpStream binary contentsOfEntireFile asString) contents.
	gzs close.
	s2 reset.

	cont := String streamContents: [:strm |
		strm nextPutAll: '"Gzip+Base64 encoded translation for;'; cr.
		strm nextPutAll: '#('.
		keys do: [:each | strm  nextPutAll: '''', each, ''' '.].
		strm nextPutAll: ')"'; cr; cr.
		strm nextPutAll: 'NaturalLanguageTranslator loadForLocaleIsoString: '.
		strm nextPut: $'.
		strm nextPutAll: translator localeID isoString.
		strm nextPut: $'.
		strm nextPutAll: ' fromGzippedMimeLiteral: '.
		strm nextPut: $'.
		strm nextPutAll: (Base64MimeConverter mimeEncode: s2) contents.
		strm nextPutAll: '''.'.
		strm cr.
	].
	
	(StringHolder new contents: cont)
		openLabel: 'exported codes in Gzip+Base64 encoding' translated!

----- Method: LanguageEditor>>createButtonLabel:action:help: (in category 'initialization - toolbar') -----
createButtonLabel: aString action: actionSelector help: helpString 
	"create a toolbar for the receiver"
	| button |
	button := SimpleButtonMorph new target: self;
				 label: aString translated "font: Preferences standardButtonFont";
				 actionSelector: actionSelector;
				 setBalloonText: helpString translated;
				 color: translator defaultBackgroundColor twiceDarker;
				 borderWidth: 2;
				 borderColor: #raised.
	""
	^ button!

----- Method: LanguageEditor>>createMainToolbar (in category 'initialization - toolbar') -----
createMainToolbar
	"create a toolbar for the receiver"
	| toolbar |
	toolbar := self createRow.
	""
"	toolbar
		addMorphBack: (self
				createUpdatingButtonWording: #debugWording
				action: #switchDebug
				help: 'Switch the debug flag')."
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	""
	toolbar
		addMorphBack: (self
				createButtonLabel: 'new'
				action: #newTranslations
				help: 'Create translations for new language.').
	toolbar
		addMorphBack: (self
				createButtonLabel: 'save'
				action: #saveToFile
				help: 'Save the translations to a file').
	toolbar
		addMorphBack: (self
				createButtonLabel: 'load'
				action: #loadFromFile
				help: 'Load the translations from a file').
	toolbar
		addMorphBack: (self
				createButtonLabel: 'merge'
				action: #mergeFromFile
				help: 'Merge the current translations with the translations in a file').
	""
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'apply'
				action: #applyTranslations
				help: 'Apply the translations as much as possible.').
	""
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'check translations'
				action: #check
				help: 'Check the translations and report the results.').
	toolbar
		addMorphBack: (self
				createButtonLabel: 'report'
				action: #report
				help: 'Create a report.').
	toolbar
		addMorphBack: (self
				createButtonLabel: 'gettext'
				action: #getText
				help: 'Interface with gettext.').
	""
	^ toolbar!

----- Method: LanguageEditor>>createRow (in category 'initialization - toolbar') -----
createRow
	"create a row"
	| row |
	row := AlignmentMorph newRow.
	row layoutInset: 3;
		 wrapCentering: #center;
		 cellPositioning: #leftCenter.
	""
	^ row!

----- Method: LanguageEditor>>createStatusbar (in category 'initialization - statusbar') -----
createStatusbar
	"create the statusbar for the receiver"
	| statusbar |
	statusbar := self createRow.
	statusbar addMorph: ((UpdatingStringMorph on: self selector: #status) growable: true;
			 useStringFormat;
			 hResizing: #spaceFill;
			 stepTime: 2000).
	^ statusbar!

----- Method: LanguageEditor>>createTranslationsToolbar (in category 'initialization - toolbar') -----
createTranslationsToolbar
	"create a toolbar for the receiver"
	| toolbar |
	toolbar := self createRow.
	""
	toolbar
		addMorphBack: (self
				createUpdatingButtonWording: #translationsFilterWording
				action: #filterTranslations
				help: 'Filter the translations list.').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	""
	toolbar
		addMorphBack: (self
				createButtonLabel: 'search'
				action: #searchTranslation
				help: 'Search for a translation containing...').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'remove'
				action: #removeTranslation
				help: 'Remove the selected translation.  If none is selected, ask for the one to remove.').
	""
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'where'
				action: #browseMethodsWithTranslation
				help: 'Launch a browser on all methods that contain the phrase as a substring of any literal String.').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'r-unused'
				action: #removeTranslatedButUnusedStrings
				help: 'Remove all the strings that are not used by the system').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'add '
				action: #addTranslation
				help: 'Add a new phrase').

	^ toolbar!

----- Method: LanguageEditor>>createUntranslatedToolbar (in category 'initialization - toolbar') -----
createUntranslatedToolbar
	"create a toolbar for the receiver"
	| toolbar |
	toolbar := self createRow.
	""
	toolbar
		addMorphBack: (self
				createUpdatingButtonWording: #untranslatedFilterWording
				action: #filterUntranslated
				help: 'Filter the untranslated list.').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	""
	toolbar
		addMorphBack: (self
				createButtonLabel: 'search'
				action: #searchUntranslated
				help: 'Search for a untranslated phrase containing...').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'remove'
				action: #removeUntranslated
				help: 'Remove the selected untranslated phrease.  If none is selected, ask for the one to remove.').
	""
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'translate'
				action: #translate
				help: 'Translate the selected untranslated phrase or a new phrase').
	""
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'where'
				action: #browseMethodsWithUntranslated
				help: 'Launch a browser on all methods that contain the phrase as a substring of any literal String.').
	toolbar addTransparentSpacerOfSize: 5 @ 0.
	toolbar
		addMorphBack: (self
				createButtonLabel: 'r-unused'
				action: #removeUntranslatedButUnusedStrings
				help: 'Remove all the strings that are not used by the system').
	^ toolbar!

----- Method: LanguageEditor>>createUpdatingButtonWording:action:help: (in category 'initialization - toolbar') -----
createUpdatingButtonWording: wordingSelector action: actionSelector help: helpString 
	"create a toolbar for the receiver"
	| button |
	button := (UpdatingSimpleButtonMorph newWithLabel: '-') target: self;
				 wordingSelector: wordingSelector;
				 actionSelector: actionSelector;
				 setBalloonText: helpString translated;
				 color: translator defaultBackgroundColor twiceDarker;
				 borderWidth: 1;
				 borderColor: #raised; cornerStyle: #square.
	""
	^ button!

----- Method: LanguageEditor>>delete (in category 'open/close') -----
delete
	"Remove the receiver as a submorph of its owner"
	self model: nil.
	super delete !

----- Method: LanguageEditor>>deselectAllTranslation (in category 'gui methods') -----
deselectAllTranslation
	selectedTranslations := IdentitySet new.
	self changed: #allSelections!

----- Method: LanguageEditor>>filterTranslations (in category 'gui methods') -----
filterTranslations
	| filter |
	filter := UIManager default
		request: 'filter with
(empty string means no-filtering)' translated
		initialAnswer: self translationsFilter.

	self filterTranslations: filter!

----- Method: LanguageEditor>>filterTranslations: (in category 'gui methods') -----
filterTranslations: aString 
| filter |
filter := aString ifNil:[''].
""
	translationsFilter := filter.
	self refreshTranslations.
!

----- Method: LanguageEditor>>filterUntranslated (in category 'gui methods') -----
filterUntranslated
	| filter |
	filter := UIManager default
		request: 'filter with
(empty string means no-filtering)' translated
		initialAnswer: self untranslatedFilter.

	self filterUntranslated: filter!

----- Method: LanguageEditor>>filterUntranslated: (in category 'gui methods') -----
filterUntranslated: aString 
	| filter |
	filter := aString
				ifNil: [''].
	""
	untranslatedFilter := filter.
	self refreshUntranslated!

----- Method: LanguageEditor>>getText (in category 'gui methods') -----
getText
	| menu |
	menu := MenuMorph new defaultTarget: self.
	""
	menu
		add: 'gettext template' translated
		target: self
		selector: #getTextExportTemplate.
	menu lastItem setBalloonText: 'exports the translations to gettext separated format.' translated.
	menu
		add: 'gettext export' translated
		target: self
		selector: #getTextExport.
	menu lastItem setBalloonText: 'Exports the translations to GetText format.' translated.

	""
	menu
		add: 'gettext import' translated
		target: self
		selector: #getTextImport.
	menu lastItem setBalloonText: 'Imports the translations from GetText format.' translated.
	""
	menu popUpInWorld!

----- Method: LanguageEditor>>getTextExport (in category 'gui methods') -----
getTextExport
	Cursor wait
		showWhile: [GetTextExporter new exportTranslator: self model]!

----- Method: LanguageEditor>>getTextExportTemplate (in category 'gui methods') -----
getTextExportTemplate
	Cursor wait
		showWhile: [GetTextExporter new exportTemplate] !

----- Method: LanguageEditor>>getTextImport (in category 'gui methods') -----
getTextImport
	| menu |
	menu := MenuMorph new defaultTarget: self.
	menu addTitle: 'Choose translation file' translated.
	menu add: 'All *.po files' translated action: #getTextImportAll.
	menu add: 'Choose a file' translated action: #getTextImportAFile.
	menu popUpInWorld!

----- Method: LanguageEditor>>getTextImportAFile (in category 'gui methods') -----
getTextImportAFile
	| result |
	result := (StandardFileMenu new pattern: '*.po';
				
				oldFileFrom: (FileDirectory default directoryNamed: 'po')) startUpWithCaption: 'Select a File:' translated.
	result
		ifNil: [^ self].
	self
		withUnboundModelDo: [:trans | Cursor wait
				showWhile: [GetTextImporter new
						import: trans
						fileNamed: (result directory fullNameFor: result name)]]!

----- Method: LanguageEditor>>getTextImportAll (in category 'gui methods') -----
getTextImportAll
	self
		withUnboundModelDo: [:trans | Cursor wait
				showWhile: [GetTextImporter import: trans allDirectory: FileDirectory default]].
	self refreshBoth!

----- Method: LanguageEditor>>identifyUnusedStrings (in category 'stef') -----
identifyUnusedStrings
	"self new identifyUnusedStrings"
	translationsList getList
		do: [:each | 
			Transcript show: each.
			Transcript show: (Smalltalk
					allSelect: [:method | method
							hasLiteralSuchThat: [:lit | lit isString
									and: [lit includesSubstring: each caseSensitive: true]]]) size printString; cr]!

----- Method: LanguageEditor>>initializeNewerKeys (in category 'initialization') -----
initializeNewerKeys

	newerKeys := Set new.
!

----- Method: LanguageEditor>>initializeOn: (in category 'initialization') -----
initializeOn: aLanguage 
	"initialize the receiver on aLanguage"
	""
	selectedTranslation := 0.
	selectedUntranslated := 0.
	selectedTranslations := IdentitySet new.
	""
	translator := aLanguage.
	""
	self model: aLanguage.
	self setLabel: 'Language editor for: ' translated , self translator name.
	""
	self initializeToolbars.
	self initializePanels.
	self initializeStatusbar.
	self initializeNewerKeys.
!

----- Method: LanguageEditor>>initializePanels (in category 'initialization') -----
initializePanels
	"initialize the receiver's panels"
	translationsList := PluggableListMorphOfMany
				on: self
				list: #translations
				primarySelection: #selectedTranslation
				changePrimarySelection: #selectedTranslation:
				listSelection: #selectedTranslationsAt:
				changeListSelection: #selectedTranslationsAt:put:
				menu: #translationsMenu:
				keystroke: #translationsKeystroke:.
	translationsList setBalloonText: 'List of all the translated phrases.' translated.
	""
	untranslatedList := PluggableListMorph
				on: self
				list: #untranslated
				selected: #selectedUntranslated
				changeSelected: #selectedUntranslated:
				menu: #untranslatedMenu:
				keystroke: #untranslatedKeystroke:.
	untranslatedList setBalloonText: 'List of all the untranslated phrases.' translated.
	""
	translationText := PluggableTextMorph
				on: self
				text: #translation
				accept: #translation:
				readSelection: nil
				menu: nil.
	translationText setBalloonText: 'Translation for the selected phrase in the upper list.' translated.
	""
	self
		addMorph: translationsList
		frame: (0 @ 0.18 corner: 0.5 @ 0.66).
	self
		addMorph: untranslatedList
		frame: (0.5 @ 0.18 corner: 1 @ 0.93).
	self
		addMorph: translationText
		frame: (0 @ 0.66 corner: 0.5 @ 0.93).
	self hResizing: #shrinkWrap!

----- Method: LanguageEditor>>initializeStatusbar (in category 'initialization - statusbar') -----
initializeStatusbar
	"initialize the receiver's statusbar"
	self
		addMorph: self createStatusbar
		frame: (0 @ 0.93 corner: 1 @ 1)!

----- Method: LanguageEditor>>initializeToolbars (in category 'initialization - toolbar') -----
initializeToolbars
	"initialize the receiver's toolbar"
	self
		addMorph: self createMainToolbar
		frame: (0 @ 0 corner: 1 @ 0.09).
	""
	self
		addMorph: self createTranslationsToolbar
		frame: (0 @ 0.09 corner: 0.5 @ 0.18).
	self
		addMorph: self createUntranslatedToolbar
		frame: (0.5 @ 0.09 corner: 1 @ 0.18)!

----- Method: LanguageEditor>>loadFromFile (in category 'gui methods') -----
loadFromFile
	| fileName |
	fileName := self selectTranslationFileName.
	fileName isNil
		ifTrue: [""
			self beep.
			^ self].
	""
	Cursor wait
		showWhile: [
			self translator loadFromFileNamed: fileName.
			self refreshBoth]!

----- Method: LanguageEditor>>mergeFromFile (in category 'gui methods') -----
mergeFromFile
	| fileName |
	fileName := self selectTranslationFileName.
	fileName isNil
		ifTrue: [""
			self beep.
			^ self].
	""
	Cursor wait
		showWhile: [
			self translator loadFromFileNamed: fileName.
			self refreshBoth]!

----- Method: LanguageEditor>>newTranslations (in category 'gui methods') -----
newTranslations
	"private - try to apply the translations as much as possible all 
	over the image"
	| result newID |
	result := UIManager default request: 'New locale ID string?' translated initialAnswer: Locale current determineLocaleID isoString.
	result isEmpty
		ifTrue: ["Do nothing"
			^ self].
	newID := LocaleID isoString: result.
	InternalTranslator
		newLocaleID: (LocaleID isoString: result).
	self class openOn: newID!

----- Method: LanguageEditor>>numberOfTimesStringIsUsed: (in category 'stef') -----
numberOfTimesStringIsUsed: aString

	^ (self systemNavigation allSelect: [:method | method
							hasLiteralSuchThat: [:lit | lit isString
									and: [lit includesSubstring: aString caseSensitive: true]]]) size!

----- Method: LanguageEditor>>okToChange (in category 'updating') -----
okToChange
	"Allows a controller to ask this of any model"
	self selectedTranslation isZero
		ifTrue: [^ true].
	""
	translationText hasUnacceptedEdits
		ifFalse: [^ true].
	^ (CustomMenu confirm: 'Discard the changes to currently selected translated phrase?' translated)
		and: [""
			translationText hasUnacceptedEdits: false.
			true]!

----- Method: LanguageEditor>>perform:orSendTo: (in category 'message handling') -----
perform: selector orSendTo: otherTarget 
	"I wish to intercept and handle selector myself"
	^ self perform: selector!

----- Method: LanguageEditor>>phrase:translation: (in category 'gui methods') -----
phrase: phraseString translation: translationString 
	"set the models's translation for phraseString"
	self translator phrase: phraseString translation: translationString.
	self refreshBoth.

	newerKeys add: phraseString.
!

----- Method: LanguageEditor>>phraseToTranslate (in category 'gui methods') -----
phraseToTranslate
	"answer a phrase to translate.  use the selected untranslated phrase or ask for a new one"
	^ self selectedUntranslated isZero
		ifTrue: [UIManager default
				multiLineRequest: 'new phrase to translate' translated
				centerAt: Sensor cursorPoint
				initialAnswer: ''
				answerHeight: 200]
		ifFalse: [self untranslated at: self selectedUntranslated]!

----- Method: LanguageEditor>>printHeaderReportOn: (in category 'reporting') -----
printHeaderReportOn: aStream 
	"append to aStream a header report of the receiver with swiki  
	format"
	aStream nextPutAll: '!!!!';
		
		nextPutAll: ('Language: {1}' translated format: {self translator localeID isoString});
		 cr.

	aStream nextPutAll: '- ';
		
		nextPutAll: ('{1} translated phrases' translated format: {self translator translations size});
		 cr.

	aStream nextPutAll: '- ';
		
		nextPutAll: ('{1} untranslated phrases' translated format: {self translator untranslated size});
		 cr.

	aStream cr; cr!

----- Method: LanguageEditor>>printReportOn: (in category 'reporting') -----
printReportOn: aStream 
	"append to aStream a report of the receiver with swiki format"
	self printHeaderReportOn: aStream.
	self printUntranslatedReportOn: aStream.
	self printTranslationsReportOn: aStream!

----- Method: LanguageEditor>>printTranslationsReportOn: (in category 'reporting') -----
printTranslationsReportOn: aStream 
	"append to aStream a report of the receiver's translations"
	| originalPhrases |
	aStream nextPutAll: '!!';
		 nextPutAll: 'translations' translated;
		 cr.

	originalPhrases := self translator translations keys asSortedCollection.

	originalPhrases
		do: [:each | 
			aStream
				nextPutAll: ('|{1}|{2}|' format: {self asHtml: each. self
							asHtml: (self translator translate: each)});
				 cr].

	aStream cr; cr!

----- Method: LanguageEditor>>printUntranslatedReportOn: (in category 'reporting') -----
printUntranslatedReportOn: aStream 
	"append to aStream a report of the receiver's translations"
	aStream nextPutAll: '!!';
		 nextPutAll: 'not translated' translated;
		 cr.

	self untranslated asSortedCollection
		do: [:each | 
			aStream
				nextPutAll: ('|{1}|' format: {self asHtml: each});
				 cr].

	aStream cr; cr!

----- Method: LanguageEditor>>refreshBoth (in category 'updating') -----
refreshBoth
	self refreshUntranslated
!

----- Method: LanguageEditor>>refreshTranslations (in category 'updating') -----
refreshTranslations
	"refresh the translations panel"
	self selectedTranslation: 0.
	translations := nil.
	self changed: #translations.
!

----- Method: LanguageEditor>>refreshUntranslated (in category 'updating') -----
refreshUntranslated
"refresh the untranslated panel"
	self refreshTranslations.
	self selectedUntranslated: 0.
	untranslated := nil.
	self changed: #untranslated.
!

----- Method: LanguageEditor>>removeTranslatedButUnusedStrings (in category 'stef') -----
removeTranslatedButUnusedStrings
	(self confirm: 'Are you sure that you want to remove unused strings?' translated)
		ifFalse: [^ self].
	translationsList getList
		do: [:each | 
			| timesUsed | 
			timesUsed := self numberOfTimesStringIsUsed: each.
			Transcript show: each.
			Transcript show: timesUsed printString;
				 cr.
			timesUsed isZero
				ifTrue: [self translator removeTranslationFor: each]]!

----- Method: LanguageEditor>>removeTranslation (in category 'gui methods') -----
removeTranslation
	"remove the selected translation"
	| translation |
	self selectedTranslation isZero
		ifTrue: [""
			self beep.
			self inform: 'select the translation to remove' translated.
			^ self].
	""
	translation := self translations at: self selectedTranslation.
""
	(self
			confirm: ('Removing "{1}".
Are you sure you want to do this?' translated format: {translation}))
		ifFalse: [^ self].
""
	self translator removeTranslationFor: translation.
	self refreshBoth!

----- Method: LanguageEditor>>removeUntranslated (in category 'gui methods') -----
removeUntranslated
	"remove the selected untranslated phrase"
	| untrans |
	self selectedUntranslated isZero
		ifTrue: [""
			self beep.
			self inform: 'select the untranslated phrase to remove' translated.
			^ self].
	""
	untrans := self untranslated at: self selectedUntranslated.
	""
	(self
			confirm: ('Removing "{1}".
Are you sure you want to do this?' translated format: {untrans}))
		ifFalse: [^ self].
	""
	self translator removeUntranslated: untrans!

----- Method: LanguageEditor>>removeUntranslatedButUnusedStrings (in category 'stef') -----
removeUntranslatedButUnusedStrings
	(self confirm: 'Are you sure that you want to remove unused strings?' translated)
		ifFalse: [^ self].
	untranslatedList getList
		do: [:each | 
			| timesUsed | 
			timesUsed := self numberOfTimesStringIsUsed: each.
			Transcript show: each.
			Transcript show: timesUsed printString;
				 cr.
			timesUsed isZero 
				ifTrue: [self translator removeUntranslated: each]].

	self refreshUntranslated.
!

----- Method: LanguageEditor>>report (in category 'gui methods') -----
report
	self reportString openInWorkspaceWithTitle: 'report' translated!

----- Method: LanguageEditor>>reportString (in category 'reporting') -----
reportString
	"answer a string with a report of the receiver"
	| stream |
	stream := String new writeStream.
	self printReportOn: stream.
	^ stream contents!

----- Method: LanguageEditor>>resetNewerKeys (in category 'gui methods') -----
resetNewerKeys

	self initializeNewerKeys.
!

----- Method: LanguageEditor>>saveToFile (in category 'gui methods') -----
saveToFile
	"save the translator to a file"
	| fileName |
	fileName := UIManager default request: 'file name' translated initialAnswer: translator localeID isoString , '.translation'.
	(fileName isNil
			or: [fileName isEmpty])
		ifTrue: [
			self beep.
			^ self].

Cursor wait
		showWhile: [
	self translator saveToFileNamed: fileName]!

----- Method: LanguageEditor>>searchTranslation (in category 'gui methods') -----
searchTranslation
	| search |
	search := UIManager default request: 'search for' translated initialAnswer: ''.
	(search isNil
			or: [search isEmpty])
		ifTrue: [
			self beep.
			^ self].

self searchTranslation: search!

----- Method: LanguageEditor>>searchTranslation: (in category 'gui methods') -----
searchTranslation: aString 
	| results index |
	results := self translations
				select: [:each | ""
					('*' , aString , '*' match: each)
						or: ['*' , aString , '*' match: (self translator translate: each)]].
	""
	results isEmpty
		ifTrue: [""
			self inform: 'no matches for' translated , ' ''' , aString , ''''.
			^ self].
	""
	results size = 1
		ifTrue: [""
			self selectTranslationPhrase: results first.
			^ self].
	""
	index := (PopUpMenu
				labelArray: (results
						collect: [:each | ""
							(each copy replaceAll: Character cr with: $\)
								, ' -> '
								, ((self translator translate: each) copy replaceAll: Character cr with: $\)]))
				startUpWithCaption: 'select the translation...' translated.
	""
	index isZero
		ifTrue: [""
			self beep.
			^ self].
	""
	self
		selectTranslationPhrase: (results at: index)!

----- Method: LanguageEditor>>searchUntranslated (in category 'gui methods') -----
searchUntranslated
	| search |
	search := UIManager default request: 'search for' translated initialAnswer: ''.
	(search isNil
			or: [search isEmpty])
		ifTrue: [
			self beep.
			^ self].

	self searchUntranslated: search!

----- Method: LanguageEditor>>searchUntranslated: (in category 'gui methods') -----
searchUntranslated: aString 
	| untranslateds results index |
	untranslateds := self untranslated.
	results := untranslateds
				select: [:each | '*' , aString , '*' match: each].
	""
	results isEmpty
		ifTrue: [""
			self inform: 'no matches for' translated , ' ''' , aString , ''''.
			^ self].
	""
	results size = 1
		ifTrue: [""
			self selectUntranslatedPhrase: results first.
			^ self].
	""
	index := (PopUpMenu
				labelArray: (results
						collect: [:each | each copy replaceAll: Character cr with: $\]))
				startUpWithCaption: 'select the untranslated phrase...' translated.
	""
	index isZero
		ifTrue: [""
			self beep.
			^ self].
	""
	self
		selectUntranslatedPhrase: (results at: index)!

----- Method: LanguageEditor>>selectAllTranslation (in category 'gui methods') -----
selectAllTranslation
	selectedTranslations := (1 to: self translations size) asIdentitySet.
	self changed: #allSelections!

----- Method: LanguageEditor>>selectNewerKeys (in category 'gui methods') -----
selectNewerKeys

	| index |
	self deselectAllTranslation.
	newerKeys do: [:k |
		index := self translations indexOf: k ifAbsent: [0].
		index > 0 ifTrue: [
			self selectedTranslationsAt: index put: true
		].
	].
!

----- Method: LanguageEditor>>selectTranslationFileName (in category 'gui methods') -----
selectTranslationFileName
	"answer a file with a translation"
	| file |
	file := (StandardFileMenu oldFileMenu: FileDirectory default withPattern: '*.translation')
				startUpWithCaption: 'Select the file...' translated.
	^ file isNil
		ifFalse: [file directory fullNameFor: file name]!

----- Method: LanguageEditor>>selectTranslationPhrase: (in category 'gui methods') -----
selectTranslationPhrase: phraseString 
	self selectedTranslation: (self translations indexOf: phraseString)!

----- Method: LanguageEditor>>selectUntranslatedPhrase: (in category 'gui methods') -----
selectUntranslatedPhrase: phraseString 
	self
		selectedUntranslated: (self untranslated indexOf: phraseString)!

----- Method: LanguageEditor>>selectedTranslation (in category 'accessing') -----
selectedTranslation
	"answer the selectedTranslation"
	^ selectedTranslation!

----- Method: LanguageEditor>>selectedTranslation: (in category 'accessing') -----
selectedTranslation: anInteger 
	"change the receiver's selectedTranslation"
	selectedTranslation := anInteger.
	""
	self changed: #selectedTranslation.
	self changed: #translation!

----- Method: LanguageEditor>>selectedTranslationsAt: (in category 'accessing') -----
selectedTranslationsAt: index
	^ selectedTranslations includes: index!

----- Method: LanguageEditor>>selectedTranslationsAt:put: (in category 'accessing') -----
selectedTranslationsAt: index put: value 
	value = true
		ifTrue: [selectedTranslations add: index]
		ifFalse: [selectedTranslations
				remove: index
				ifAbsent: []]!

----- Method: LanguageEditor>>selectedUntranslated (in category 'accessing') -----
selectedUntranslated
	"answer the selectedUntranslated"
	^ selectedUntranslated!

----- Method: LanguageEditor>>selectedUntranslated: (in category 'accessing') -----
selectedUntranslated: anInteger 
	"change the selectedUntranslated"
	selectedUntranslated := anInteger.
	""
	self changed: #selectedUntranslated!

----- Method: LanguageEditor>>status (in category 'gui methods') -----
status
	"answer a status string"
	| translationsSize untranslatedSize |
	translationsSize := self translator translations size.
	untranslatedSize := self translator untranslated size.
	^ '| {1} phrases | {2} translated | {3} untranslated |' translated format: {translationsSize + untranslatedSize. translationsSize. untranslatedSize}!

----- Method: LanguageEditor>>translate (in category 'gui methods') -----
translate
	"translate a phrase"
	| phrase |
	phrase := self phraseToTranslate.
	""
	(phrase isNil
			or: [phrase = ''])
		ifTrue: [""
			self beep.
			^ self].
	""
	self translatePhrase: phrase.
	self refreshBoth!

----- Method: LanguageEditor>>translatePhrase: (in category 'gui methods') -----
translatePhrase: aString 
	"translate aString"
	| translation |
	translation := UIManager default
				multiLineRequest: 'translation for: ' translated , '''' , aString , ''''
				centerAt: Sensor cursorPoint
				initialAnswer: aString
				answerHeight: 200.

	(translation isNil
			or: [translation = ''])
		ifTrue: [""
			self beep.
			^ self].

	self phrase: aString translation: translation!

----- Method: LanguageEditor>>translation (in category 'accessing') -----
translation
	"answer the translation for the selected phrase"
	self selectedTranslation isZero
		ifTrue: [^ '<select a phrase from the upper list>' translated].
	""
	^ self translator
		translate: (self translations at: self selectedTranslation)!

----- Method: LanguageEditor>>translation: (in category 'accessing') -----
translation: aStringOrText 
	"change the translation for the selected phrase"
	| phrase |
	self selectedTranslation isZero
		ifTrue: [^ self].
	phrase := self translations at: self selectedTranslation.
	translator
		phrase: phrase
		translation: aStringOrText asString.
	newerKeys add: phrase.
	^ true!

----- Method: LanguageEditor>>translations (in category 'accessing') -----
translations
	"answet the translator's translations"
	| allTranslations filterString |
	translations ifNotNil: [^translations].
	allTranslations := self translator translations keys.
	""
	filterString := self translationsFilter.
	""
	filterString isEmpty
		ifFalse: [allTranslations := allTranslations
						select: [:each | ""
							('*' , filterString , '*' match: each)
								or: ['*' , filterString , '*'
										match: (self translator translate: each)]]].
""
	^ translations := allTranslations asSortedCollection asArray!

----- Method: LanguageEditor>>translationsFilter (in category 'accessing') -----
translationsFilter
^translationsFilter ifNil:['']!

----- Method: LanguageEditor>>translationsFilterWording (in category 'gui methods') -----
translationsFilterWording
	^ (self translationsFilter isEmpty
		ifTrue: ['filter' translated]
		ifFalse: ['filtering: {1}' translated format:{self translationsFilter}]) !

----- Method: LanguageEditor>>translationsKeystroke: (in category 'gui methods') -----
translationsKeystroke: aChar 
	"Respond to a Command key in the translations list."
	aChar == $x
		ifTrue: [^ self removeTranslation].
	aChar == $E
		ifTrue: [^ self browseMethodsWithTranslation]!

----- Method: LanguageEditor>>translationsMenu: (in category 'gui methods') -----
translationsMenu: aMenu 
	^ aMenu add: 'remove (x)' translated action: #removeTranslation;
		 add: 'where (E)' translated action: #browseMethodsWithTranslation;
		 add: 'select all' translated action: #selectAllTranslation;
		 add: 'deselect all' translated action: #deselectAllTranslation;
		 add: 'select changed keys' translated action: #selectNewerKeys;
		 add: 'export selection' translated action: #codeSelectedTranslation;
		 add: 'export selection in do-it form' translated action: #codeSelectedTranslationAsMimeString;
		 add: 'reset changed keys' translated action: #resetNewerKeys;
		 yourself!

----- Method: LanguageEditor>>translator (in category 'private') -----
translator
	^translator!

----- Method: LanguageEditor>>untranslated (in category 'accessing') -----
untranslated
	"answer the translator's untranslated phrases"
	

	| all filterString |
	untranslated ifNotNil: [^ untranslated].
	all := self translator untranslated.
	""
	filterString := self untranslatedFilter.
	""
	filterString isEmpty
		ifFalse: [all := all
						select: [:each | ""
							('*' , filterString , '*' match: each)
								or: ['*' , filterString , '*'
										match: (self translator translate: each)]]].
	""
	^ untranslated := all asSortedCollection asArray!

----- Method: LanguageEditor>>untranslatedFilter (in category 'accessing') -----
untranslatedFilter
	^ untranslatedFilter
		ifNil: ['']!

----- Method: LanguageEditor>>untranslatedFilterWording (in category 'gui methods') -----
untranslatedFilterWording
	^ self untranslatedFilter isEmpty
		ifTrue: ['filter' translated]
		ifFalse: ['filtering: {1}' translated format: {self untranslatedFilter}]!

----- Method: LanguageEditor>>untranslatedKeystroke: (in category 'gui methods') -----
untranslatedKeystroke: aChar 
	"Respond to a Command key in the translations list."
	aChar == $t
		ifTrue: [^ self translate].
	aChar == $E
		ifTrue: [^ self browseMethodsWithUntranslated]!

----- Method: LanguageEditor>>untranslatedMenu: (in category 'gui methods') -----
untranslatedMenu: aMenu 
	^ aMenu add: 'remove' translated action: #removeUntranslated;
		 add: 'translate (t)' translated action: #translate;
		 add: 'where (E)' translated action: #browseMethodsWithUntranslated;
		 yourself!

----- Method: LanguageEditor>>update: (in category 'updating') -----
update: aSymbol 
	"Receive a change notice from an object of whom the receiver  
	is a dependent."
	super update: aSymbol.
	""
	aSymbol == #untranslated
		ifTrue: [self refreshUntranslated].
	aSymbol == #translations
		ifTrue: [self refreshTranslations]!

----- Method: LanguageEditor>>withUnboundModelDo: (in category 'private') -----
withUnboundModelDo: aBlock 
	"Private - Evaluate aBlock with the receiver temporary  
	unbound from the model.  
	 
	Useful to perform a batch of modifications to the model  
	without updating the view."
	| formerModel |
	formerModel := self model.
	self model: nil.
	[aBlock value: formerModel]
		ensure: [self model: formerModel]!



More information about the Squeak-dev mailing list