[Pkg] The Trunk: GetText-edc.1.mcz

commits at source.squeak.org commits at source.squeak.org
Wed Mar 14 22:27:23 UTC 2012


Edgar J. De Cleene uploaded a new version of GetText to project The Trunk:
http://source.squeak.org/trunk/GetText-edc.1.mcz

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

Name: GetText-edc.1
Author: edc
Time: 13 March 2012, 10:16:32.385 am
UUID: 39781d00-6f75-4ec4-9033-48a1799d75b9
Ancestors: 

empty log message

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

SystemOrganization addCategory: #'GetText-Editor'!
SystemOrganization addCategory: #'GetText-Localization'!

----- Method: String>>literalStringsDo: (in category '*gettext') -----
literalStringsDo: aBlock 
	"Assuming the receiver receiver is a literal, evaluate aBlock with all Strings (but not Symbols) within it."
	aBlock value: self!

----- Method: String>>translated (in category '*gettext') -----
translated
	"answer the receiver translated to the default language"
	^ self
		translatedTo: LocaleID current
		inDomain: (TextDomainManager domainOfMethod: thisContext sender method) !

----- Method: String>>translatedIfCorresponds (in category '*gettext') -----
translatedIfCorresponds
	"answer the receiver translated to the default language only if 
	the receiver begins and ends with an underscore (_)"
	^ ('_*_' match: self)
		ifTrue: [(self copyFrom: 2 to: self size - 1) translated]
		ifFalse: [self]!

----- Method: String>>translatedInAnyDomain (in category '*gettext') -----
translatedInAnyDomain
| translation |
Transcript show: self printString, ' translatedInAnyDomain'; cr.
TextDomainManager allKnownDomains do: [:domain |
	translation := self translatedInDomain: domain.
	self = translation ifFalse: [^translation]
].
^self!

----- Method: String>>translatedInDomain: (in category '*gettext') -----
translatedInDomain: aDomainName
^self translatedTo: LocaleID current inDomain: aDomainName
!

----- Method: String>>translatedNoop (in category '*gettext') -----
translatedNoop
	"This is correspondence gettext_noop() in gettext."
	^ self!

----- Method: String>>translatedTo: (in category '*gettext') -----
translatedTo: localeID 
	"answer the receiver translated to the given locale id"
	^ self translatedTo: localeID inDomain: (TextDomainManager domainOfMethod: thisContext sender method).!

----- Method: String>>translatedTo:inDomain: (in category '*gettext') -----
translatedTo: localeID inDomain: aDomainName
	"answer the receiver translated to the given locale id in the textdomain"

	^ NaturalLanguageTranslator translate: self 
								toLocaleID: localeID 
								inDomain:  aDomainName!

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 := FillInTheBlank
				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 roundTo: 0.01.
					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 roundTo: 0.01.
					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 := FillInTheBlank 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 := FillInTheBlank 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: [GetTextExporter2 new exportTranslator: self model]!

----- Method: LanguageEditor>>getTextExportTemplate (in category 'gui methods') -----
getTextExportTemplate
	Cursor wait
		showWhile: [GetTextExporter2 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 := FillInTheBlank 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: [FillInTheBlank
				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 := FillInTheBlank 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 := FillInTheBlank 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 := FillInTheBlank 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 := FillInTheBlank
				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]!

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

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

"Export gettext template files"
GetTextExporter2 new exportTemplate.

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

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

"To register a class category as a new domain"
TextDomainManager registerClassCategory: 'Morphic-Books' domain: 'Book'.
"Remove a class category"
TextDomainManager unregisterClassCategory: 'Morphic-Books'.!

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

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

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

----- Method: GetTextExporter2 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: GetTextExporter2 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.
	GetTextExporter2 new exportTranslator: src.
	InternalTranslator removeLocaleID: localeID.
	dst := localeID translator.
	GetTextImporter import: dst allDirectory: FileDirectory default!

----- Method: GetTextExporter2 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: GetTextExporter2>>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 _ self getTextDomainForPackage:
			(PackageOrganizer default packageOfMethod: methodReference ifNone: [TextDomainManager defaultDomain]).
		literals _ domains at: domainName ifAbsentPut: [Dictionary new].
		keywords do: [ :literal |
			references _ literals at: literal ifAbsentPut: [OrderedCollection new].
			references add: methodReference.
		].
	]. 
!

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

----- Method: GetTextExporter2>>appendVocabularies: (in category 'private') -----
appendVocabularies: domains
	| literalsForDomain references domainName methodReference |
	
	EToyVocabulary allPhrasesWithContextToTranslate do: [ :r |
		methodReference :=  (MethodReference new setStandardClass: (r second) methodSymbol: (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: GetTextExporter2>>createExtraInformation (in category 'private') -----
createExtraInformation
	| extras |
	extras := OrderedCollection new.
	#(
		'Language name as you''d like it to appear in the Languages menu' 'Language-Name'
		'Scale to apply to font size (2 for twice as large)' 'Font-Scale'
		'Directionality of language' 'Language-Direction'
		'Use this if you do not want any of the text to be bolded, for legibility' 'Suppress-Bold'
		'Font to use on a Windows system' 'Win-Font'
		'Font to use on a Mac system' 'Mac-Font'
		'Font to use on a Linux system' 'Linux-Font') pairsDo: [:first :second |
			extras add: (Array with: '' with: first with: second).
	].
	^ extras!

----- Method: GetTextExporter2>>createHeaders (in category 'private') -----
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 add: 'X-Etoys-SystemVersion' -> (SystemVersion current asString).
	^ headers!

----- Method: GetTextExporter2>>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: GetTextExporter2>>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: GetTextExporter2>>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 := translator
				ifNil: [domain , '.pot']
				ifNotNil: [translator localeID posixName , '.po'].
	dirName := 'po', FileDirectory slash, domain.
	pathName := dirName , FileDirectory slash , fileName.
	(FileDirectory default directoryNamed: dirName) assureExistence.
	^ pathName!

----- Method: GetTextExporter2>>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.
	self exportInformation: self createExtraInformation.
	self exportBody: literals translator: translator]
		ensure: [stream close]!

----- Method: GetTextExporter2>>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: GetTextExporter2>>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: GetTextExporter2>>exportHeaderLineKey:value: (in category 'private') -----
exportHeaderLineKey: keyString value: valueString 
	stream nextPut: $";
		 nextPutAll: keyString;
		 nextPut: $:;
		 space;
		 nextPutAll: valueString;
		 nextPutAll: '\n';
		 nextPut: $";
		 cr.!

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

----- Method: GetTextExporter2>>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: GetTextExporter2>>exportRecordHeader: (in category 'private') -----
exportRecordHeader: context
	stream 
		nextPutAll: '#: ';
		nextPutAll: context;
		cr.!

----- Method: GetTextExporter2>>exportTag:msg: (in category 'private') -----
exportTag: tag msg: aString 
	| pos end line |
	(aString indexOf: Character cr)
			= 0
		ifTrue: [self exportTag: tag singleLine: aString]
		ifFalse: [self exportTag: tag singleLine: ''.
			pos := 1.
			end := 0.
			[end < aString size]
				whileTrue: [end := aString indexOf: Character cr startingAt: pos.
					end = 0
						ifTrue: [end := aString size].
					line := aString copyFrom: pos to: end.
					stream nextPut: $";
						
						nextPutAll: (self formatString: line);
						 nextPut: $";
						 cr.
					pos := end + 1]]!

----- Method: GetTextExporter2>>exportTag:singleLine: (in category 'private') -----
exportTag: tag singleLine: aString 
	stream nextPutAll: tag.
	stream space.
	stream nextPut: $".
	stream
		nextPutAll: (self formatString: aString).
	stream nextPut: $".
	stream cr!

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

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

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

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

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

----- Method: GetTextExporter2>>translationFor:in: (in category 'private') -----
translationFor: aKey in: translator
	translator ifNil: [^''].
	translator translations at: aKey ifPresent: [:s | ^s].
	"If we have old camelCase translations, make space-separated words"
	translator translations at: aKey toCamelCase ifPresent: [:s | 
		(s includes: Character space) ifTrue: [^s].
		^s fromCamelCase].
	^''!

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

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

!GetTextExporter commentStamp: 'tak 7/23/2007 00:31' prior: 0!
Export translations to one .po file.

GetTextExporter new export: NaturalLanguageTranslator current
!

----- 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-headers') -----
currentDateAndTime
	^ String
		streamContents: [:aStream | 
			aStream nextPutAll: Date today yyyymmdd;
				space.
			Time now
				print24: true
				showSeconds: false
				on: aStream.
			aStream nextPutAll: '-0000']!

----- Method: GetTextExporter>>export: (in category 'exporting') -----
export: aLanguage 
	language := aLanguage.
	[stream := FileStream forceNewFileNamed: self defaultFileName.
	stream lineEndConvention: #lf.
	stream converter: UTF8TextConverter new.
	self exportHeader.
	self exportTranslations.
	self exportUntranslated]
		ensure: [stream close]!

----- 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>>exportHeaderLineKey:value: (in category 'private-headers') -----
exportHeaderLineKey: keyString value: valueString 
	stream nextPut: $";
		 nextPutAll: keyString;
		 nextPut: $:;
		 space;
		 nextPutAll: valueString;
		 nextPutAll: '\n';
		 nextPut: $";
		 cr!

----- Method: GetTextExporter>>exportPhrase:translation: (in category 'private-translations') -----
exportPhrase: phraseString translation: translationString 
	phraseString isEmpty
		ifTrue: [^ self].
	self exportTag: 'msgid' msg: phraseString.
	self exportTag: 'msgstr' msg: translationString.
	stream cr!

----- Method: GetTextExporter>>exportTag:msg: (in category 'private') -----
exportTag: tag msg: aString 
	| pos end line |
	(aString indexOf: Character cr)
			= 0
		ifTrue: [self exportTag: tag singleLine: aString]
		ifFalse: [self exportTag: tag singleLine: ''.
			pos := 1.
			end := 0.
			[end < aString size]
				whileTrue: [end := aString indexOf: Character cr startingAt: pos.
					end = 0
						ifTrue: [end := aString size].
					line := aString copyFrom: pos to: end.
					stream nextPut: $";
						
						nextPutAll: (self formatString: line);
						 nextPut: $";
						 cr.
					pos := end + 1]]!

----- Method: GetTextExporter>>exportTag:singleLine: (in category 'private') -----
exportTag: tag singleLine: aString 
	stream nextPutAll: tag.
	stream space.
	stream nextPut: $".
	stream
		nextPutAll: (self formatString: aString).
	stream nextPut: $".
	stream cr!

----- Method: GetTextExporter>>exportTranslations (in category 'private-translations') -----
exportTranslations
	| keys size |
	keys := language translations keys asArray sort.
	size := keys size.
	ProgressInitiationException
		display: 'Exporting translated phrases as a gettext file.'
		during: [:bar | 1
				to: size
				do: [:index | 
					self
						exportPhrase: (keys at: index)
						translation: (language translations
								at: (keys at: index)).
					bar value: index / size]]!

----- Method: GetTextExporter>>exportUntranslated (in category 'private-translations') -----
exportUntranslated
	| keys |
	keys := language untranslated keys asArray sort.
	ProgressInitiationException
		display: 'Exporting untranslated phrases as a gettext file.'
		at: Sensor cursorPoint
		from: 1
		to: keys size
		during: [:bar | 1
				to: keys size
				do: [:index | 
					self
						exportPhrase: (keys at: index)
						translation: ''.
					bar value: index]]!

----- 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!

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!

Object subclass: #ISOLanguageDefinition
	instanceVariableNames: 'iso3 iso2 iso3Alternate language'
	classVariableNames: 'ISO2Countries ISO2Table ISO3Countries ISO3Table'
	poolDictionaries: ''
	category: 'GetText-Localization'!

----- Method: ISOLanguageDefinition class>>buildIso3166CodesTables (in category 'private') -----
buildIso3166CodesTables
	"ISOLanguageDefinition buildIso3166CodesTables"
	| rawdata stream country isoa2 isoa3 unNumeric macName macCode windowsName windowsCode empty table |
	rawdata := self iso3166Codes.
	table := OrderedCollection new: 200. 
	stream := rawdata readStream.
	empty := 160 asCharacter asString.
	[stream atEnd] whileFalse: 
		[country := stream nextLine.
		isoa2 := stream nextLine.
		isoa3 := stream nextLine.
		unNumeric := stream nextLine.
		windowsName := stream nextLine.
		windowsName = empty ifTrue: [windowsName := nil].
		windowsCode := stream nextLine. 
		windowsCode = empty ifTrue: [windowsCode := nil].
		macName := stream nextLine.
		macName = empty ifTrue: [macName := nil].
		macCode := stream nextLine.
		macCode = empty ifTrue: [macCode := nil].
		table add: { country.  isoa2. isoa3.  unNumeric. windowsName.  windowsCode.  macName. macCode. }].
	^table!

----- Method: ISOLanguageDefinition class>>extraCountryDefinitions (in category 'private') -----
extraCountryDefinitions
	^{
	{'Kids'. 'KIDS'. 'KIDS'.}.
	}!

----- Method: ISOLanguageDefinition class>>extraISO3Definitions (in category 'private') -----
extraISO3Definitions

	^self readISOLanguagesFrom: 'jpk		Japanese (Kids)
' readStream!

----- Method: ISOLanguageDefinition class>>initISO3LanguageTable (in category 'private') -----
initISO3LanguageTable
	"ISOLanguageDefinition initIso3LanguageTable"

	| table |
	table := ISOLanguageDefinition readISOLanguagesFrom: ISOLanguageDefinition isoLanguages readStream.
	table addAll: self extraISO3Definitions.
	^table!

----- Method: ISOLanguageDefinition class>>initISOCountries (in category 'private') -----
initISOCountries
	"ISOLanguageDefinition initISOCountries"
	| iso3166Table |
	iso3166Table := ISOLanguageDefinition buildIso3166CodesTables.
	ISO2Countries := Dictionary new.
	ISO3Countries := Dictionary new.
	iso3166Table do: [:entry | 
		ISO2Countries at: (entry at: 2) put: (entry at: 1).
		ISO3Countries at: (entry at: 3) put: (entry at: 1)].
	self extraCountryDefinitions do: [:entry | 
		ISO2Countries at: (entry at: 2) put: (entry at: 1).
		ISO3Countries at: (entry at: 3) put: (entry at: 1)]!

----- Method: ISOLanguageDefinition class>>initialize (in category 'class initialization') -----
initialize
	"ISOLanguageDefinition initialize"

	ISO3Table := nil.
	ISO2Table := nil!

----- Method: ISOLanguageDefinition class>>iso2Countries (in category 'private') -----
iso2Countries
	"ISOLanguageDefinition iso2Countries"
	"ISO2Countries := nil. ISO3Countries := nil"

	ISO2Countries ifNil: [self initISOCountries].
	^ISO2Countries!

----- Method: ISOLanguageDefinition class>>iso2LanguageDefinition: (in category 'accessing') -----
iso2LanguageDefinition: aString
	^self iso2LanguageTable at: aString!

----- Method: ISOLanguageDefinition class>>iso2LanguageTable (in category 'private') -----
iso2LanguageTable
	"ISOLanguageDefinition iso2LanguageTable"

	ISO2Table ifNotNil: [^ISO2Table].
	ISO2Table := Dictionary new: self iso3LanguageTable basicSize.
	self iso3LanguageTable do: [:entry |
		ISO2Table at: entry iso2 put: entry].
	^ISO2Table!

----- Method: ISOLanguageDefinition class>>iso3166Codes (in category 'private') -----
iso3166Codes
"http://www.unicode.org/onlinedat/countries.html"

^'ÅLAND ISLANDS
AX


 
 
 
 
AFGHANISTAN
AF
AFG
004
 
 
 
 
ALBANIA
AL
ALB
008
CTRY_ALBANIA
355
 
 
ALGERIA
DZ
DZA
012
CTRY_ALGERIA
213
verArabic
16
AMERICAN SAMOA
AS
ASM
016
 
 
 
 
ANDORRA
AD
AND
020
 
 
 
 
ANGOLA
AO
AGO
024
 
 
 
 
ANGUILLA
AI
AIA
660
 
 
 
 
ANTARCTICA
AQ
ATA
010
 
 
 
 
ANTIGUA AND BARBUDA
AG
ATG
028
 
 
 
 
ARGENTINA
AR
ARG
032
CTRY_ARGENTINA
54
 
 
ARMENIA
AM
ARM
051
CTRY_ARMENIA
374
verArmenian
84
ARUBA
AW
ABW
533
 
 
 
 
AUSTRALIA
AU
AUS
036
CTRY_AUSTRALIA
61
verAustralia
15
AUSTRIA
AT
AUT
040
CTRY_AUSTRIA
43
verAustria
92
AZERBAIJAN
AZ
AZE
031
CTRY_AZERBAIJAN
994
 
 
BAHAMAS
BS
BHS
044
 
 
 
 
BAHRAIN
BH
BHR
048
CTRY_BAHRAIN
973
 
 
BANGLADESH
BD
BGD
050
 
 
verBengali
60
BARBADOS
BB
BRB
052
 
 
 
 
BELARUS
BY
BLR
112
CTRY_BELARUS
375
 
 
BELGIUM
BE
BEL
056
CTRY_BELGIUM
32
verFrBelgium, verFlemish
98
BELIZE
BZ
BLZ
084
CTRY_BELIZE
501
 
 
BENIN
BJ
BEN
204
 
 
 
 
BERMUDA
BM
BMU
060
 
 
 
 
BHUTAN
BT
BTN
064
 
 
verBhutan
83
BOLIVIA
BO
BOL
068
CTRY_BOLIVIA
591
 
 
BOSNIA AND HERZEGOVINA
BA
BIH
070
 
 
 
 
BOTSWANA
BW
BWA
072
 
 
 
 
BOUVET ISLAND
BV
BVT
074
 
 
 
 
BRAZIL
BR
BRA
076
CTRY_BRAZIL
55
verBrazil
71
BRITISH INDIAN OCEAN TERRITORY
IO
IOT
086
 
 
 
 
BRUNEI DARUSSALAM
BN
BRN
096
CTRY_BRUNEI_DARUSSALAM
673
 
 
BULGARIA
BG
BGR
100
CTRY_BULGARIA
359
verBulgaria 
72
BURKINA FASO
BF
BFA
854
 
 
 
 
BURUNDI
BI
BDI
108
 
 
 
 
CAMBODIA
KH
KHM
116
 
 
 
 
CAMEROON
CM
CMR
120
 
 
 
 
CANADA
CA
CAN
124
CTRY_CANADA
2
verFrCanada, verEndCanada
82
CAPE VERDE
CV
CPV
132
 
 
 
 
CAYMAN ISLANDS
KY
CYM
136
 
 
 
 
CENTRAL AFRICAN REPUBLIC
CF
CAF
140
 
 
 
 
CHAD
TD
TCD
148
 
 
 
 
CHILE
CL
CHL
152
CTRY_CHILE
56
 
 
CHINA
CN
CHN
156
CTRY_PRCHINA
86
verChina
52
CHRISTMAS ISLAND
CX
CXR
162
 
 
 
 
COCOS (KEELING) ISLANDS
CC
CCK
166
 
 
 
 
COLOMBIA
CO
COL
170
CTRY_COLOMBIA
57
 
 
COMOROS
KM
COM
174
 
 
 
 
CONGO
CG
COG
178
 
 
 
 
CONGO, THE DEMOCRATIC REPUBLIC OF THE
CD


 
 
 
 
COOK ISLANDS
CK
COK
184
 
 
 
 
COSTA RICA
CR
CRI
188
CTRY_COSTA_RICA
506
 
 
COTE D''IVOIRE
CI
CIV
384
 
 
 
 
CROATIA (local name: Hrvatska)
HR
HRV
191
CTRY_CROATIA
385
verCroatia, verYugoCroatian
68 (c), 25 (y)
CUBA
CU
CUB
192
 
 
 
 
CYPRUS
CY
CYP
196
 
 
verCyprus
23
CZECH REPUBLIC
CZ
CZE
203
CTRY_CZECH
420
verCzech 
56
DENMARK
DK
DNK
208
CTRY_DENMARK
45
verDenmark(da), verFaeroeIsl(fo)
9(da), 47(fo)
DJIBOUTI
DJ
DJI
262
 
 
 
 
DOMINICA
DM
DMA
212
 
 
 
 
DOMINICAN REPUBLIC
DO
DOM
214
CTRY_DOMINICAN_REPUBLIC
1
 
 
EAST TIMOR
TL
TLS
626
 
 
 
 
ECUADOR
EC
ECU
218
CTRY_ECUADOR
593
 
 
EGYPT
EG
EGY
818
CTRY_EGYPT
20
verArabic
16
EL SALVADOR
SV
SLV
222
CTRY_EL_SALVADOR
503
 
 
EQUATORIAL GUINEA
GQ
GNQ
226
 
 
 
 
ERITREA
ER
ERI
232
 
 
 
 
ESTONIA
EE
EST
233
CTRY_ESTONIA
372
verEstonia
44
ETHIOPIA
ET
ETH
210
 
 
 
 
FALKLAND ISLANDS (MALVINAS)
FK
FLK
238
 
 
 
 
FAROE ISLANDS
FO
FRO
234
CTRY_FAEROE_ISLANDS
298
 
 
FIJI
FJ
FJI
242
 
 
 
 
FINLAND
FI
FIN
246
CTRY_FINLAND
358
verFinland
17
FRANCE
FR
FRA
250
CTRY_FRANCE
33
verFrance
1
FRANCE, METROPOLITAN
FX
FXX
249
 
 
 
 
FRENCH GUIANA
GF
GUF
254
 
 
 
 
FRENCH POLYNESIA
PF
PYF
258
 
 
 
 
FRENCH SOUTHERN TERRITORIES
TF
ATF
260
 
 
 
 
GABON
GA
GAB
266
 
 
 
 
GAMBIA
GM
GMB
270
 
 
 
 
GEORGIA
GE
GEO
268
CTRY_GEORGIA
995
verGeorgian
85
GERMANY
DE
DEU
276
CTRY_GERMANY
49
verGermany
3
GHANA
GH
GHA
288
 
 
 
 
GIBRALTAR
GI
GIB
292
 
 
 
 
GREECE
GR
GRC
300
CTRY_GREECE
30
verGreece, verGreecePoly
20, 40
GREENLAND
GL
GRL
304
 
 
verGreenland
107
GRENADA
GD
GRD
308
 
 
 
 
GUADELOUPE
GP
GLP
312
 
 
 
 
GUAM
GU
GUM
316
 
 
 
 
GUATEMALA
GT
GTM
320
CTRY_GUATEMALA
502
 
 
GUINEA
GN
GIN
324
 
 
 
 
GUINEA-BISSAU
GW
GNB
624
 
 
 
 
GUYANA
GY
GUY
328
 
 
 
 
HAITI
HT
HTI
332
 
 
 
 
HEARD ISLAND & MCDONALD ISLANDS
HM
HMD
334
 
 
 
 
HONDURAS
HN
HND
340
CTRY_HONDURAS
504
 
 
HONG KONG
HK
HKG
344
CTRY_HONG_KONG
852
 
 
HUNGARY
HU
HUN
348
CTRY_HUNGARY
36
verHungary
43
ICELAND
IS
ISL
352
CTRY_ICELAND
354
verIceland
21
INDIA
IN
IND
356
CTRY_INDIA
91
verIndiaHindi(hi)
33
INDONESIA
ID
IDN
360
CTRY_INDONESIA
62
 
 
IRAN, ISLAMIC REPUBLIC OF
IR
IRN
364
CTRY_IRAN
981
verIran
48
IRAQ
IQ
IRQ
368
CTRY_IRAQ
964
verArabic
16
IRELAND
IE
IRL
372
CTRY_IRELAND
353
verIreland
50
ISRAEL
IL
ISR
376
CTRY_ISRAEL
972
verIsrael
13
ITALY
IT
ITA
380
CTRY_ITALY
39
verItaly
4
JAMAICA
JM
JAM
388
CTRY_JAMAICA
1
 
 
JAPAN
JP
JPN
392
CTRY_JAPAN
81
verJapan
14
JORDAN
JO
JOR
400
CTRY_JORDAN
962
 
 
KAZAKHSTAN
KZ
KAZ
398
CTRY_KAZAKSTAN
7
 
 
KENYA
KE
KEN
404
CTRY_KENYA
254
 
 
KIRIBATI
KI
KIR
296
 
 
 
 
KOREA, DEMOCRATIC PEOPLE''S REPUBLIC OF
KP
PRK
408
 
 
verKorea
51
KOREA, REPUBLIC OF
KR
KOR
410
CTRY_SOUTH_KOREA
82
verKorea
 
KUWAIT
KW
KWT
414
CTRY_KUWAIT
965
 
 
KYRGYZSTAN
KG
KGZ
417
CTRY_KYRGYZSTAN
996
 
 
LAO PEOPLE''S DEMOCRATIC REPUBLIC
LA
LAO
418
 
 
 
 
LATVIA
LV
LVA
428
CTRY_LATVIA
371
verLatvia
45
LEBANON
LB
LBN
422
CTRY_LEBANON
961
 
 
LESOTHO
LS
LSO
426
 
 
 
 
LIBERIA
LR
LBR
430
 
 
 
 
LIBYAN ARAB JAMAHIRIYA
LY
LBY
434
CTRY_LIBYA
218
verArabic
16
LIECHTENSTEIN
LI
LIE
438
CTRY_LIECHTENSTEIN
41
 
 
LITHUANIA
LT
LTU
440
CTRY_LITHUANIA
370
verLithuania
41
LUXEMBOURG
LU
LUX
442
CTRY_LUXEMBOURG
352
verFrBelgiumLux
6
MACAU
MO
MAC
446
CTRY_MACAU
853
 
 
MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF
MK
MKD
807
CTRY_MACEDONIA
389
verMacedonian
 
MADAGASCAR
MG
MDG
450
 
 
 
 
MALAWI
MW
MWI
454
 
 
 
 
MALAYSIA
MY
MYS
458
CTRY_MALAYSIA
60
 
 
MALDIVES
MV
MDV
462
CTRY_MALDIVES
960
 
 
MALI
ML
MLI
466
 
 
 
 
MALTA
MT
MLT
470
 
 
verMalta
22
MARSHALL ISLANDS
MH
MHL
584
 
 
 
 
MARTINIQUE
MQ
MTQ
474
 
 
 
 
MAURITANIA
MR
MRT
478
 
 
 
 
MAURITIUS
MU
MUS
480
 
 
 
 
MAYOTTE
YT
MYT
175
 
 
 
 
MEXICO
MX
MEX
484
CTRY_MEXICO
52
 
 
MICRONESIA, FEDERATED STATES OF
FM
FSM
583
 
 
 
 
MOLDOVA, REPUBLIC OF
MD
MDA
498
 
 
 
 
MONACO
MC
MCO
492
CTRY_MONACO
33
 
 
MONGOLIA
MN
MNG
496
CTRY_MONGOLIA
976
 
 
MONTSERRAT
MS
MSR
500
 
 
 
 
MOROCCO
MA
MAR
504
CTRY_MOROCCO
212
verArabic
16
MOZAMBIQUE
MZ
MOZ
508
 
 
 
 
MYANMAR
MM
MMR
104
 
 
 
 
NAMIBIA
NA
NAM
516
 
 
 
 
NAURU
NR
NRU
520
 
 
 
 
NEPAL
NP
NPL
524
 
 
verNepal
106
NETHERLANDS
NL
NLD
528
CTRY_NETHERLANDS
31
verNetherlands
5
NETHERLANDS ANTILLES
AN
ANT
530
 
 
 
 
NEW CALEDONIA
NC
NCL
540
 
 
 
 
NEW ZEALAND
NZ
NZL
554
CTRY_NEW_ZEALAND
64
 
 
NICARAGUA
NI
NIC
558
CTRY_NICARAGUA
505
 
 
NIGER
NE
NER
562
 
 
 
 
NIGERIA
NG
NGA
566
 
 
 
 
NIUE
NU
NIU
570
 
 
 
 
NORFOLK ISLAND
NF
NFK
574
 
 
 
 
NORTHERN MARIANA ISLANDS
MP
MNP
580
 
 
 
 
NORWAY
NO
NOR
578
CTRY_NORWAY
47
verNorway
12
OMAN
OM
OMN
512
CTRY_OMAN
968
 
 
PAKISTAN
PK
PAK
586
CTRY_PAKISTAN
92
verPakistanUrdu, verPunjabi
34 (U), 95 (P)
PALAU
PW
PLW
585
 
 
 
 
PANAMA
PA
PAN
591
CTRY_PANAMA
507
 
 
PALESTINIAN TERRITORY, OCCUPIED
PS




 
 
PAPUA NEW GUINEA
PG
PNG
598
 
 
 
 
PARAGUAY
PY
PRY
600
CTRY_PARAGUAY
595
 
 
PERU
PE
PER
604
CTRY_PERU
51
 
 
PHILIPPINES
PH
PHL
608
CTRY_PHILIPPINES
63
 
 
PITCAIRN
PN
PCN
612
 
 
 
 
POLAND
PL
POL
616
CTRY_POLAND
48
verPoland
42
PORTUGAL
PT
PRT
620
CTRY_PORTUGAL
351
verPortugal
10
PUERTO RICO
PR
PRI
630
CTRY_PUERTO_RICO
1
 
 
QATAR
QA
QAT
634
CTRY_QATAR
974
 
 
REUNION
RE
REU
638
 
 
 
 
ROMANIA
RO
ROU*
642
CTRY_ROMANIA
40
verRomania
39
RUSSIAN FEDERATION
RU
RUS
643
CTRY_RUSSIA
7
verRussia
49
RWANDA
RW
RWA
646
 
 
 
 
SAINT KITTS AND NEVIS
KN
KNA
659
 
 
 
 
SAINT LUCIA
LC
LCA
662
 
 
 
 
SAINT VINCENT AND THE GRENADINES
VC
VCT
670
 
 
 
 
SAMOA
WS
WSM
882
 
 
 
 
SAN MARINO
SM
SMR
674
 
 
 
 
SAO TOME AND PRINCIPE
ST
STP
678
 
 
 
 
SAUDI ARABIA
SA
SAU
682
CTRY_SAUDI_ARABIA
966
verArabic
16
SENEGAL
SN
SEN
686
 
 
 
 
SERBIA AND MONTENEGRO
CS
 
 
CTRY_SERBIA
381
 
 
SEYCHELLES
SC
SYC
690
 
 
 
 
SIERRA LEONE
SL
SLE
694
 
 
 
 
SINGAPORE
SG
SGP
702
CTRY_SINGAPORE
65
verSingapore
100
SLOVAKIA (Slovak Republic)
SK
SVK
703
CTRY_SLOVAK
421
verSlovak
57 
SLOVENIA
SI
SVN
705
CTRY_SLOVENIA
386
verSlovenian
66
SOLOMON ISLANDS
SB
SLB
90
 
 
 
 
SOMALIA
SO
SOM
706
 
 
 
 
SOUTH AFRICA
ZA
ZAF
710
CTRY_SOUTH_AFRICA
27
 
 
SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS
GS




 
 
SPAIN
ES
ESP
724
CTRY_SPAIN
34
verSpain
8
SRI LANKA
LK
LKA
144
 
 
 
 
SAINT HELENA
SH
SHN
654
 
 
 
 
SAINT PIERRE AND MIQUELON
PM
SPM
666
 
 
 
 
SUDAN
SD
SDN
736
 
 
 
 
SURINAME
SR
SUR
740
 
 
 
 
SVALBARD AND JAN MAYEN ISLANDS
SJ
SJM
744
 
 
 
 
SWAZILAND
SZ
SWZ
748
 
 
 
 
SWEDEN
SE
SWE
752
CTRY_SWEDEN
46
verSweden
7
SWITZERLAND
CH
CHE
756
CTRY_SWITZERLAND
41
verFrSwiss(fr), verGrSwiss(de)
18(fr), 19(de)
SYRIAN ARAB REPUBLIC
SY
SYR
760
CTRY_SYRIA
963
 
 
TAIWAN, PROVINCE OF CHINA
TW
TWN
158
CTRY_TAIWAN
886
verTaiwan
53
TAJIKISTAN
TJ
TJK
762
 
 
 
 
TANZANIA, UNITED REPUBLIC OF
TZ
TZA
834
 
 
 
 
TATARSTAN


 
CTRY_TATARSTAN
7
 
 
THAILAND
TH
THA
764
CTRY_THAILAND
66
verThailand
54
TIMOR-LESTE
TL


 
 
 
 
TOGO
TG
TGO
768
 
 
 
 
TOKELAU
TK
TKL
772
 
 
 
 
TONGA
TO
TON
776
 
 
verTonga
88
TRINIDAD AND TOBAGO
TT
TTO
780
CTRY_TRINIDAD_Y_TOBAGO
1
 
 
TUNISIA
TN
TUN
788
CTRY_TUNISIA
216
verArabic
16
TURKEY
TR
TUR
792
CTRY_TURKEY
90
verTurkey
24
TURKMENISTAN
TM
TKM
795
 
 
 
 
TURKS AND CAICOS ISLANDS
TC
TCA
796
 
 
 
 
TUVALU
TV
TUV
798
 
 
 
 
UGANDA
UG
UGA
800
 
 
 
 
UKRAINE
UA
UKR
804
CTRY_UKRAINE
380
verUkraine 
62
UNITED ARAB EMIRATES
AE
ARE
784
CTRY_UAE
971
 
 
UNITED KINGDOM
GB
GBR
826
CTRY_UNITED_KINGDOM
44
verBritain
2
UNITED STATES
US
USA
840
CTRY_UNITED_STATES
1
verUS
0
UNITED STATES MINOR OUTLYING ISLANDS
UM
UMI
581
 
 
 
 
URUGUAY
UY
URY
858
CTRY_URUGUAY
598
 
 
UZBEKISTAN
UZ
UZB
860
CTRY_UZBEKISTAN
7
 
 
VANUATU
VU
VUT
548
 
 
 
 
VATICAN CITY STATE (HOLY SEE)
VA
VAT
336
 
 
 
 
VENEZUELA
VE
VEN
862
CTRY_VENEZUELA
58
 
 
VIET NAM
VN
VNM
704
CTRY_VIET_NAM
84
verVietnam
 
VIRGIN ISLANDS (BRITISH)
VG
VGB
92
 
 
 
 
VIRGIN ISLANDS (U.S.)
VI
VIR
850
 
 
 
 
WALLIS AND FUTUNA ISLANDS
WF
WLF
876
 
 
 
 
WESTERN SAHARA
EH
ESH
732
 
 
 
 
YEMEN
YE
YEM
887
CTRY_YEMEN
967
 
 
YUGOSLAVIA
YU
YUG
891
 
 
 
 
ZAIRE
ZR
ZAR
180
 
 
 
 
ZAMBIA
ZM
ZMB
894
 
 
 
 
ZIMBABWE
ZW
ZWE
716
CTRY_ZIMBABWE
263
 
 
'!

----- Method: ISOLanguageDefinition class>>iso3Countries (in category 'private') -----
iso3Countries
	"ISOLanguageDefinition iso3Countries"
	"ISO2Countries := nil. ISO3Countries := nil"

	ISO3Countries ifNil: [self initISOCountries].
	^ISO3Countries!

----- Method: ISOLanguageDefinition class>>iso3LanguageDefinition: (in category 'accessing') -----
iso3LanguageDefinition: aString
	^self iso3LanguageTable at: aString!

----- Method: ISOLanguageDefinition class>>iso3LanguageTable (in category 'private') -----
iso3LanguageTable
	"ISOLanguageDefinition iso3LanguageTable"

	^ISO3Table ifNil: [ISO3Table := self initISO3LanguageTable]!

----- Method: ISOLanguageDefinition class>>isoLanguages (in category 'private') -----
isoLanguages
	"ISO 639: 3-letter codes"
	^'aar	aa	Afar
abk	ab	Abkhazian
ace		Achinese
ach		Acoli
ada		Adangme
ady		Adyghe; Adygei
afa		Afro-Asiatic languages
afh		Afrihili
afr	af	Afrikaans
ain		Ainu
aka	ak	Akan
akk		Akkadian
alb/sqi	sq	Albanian
ale		Aleut
alg		Algonquian languages
alt		Southern Altai
amh	am	Amharic
ang		English, Old (ca.450-1100)
anp		Angika
apa		Apache languages
ara	ar	Arabic
arc		Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)
arg	an	Aragonese
arm/hye	hy	Armenian
arn		Mapudungun; Mapuche
arp		Arapaho
art		Artificial languages
arw		Arawak
asm	as	Assamese
ast		Asturian; Bable; Leonese; Asturleonese
ath		Athapascan languages
aus		Australian languages
ava	av	Avaric
ave	ae	Avestan
awa		Awadhi
aym	ay	Aymara
aze	az	Azerbaijani
bad		Banda languages
bai		Bamileke languages
bak	ba	Bashkir
bal		Baluchi
bam	bm	Bambara
ban		Balinese
baq/eus	eu	Basque
bas		Basa
bat		Baltic languages
bej		Beja; Bedawiyet
bel	be	Belarusian
bem		Bemba
ben	bn	Bengali
ber		Berber languages)
bho		Bhojpuri
bih	bh	Bihari
bik		Bikol
bin		Bini; Edo
bis	bi	Bislama
bla		Siksika
bnt		Bantu languages
bos	bs	Bosnian
bra		Braj
bre	br	Breton
btk		Batak languages
bua		Buriat
bug		Buginese
bul	bg	Bulgarian
bur/mya	my	Burmese
byn		Blin; Bilin
cad		Caddo
cai		Central American Indian languages
car		Galibi Carib
cat	ca	Catalan; Valencian
cau		Caucasian languages
ceb		Cebuano
cel		Celtic languages
cha	ch	Chamorro
chb		Chibcha
che	ce	Chechen
chg		Chagatai
chi/zho	zh	Chinese
chk		Chuukese
chm		Mari
chn		Chinook jargon
cho		Choctaw
chp		Chipewyan; Dene Suline
chr		Cherokee
chu	cu	Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic
chv	cv	Chuvash
chy		Cheyenne
cmc		Chamic languages
cop		Coptic
cor	kw	Cornish
cos	co	Corsican
cpe		Creoles and pidgins, English based
cpf		Creoles and pidgins, French-based
cpp		Creoles and pidgins, Portuguese-based
cre	cr	Cree
crh		Crimean Tatar; Crimean Turkish
crp		Creoles and pidgins
csb		Kashubian
cus		Cushitic languages
cze/ces	cs	Czech
dak		Dakota
dan	da	Danish
dar		Dargwa
day		Land Dayak languages
del		Delaware
den		Slave (Athapascan)
dgr		Dogrib
din		Dinka
div	dv	Divehi; Dhivehi; Maldivian
doi		Dogri
dra		Dravidian languages
dsb		Lower Sorbian
dua		Duala
dum		Dutch, Middle (ca.1050-1350)
dut/nld	nl	Dutch; Flemish
dyu		Dyula
dzo	dz	Dzongkha
efi		Efik
egy		Egyptian (Ancient)
eka		Ekajuk
elx		Elamite
eng	en	English
enm		English, Middle (1100-1500)
epo	eo	Esperanto
est	et	Estonian
ewe	ee	Ewe
ewo		Ewondo
fan		Fang
fao	fo	Faroese
fat		Fanti
fij	fj	Fijian
fil		Filipino; Pilipino
fin	fi	Finnish
fiu		Finno-Ugrian languages)
fon		Fon
fre/fra	fr	French
frm		French, Middle (ca.1400-1600)
fro		French, Old (842-ca.1400)
frr		Northern Frisian
frs		Eastern Frisian
fry	fy	Western Frisian
ful	ff	Fulah
fur		Friulian
gaa		Ga
gay		Gayo
gba		Gbaya
gem		Germanic languages
geo/kat	ka	Georgian
ger/deu	de	German
gez		Geez
gil		Gilbertese
gla	gd	Gaelic; Scottish Gaelic
gle	ga	Irish
glg	gl	Galician
glv	gv	Manx
gmh		German, Middle High (ca.1050-1500)
goh		German, Old High (ca.750-1050)
gon		Gondi
gor		Gorontalo
got		Gothic
grb		Grebo
grc		Greek, Ancient (to 1453)
gre/ell	el	Greek, Modern (1453-)
grn	gn	Guarani
gsw		Swiss German; Alemannic; Alsatian
guj	gu	Gujarati
gwi		Gwich''in
hai		Haida
hat	ht	Haitian; Haitian Creole
hau	ha	Hausa
haw		Hawaiian
heb	he	Hebrew
her	hz	Herero
hil		Hiligaynon
him		Himachali
hin	hi	Hindi
hit		Hittite
hmn		Hmong
hmo	ho	Hiri Motu
hrv	hr	Croatian
hsb		Upper Sorbian
hun	hu	Hungarian
hup		Hupa
iba		Iban
ibo	ig	Igbo
ice/isl	is	Icelandic
ido	io	Ido
iii	ii	Sichuan Yi; Nuosu
ijo		Ijo languages
iku	iu	Inuktitut
ile	ie	Interlingue; Occidental
ilo		Iloko
ina	ia	Interlingua (International Auxiliary Language Association)
inc		Indic languages
ind	id	Indonesian
ine		Indo-European languages
inh		Ingush
ipk	ik	Inupiaq
ira		Iranian languages
iro		Iroquoian languages
ita	it	Italian
jav	jv	Javanese
jbo		Lojban
jpn	ja	Japanese
jpr		Judeo-Persian
jrb		Judeo-Arabic
kaa		Kara-Kalpak
kab		Kabyle
kac		Kachin; Jingpho
kal	kl	Kalaallisut; Greenlandic
kam		Kamba
kan	kn	Kannada
kar		Karen languages
kas	ks	Kashmiri
kau	kr	Kanuri
kaw		Kawi
kaz	kk	Kazakh
kbd		Kabardian
kha		Khasi
khi		Khoisan languages
khm	km	Central Khmer
kho		Khotanese; Sakan
kik	ki	Kikuyu; Gikuyu
kin	rw	Kinyarwanda
kir	ky	Kirghiz; Kyrgyz
kmb		Kimbundu
kok		Konkani
kom	kv	Komi
kon	kg	Kongo
kor	ko	Korean
kos		Kosraean
kpe		Kpelle
krc		Karachay-Balkar
krl		Karelian
kro		Kru languages
kru		Kurukh
kua	kj	Kuanyama; Kwanyama
kum		Kumyk
kur	ku	Kurdish
kut		Kutenai
lad		Ladino
lah		Lahnda
lam		Lamba
lao	lo	Lao
lat	la	Latin
lav	lv	Latvian
lez		Lezghian
lim	li	Limburgan; Limburger; Limburgish
lin	ln	Lingala
lit	lt	Lithuanian
lol		Mongo
loz		Lozi
ltz	lb	Luxembourgish; Letzeburgesch
lua		Luba-Lulua
lub	lu	Luba-Katanga
lug	lg	Ganda
lui		Luiseno
lun		Lunda
luo		Luo (Kenya and Tanzania)
lus		Lushai
mac/mkd	mk	Macedonian
mad		Madurese
mag		Magahi
mah	mh	Marshallese
mai		Maithili
mak		Makasar
mal	ml	Malayalam
man		Mandingo
mao/mri	mi	Maori
map		Austronesian languages
mar	mr	Marathi
mas		Masai
may/msa	ms	Malay
mdf		Moksha
mdr		Mandar
men		Mende
mga		Irish, Middle (900-1200)
mic		Mi''kmaq; Micmac
min		Minangkabau
mis		Uncoded languages
mkh		Mon-Khmer languages
mlg	mg	Malagasy
mlt	mt	Maltese
mnc		Manchu
mni		Manipuri
mno		Manobo languages
moh		Mohawk
mon	mn	Mongolian
mos		Mossi
mul		Multiple languages
mun		Munda languages
mus		Creek
mwl		Mirandese
mwr		Marwari
myn		Mayan languages
myv		Erzya
nah		Nahuatl languages
nai		North American Indian languages
nap		Neapolitan
nau	na	Nauru
nav	nv	Navajo; Navaho
nbl	nr	Ndebele, South; South Ndebele
nde	nd	Ndebele, North; North Ndebele
ndo	ng	Ndonga
nds		Low German; Low Saxon; German, Low; Saxon, Low
nep	ne	Nepali
new		Nepal Bhasa; Newari
nia		Nias
nic		Niger-Kordofanian languages
niu		Niuean
nno	nn	Norwegian Nynorsk; Nynorsk, Norwegian
nob	nb	Bokmal, Norwegian; Norwegian Bokmal
nog		Nogai
non		Norse, Old
nor	no	Norwegian
nqo		N''Ko
nso		Pedi; Sepedi; Northern Sotho
nub		Nubian languages
nwc		Classical Newari; Old Newari; Classical Nepal Bhasa
nya	ny	Chichewa; Chewa; Nyanja
nym		Nyamwezi
nyn		Nyankole
nyo		Nyoro
nzi		Nzima
oci	oc	Occitan (post 1500)
oji	oj	Ojibwa
ori	or	Oriya
orm	om	Oromo
osa		Osage
oss	os	Ossetian; Ossetic
ota		Turkish, Ottoman (1500-1928)
oto		Otomian languages
paa		Papuan languages
pag		Pangasinan
pal		Pahlavi
pam		Pampanga; Kapampangan
pan	pa	Panjabi; Punjabi
pap		Papiamento
pau		Palauan
peo		Persian, Old (ca.600-400 B.C.)
per/fas	fa	Persian
phi		Philippine languages)
phn		Phoenician
pli	pi	Pali
pol	pl	Polish
pon		Pohnpeian
por	pt	Portuguese
pra		Prakrit languages
pro		Provencal, Old (to 1500);Occitan, Old (to 1500)
pus	ps	Pushto; Pashto
qaa-qtz		Reserved for local use
que	qu	Quechua
raj		Rajasthani
rap		Rapanui
rar		Rarotongan; Cook Islands Maori
roa		Romance languages
roh	rm	Romansh
rom		Romany
rum/ron	ro	Romanian; Moldavian; Moldovan
run	rn	Rundi
rup		Aromanian; Arumanian; Macedo-Romanian
rus	ru	Russian
sad		Sandawe
sag	sg	Sango
sah		Yakut
sai		South American Indian languages
sal		Salishan languages
sam		Samaritan Aramaic
san	sa	Sanskrit
sas		Sasak
sat		Santali
scn		Sicilian
sco		Scots
sel		Selkup
sem		Semitic languages
sga		Irish, Old (to 900)
sgn		Sign Languages
shn		Shan
sid		Sidamo
sin	si	Sinhala; Sinhalese
sio		Siouan languages
sit		Sino-Tibetan languages
sla		Slavic languages
slo/slk	sk	Slovak
slv	sl	Slovenian
sma		Southern Sami
sme	se	Northern Sami
smi		Sami languages
smj		Lule Sami
smn		Inari Sami
smo	sm	Samoan
sms		Skolt Sami
sna	sn	Shona
snd	sd	Sindhi
snk		Soninke
sog		Sogdian
som	so	Somali
son		Songhai languages
sot	st	Sotho, Southern
spa	es	Spanish; Castilian
srd	sc	Sardinian
srn		Sranan Tongo
srp	sr	Serbian
srr		Serer
ssa		Nilo-Saharan languages
ssw	ss	Swati
suk		Sukuma
sun	su	Sundanese
sus		Susu
sux		Sumerian
swa	sw	Swahili
swe	sv	Swedish
syc		Classical Syriac
syr		Syriac
tah	ty	Tahitian
tai		Tai languages
tam	ta	Tamil
tat	tt	Tatar
tel	te	Telugu
tem		Timne
ter		Tereno
tet		Tetum
tgk	tg	Tajik
tgl	tl	Tagalog
tha	th	Thai
tib/bod	bo	Tibetan
tig		Tigre
tir	ti	Tigrinya
tiv		Tiv
tkl		Tokelau
tlh		Klingon; tlhIngan-Hol
tli		Tlingit
tmh		Tamashek
tog		Tonga (Nyasa)
ton	to	Tonga (Tonga Islands)
tpi		Tok Pisin
tsi		Tsimshian
tsn	tn	Tswana
tso	ts	Tsonga
tuk	tk	Turkmen
tum		Tumbuka
tup		Tupi languages
tur	tr	Turkish
tut		Altaic languages
tvl		Tuvalu
twi	tw	Twi
tyv		Tuvinian
udm		Udmurt
uga		Ugaritic
uig	ug	Uighur; Uyghur
ukr	uk	Ukrainian
umb		Umbundu
und		Undetermined
urd	ur	Urdu
uzb	uz	Uzbek
vai		Vai
ven	ve	Venda
vie	vi	Vietnamese
vol	vo	Volapuk
vot		Votic
wak		Wakashan languages
wal		Wolaitta; Wolaytta
war		Waray
was		Washo
wel/cym	cy	Welsh
wen		Sorbian languages
wln	wa	Walloon
wol	wo	Wolof
xal		Kalmyk; Oirat
xho	xh	Xhosa
yao		Yao
yap		Yapese
yid	yi	Yiddish
yor	yo	Yoruba
ypk		Yupik languages
zap		Zapotec
zbl		Blissymbols; Blissymbolics; Bliss
zen		Zenaga
zha	za	Zhuang; Chuang
znd		Zande languages
zul	zu	Zulu
zun		Zuni
zxx		No linguistic content; Not applicable
zza		Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki'!

----- Method: ISOLanguageDefinition class>>readISOLanguagesFrom: (in category 'private') -----
readISOLanguagesFrom: stream
	"ISOLanguageDefinition readISOLanguagesFrom: ISOLanguageDefinition isoLanguages readStream "
	| languages language code3 index line |
	languages := Dictionary new.
	[stream atEnd
		or: [(line := stream nextLine readStream) atEnd]]
		whileFalse: [
			language := ISOLanguageDefinition new.
			code3 := line upTo: Character tab.
			(index := code3 indexOf: $/) > 0
				ifTrue: [
					language iso3: (code3 copyFrom: 1 to: index-1).
					language iso3Alternate: (code3 copyFrom: index+1 to: code3 size)]
				ifFalse: [language iso3: code3].
			language
				iso2: (line upTo: Character tab);
				language: line upToEnd.
			languages at: language iso3 put: language].
	^languages!

----- Method: ISOLanguageDefinition>>iso2 (in category 'accessing') -----
iso2
	^iso2 ifNil: [self iso3]!

----- Method: ISOLanguageDefinition>>iso2: (in category 'initialize') -----
iso2: aString
	iso2 := aString ifEmpty: [nil] ifNotEmpty: [aString]!

----- Method: ISOLanguageDefinition>>iso3 (in category 'accessing') -----
iso3
	^iso3 ifNil: ['']!

----- Method: ISOLanguageDefinition>>iso3: (in category 'initialize') -----
iso3: aString
	iso3 := aString ifEmpty: [nil] ifNotEmpty: [aString]!

----- Method: ISOLanguageDefinition>>iso3Alternate (in category 'accessing') -----
iso3Alternate
	^iso3Alternate ifNil: ['']!

----- Method: ISOLanguageDefinition>>iso3Alternate: (in category 'initialize') -----
iso3Alternate: aString
	iso3Alternate := aString ifEmpty: [nil] ifNotEmpty: [aString]!

----- Method: ISOLanguageDefinition>>language (in category 'accessing') -----
language
	^language!

----- Method: ISOLanguageDefinition>>language: (in category 'initialize') -----
language: aString
	language := aString!

Object subclass: #Locale
	instanceVariableNames: 'id shortDate longDate time decimalSymbol digitGrouping currencySymbol currencyNotation measurement offsetLocalToUTC offsetVMToUTC dstActive'
	classVariableNames: 'Current CurrentPlatform KnownLocales LanguageSymbols LocaleChangeListeners PlatformEncodings Previous'
	poolDictionaries: ''
	category: 'GetText-Localization'!

!Locale commentStamp: '<historical>' prior: 0!
Main comment stating the purpose of this class and relevant relationship to other classes.



	http://www.w3.org/WAI/ER/IG/ert/iso639.htm
	http://www.oasis-open.org/cover/iso639a.html
	See also
	http://oss.software.ibm.com/cvs/icu/~checkout~/icuhtml/design/language_code_issues.html
	http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.10
	
ISO 3166
http://mitglied.lycos.de/buran/knowhow/codes/locales/
!

----- Method: Locale class>>addLocalChangedListener: (in category 'notification') -----
addLocalChangedListener: anObjectOrClass
	self localeChangedListeners add: anObjectOrClass!

----- Method: Locale class>>clipboadInterpreter (in category 'accessing') -----
clipboadInterpreter
	^NoConversionClipboardInterpreter new!

----- Method: Locale class>>current (in category 'accessing') -----
current
	"Current := nil"
	Current ifNil: [
		Current := self determineCurrentLocale.
		"Transcript show: 'Current locale: ' , Current localeID asString; cr"].
	^Current!

----- Method: Locale class>>currentPlatform (in category 'accessing') -----
currentPlatform
	"CurrentPlatform := nil"
	CurrentPlatform ifNil: [CurrentPlatform := self determineCurrentLocale].
	^CurrentPlatform!

----- Method: Locale class>>currentPlatform: (in category 'accessing') -----
currentPlatform: locale
	CurrentPlatform := locale.
	LanguageEnvironment startUp.
!

----- Method: Locale class>>currentPlatform:during: (in category 'accessing') -----
currentPlatform: locale during: aBlock 
	"Alter current language platform during a block"
	| backupPlatform |
	backupPlatform := self currentPlatform.
	[self currentPlatform: locale.
	aBlock value]
		ensure: [self currentPlatform: backupPlatform]!

----- Method: Locale class>>defaultEncodingName: (in category 'platform specific') -----
defaultEncodingName: languageSymbol 
	| encodings platformName osVersion |
	platformName := SmalltalkImage current platformName.
	osVersion := SmalltalkImage current getSystemAttribute: 1002.
	encodings := self platformEncodings at: languageSymbol
				ifAbsent: [self platformEncodings at: #default].
	encodings at: platformName ifPresent: [:encoding | ^encoding].
	encodings at: platformName , ' ' , osVersion
		ifPresent: [:encoding | ^encoding].
	^encodings at: #default!

----- Method: Locale class>>defaultInputInterpreter (in category 'platform specific') -----
defaultInputInterpreter
	| platformName osVersion |
	platformName := SmalltalkImage current platformName.
	osVersion := SmalltalkImage current getSystemAttribute: 1002.
	(platformName = 'Win32' and: [osVersion = 'CE']) 
		ifTrue: [^NoInputInterpreter new].
	platformName = 'Win32' ifTrue: [^MacRomanInputInterpreter new].
	^NoInputInterpreter new!

----- Method: Locale class>>determineCurrentLocale (in category 'private') -----
determineCurrentLocale
	"For now just return the default locale.
	A smarter way would be to determine the current platforms default locale."
	"Locale determineCurrentLocale"

	^self new determineLocale!

----- Method: Locale class>>initKnownLocales (in category 'private') -----
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!

----- Method: Locale class>>initialize (in category 'class initialization') -----
initialize
	"Locale initialize"

	Smalltalk addToStartUpList: Locale.
	Preferences addPreference: #useLocale
		categories: #('general') default: false 
		balloonHelp: 'Use the system locale to set the system language etc at startup'.!

----- Method: Locale class>>initializePlatformEncodings (in category 'class initialization') -----
initializePlatformEncodings
	"Locale initializePlatformEncodings"

	| platform |
	PlatformEncodings isNil ifTrue: [ PlatformEncodings := Dictionary new ].

	platform := PlatformEncodings at: 'default' ifAbsentPut: Dictionary new.
	platform
		at: 'default' put: 'iso8859-1';
		at: 'Win32 CE' put: 'utf-8';
		yourself.

	platform := PlatformEncodings at: 'ja' ifAbsentPut: Dictionary new.
	platform
		at: 'default' put: 'shift-jis';
		at: 'unix' put: 'euc-jp';
		at: 'Win32 CE' put: 'utf-8';
		yourself.

	platform := PlatformEncodings at: 'ko' ifAbsentPut: Dictionary new.
	platform
		at: 'default' put: 'euc-kr';
		at: 'Win32 CE' put: 'utf-8';
		yourself.

	platform := PlatformEncodings at: 'zh' ifAbsentPut: Dictionary new.
	platform
		at: 'default' put: 'gb2312';
		at: 'unix' put: 'euc-cn';
		at: 'Win32 CE' put: 'utf-8';
		yourself.
!

----- Method: Locale class>>isoLanguage: (in category 'accessing') -----
isoLanguage: isoLanguage
	^self isoLanguage: isoLanguage isoCountry: nil!

----- Method: Locale class>>isoLanguage:isoCountry: (in category 'accessing') -----
isoLanguage: isoLanguage isoCountry: isoCountry
	^self localeID: (LocaleID  isoLanguage: isoLanguage isoCountry: isoCountry)!

----- Method: Locale class>>isoLocale: (in category 'accessing') -----
isoLocale: aString
	!

----- Method: Locale class>>knownLocales (in category 'private') -----
knownLocales
	"KnownLocales := nil"
	^KnownLocales ifNil: [KnownLocales := self initKnownLocales]!

----- Method: Locale class>>languageSymbol: (in category 'accessing') -----
languageSymbol: languageSymbol
	"Locale languageSymbol: #Deutsch"

	^self isoLanguage: (LanguageSymbols at: languageSymbol)!

----- Method: Locale class>>localeChanged (in category 'notification') -----
localeChanged

	#(#ParagraphEditor #BitEditor #FormEditor #StandardSystemController )
		do: [:key | Smalltalk
				at: key
				ifPresent: [:class | class initialize]].
	PartsBin localeChanged.
	Project localeChanged.
	PaintBoxMorph localeChanged.
	ColorPickerMorph localeChanged.
	Preferences localeChanged!

----- Method: Locale class>>localeChangedGently (in category 'notification') -----
localeChangedGently

	#(#ParagraphEditor #BitEditor #FormEditor #StandardSystemController )
		do: [:key | Smalltalk
				at: key
				ifPresent: [:class | class initialize]].
	PartsBin localeChangedGently.
	Project localeChangedGently.
	PaintBoxMorph localeChangedGently.
	ColorPickerMorph localeChangedGently.
	Preferences localeChangedGently.
!

----- Method: Locale class>>localeChangedListeners (in category 'notification') -----
localeChangedListeners
	^LocaleChangeListeners ifNil: [LocaleChangeListeners _ OrderedCollection new]!

----- Method: Locale class>>localeID: (in category 'accessing') -----
localeID: id
	^self knownLocales at: id ifAbsentPut: [Locale new localeID: id]!

----- Method: Locale class>>migrateSystem (in category 'private') -----
migrateSystem
	"Locale migrateSystem"
	"Do all the necessary operations to switch to the new Locale environment."

	LocaleChangeListeners _ nil.
	self
		addLocalChangedListener: HandMorph;
		addLocalChangedListener: Clipboard;
		addLocalChangedListener: Vocabulary;
		addLocalChangedListener: PartsBin;
		addLocalChangedListener: Project;
		addLocalChangedListener: PaintBoxMorph;
		yourself!

----- Method: Locale class>>platformEncodings (in category 'class initialization') -----
platformEncodings
	PlatformEncodings isEmptyOrNil ifTrue: [ self initializePlatformEncodings ].
	^PlatformEncodings
!

----- Method: Locale class>>previous (in category 'accessing') -----
previous

	^ Previous
!

----- Method: Locale class>>resetKnownLocales (in category 'private') -----
resetKnownLocales

	KnownLocales := nil
!

----- Method: Locale class>>startUp: (in category 'system startUp') -----
startUp: resuming
	| newID |
	resuming ifFalse: [^self].
	(Preferences valueOfFlag: #useLocale)
		ifTrue: [
			DateAndTime localTimeZone: (TimeZone
				offset:  (Duration minutes: Locale current offsetLocalToUTC)
				name: 'Local Time'
				abbreviation: 'LT').
			newID := self current determineLocaleID.
			newID ~= LocaleID current
				ifTrue: [self switchAndInstallFontToID: newID gently: true]]!

----- Method: Locale class>>stringForLanguageNameIs: (in category 'accessing') -----
stringForLanguageNameIs: localeID 
	"Answer a string for a menu determining whether the given  
	symbol is the project's natural language"
	^ (self current localeID = localeID
		ifTrue: ['<yes>']
		ifFalse: ['<no>'])
		, localeID displayName!

----- Method: Locale class>>switchAndInstallFontToID: (in category 'accessing') -----
switchAndInstallFontToID: localeID 
	"Locale switchAndInstallFontToID: (LocaleID isoLanguage: 'de')"
	| locale |
	locale := Locale localeID: localeID.
	locale languageEnvironment isFontAvailable
		ifFalse: [(self confirm: 'This language needs additional fonts.
Do you want to install the fonts?' translated)
				ifTrue: [locale languageEnvironment installFont]
				ifFalse: [^ self]].
	self
		switchTo: locale!

----- Method: Locale class>>switchAndInstallFontToID:gently: (in category 'accessing') -----
switchAndInstallFontToID: localeID gently: gentlyFlag

	| locale result env envFound ret fontInImage menu |
	"Assumption: Rainbow => can try Pango"
	"Logic:
		- in Sugar, we don't bother asking any questions, and we don't bother automatically loading fonts.
			in Sugar, and if the font is available, use the font. 
			in Sugar, and if the font is not in the image, try to enable Pango.
			- if the previous step fails, notify the user that you cannot switch to the language.
		- not in Rainbow, 
			- if the font is in the image, use the font.
			- if the font is not in the image,
				- ask to choose:
					- load font
					- try to enable pango, if pango is available
					- or cancel.
			- if the previous step fails, notify the user that you cannot switch to the language.
	Details:
		- how to check non-pango font is available:
			- if the language environment for the locale doesn't exist, the font is not available.
			- if font loading fails, it is not available.
		- how to check if the language environment doesn't exist:
			- if the locales languageEnvironment is Latin1 but the locale isn't it is not available.
	"	
	locale := Locale localeID: localeID.
	env := locale languageEnvironment.
	result := true.
	envFound := (Latin1Environment supportedLanguages includes: locale isoLanguage) or: [(env isMemberOf: Latin1Environment) not].
	fontInImage := envFound and: [env isFontAvailable].
	SugarLauncher isRunningInSugar ifTrue: [
		fontInImage ifFalse: [
			"font is not available in the image.  Even don't ask for font installation."
			Cursor wait showWhile: [
				Preferences setPreference: #usePangoRenderer toValue: true.
				TextMorph usePango: true]].
	] ifFalse: [
		fontInImage ifFalse: [
			menu := MenuMorph new.
			menu defaultTarget: menu.
			envFound ifTrue: [menu add: 'load font' translated selector: #modalSelection: argument: #loadFont].
			RomePluginCanvas pangoIsAvailable ifTrue: [menu add:  'enable Pango' translated selector: #modalSelection: argument: #enablePango].
			menu add:  'cancel' translated selector: #modalSelection: argument: #cancel.
			menu addTitle: 'This language needs additional fonts.
Do you want to install the fonts?' translated.
			ret := menu invokeModal.
			ret = #loadFont ifTrue: [result := env installFont. result ifTrue: [StrikeFont setupDefaultFallbackTextStyle]].
			ret = #enablePango ifTrue: [
				(result := RomePluginCanvas pangoIsAvailable) ifTrue: [
					Cursor wait showWhile: [
						Preferences setPreference: #usePangoRenderer toValue: true.
						TextMorph usePango: true]]].
			(ret ~~ #loadFont and: [ret ~~ #enablePango]) ifTrue: [result := false]]].

	result ifFalse: [self inform: 'Cannot load additional fonts' translated] ifTrue: [self switchTo: locale gently: gentlyFlag].
!

----- Method: Locale class>>switchTo: (in category 'accessing') -----
switchTo: locale
	self switchTo: locale gently: false.
!

----- Method: Locale class>>switchTo:gently: (in category 'accessing') -----
switchTo: locale gently: gentlyFlag
	"Locale switchTo: (Locale isoLanguage: 'de')"
	| availableID |
	availableID := (NaturalLanguageTranslator availableForLocaleID: locale localeID) localeID.
	Current localeID = availableID
		ifFalse: [Previous _ Current.
				CurrentPlatform := Current := Locale localeID: availableID.
				NaturalLanguageTranslator localeChanged.
				gentlyFlag ifTrue: [self localeChangedGently] ifFalse: [self localeChanged]]!

----- Method: Locale class>>switchToID: (in category 'accessing') -----
switchToID: localeID
	"Locale switchToID: (LocaleID isoLanguage: 'de') "

	self switchTo: (Locale localeID: localeID)!

----- Method: Locale>>determineLocale (in category 'accessing') -----
determineLocale
	self localeID: self determineLocaleID!

----- Method: Locale>>determineLocaleID (in category 'accessing') -----
determineLocaleID
	"Locale current determineLocaleID"
	| isoLang isoCountry |
	isoLang := self fetchISO2Language
				ifNil: [^ self localeID].
	isoCountry := self fetchISOCountry
				ifNil: [^ LocaleID isoLanguage: isoLang].
	^ LocaleID isoLanguage: isoLang isoCountry: isoCountry!

----- Method: Locale>>fetchISO2Language (in category 'private') -----
fetchISO2Language
	"Locale current fetchISO2Language"
	| lang isoLang |
	lang := self primLanguage.
	lang ifNil: [^nil].
	lang := lang copyUpTo: 0 asCharacter.
	lang size == 2
		ifTrue: [^lang].
	isoLang := ISOLanguageDefinition iso3LanguageDefinition: lang.
	^isoLang
		ifNil: [nil]
		ifNotNil: [isoLang iso2]!

----- Method: Locale>>fetchISOCountry (in category 'private') -----
fetchISOCountry
	"Locale current fetchISOCountry"
	| countryCode |
	countryCode := self primCountry
				ifNil: [^ nil].
	^ countryCode copyUpTo: 0 asCharacter!

----- Method: Locale>>iconForNativeLanguage (in category 'accessing') -----
iconForNativeLanguage

	^ (NaturalLanguageFormTranslator localeID: self localeID) translate: 'LanguageNameInNativeLanguage'.
!

----- Method: Locale>>isoCountry (in category 'accessing') -----
isoCountry
	^self localeID isoCountry!

----- Method: Locale>>isoLanguage (in category 'accessing') -----
isoLanguage
	^self localeID isoLanguage!

----- Method: Locale>>isoLocale (in category 'accessing') -----
isoLocale
	"<language>-<country>"
	^self isoCountry
		ifNil: [self isoLanguage]
		ifNotNil: [self isoLanguage , '-' , self isoCountry]!

----- Method: Locale>>languageEnvironment (in category 'accessing') -----
languageEnvironment
	^LanguageEnvironment localeID: self localeID!

----- Method: Locale>>localeID (in category 'accessing') -----
localeID
	^id!

----- Method: Locale>>localeID: (in category 'accessing') -----
localeID: anID
	id := anID!

----- Method: Locale>>offsetLocalToUTC (in category 'accessing') -----
offsetLocalToUTC
	^self primTimezone!

----- Method: Locale>>primCountry (in category 'system primitives') -----
primCountry
	"Returns string with country tag according to ISO 639"
	<primitive: 'primitiveCountry' module: 'LocalePlugin'>
	^'US'!

----- Method: Locale>>primCurrencyNotation (in category 'system primitives') -----
primCurrencyNotation
	"Returns boolean if symbol is pre- (true) or post-fix (false)"
	<primitive: 'primitiveCurrencyNotation' module: 'LocalePlugin'>
	^true!

----- Method: Locale>>primCurrencySymbol (in category 'system primitives') -----
primCurrencySymbol
	"Returns string with currency symbol"
	<primitive: 'primitiveCurrencySymbol' module:'LocalePlugin'>
	^'$'!

----- Method: Locale>>primDST (in category 'system primitives') -----
primDST
	"Returns boolean if DST  (daylight saving time) is active or not"
	<primitive:'primitiveDaylightSavings' module: 'LocalePlugin'>
	^false!

----- Method: Locale>>primDecimalSymbol (in category 'system primitives') -----
primDecimalSymbol
	"Returns string with e.g. '.' or ','"
	<primitive:'primitiveDecimalSymbol' module: 'LocalePlugin'>
	^'.'!

----- Method: Locale>>primDigitGrouping (in category 'system primitives') -----
primDigitGrouping
	"Returns string with e.g. '.' or ',' (thousands etc)"
	<primitive:'primitiveDigitGroupingSymbol' module: 'LocalePlugin'>
	^','!

----- Method: Locale>>primLanguage (in category 'system primitives') -----
primLanguage
	"returns string with language tag according to ISO 639"
	<primitive:'primitiveLanguage' module: 'LocalePlugin'>
	^'en'

!

----- Method: Locale>>primLongDateFormat (in category 'system primitives') -----
primLongDateFormat
	"Returns the long date format
	d day, m month, y year,
	double symbol is null padded, single not padded (m=6, mm=06)
	dddd weekday
	mmmm month name"
	<primitive:'primitiveLongDateFormat' module: 'LocalePlugin'>
	^'dddd, mmmm d, yyyy'!

----- Method: Locale>>primMeasurement (in category 'system primitives') -----
primMeasurement
	"Returns boolean denoting metric(true) or imperial(false)."
	<primitive:'primitiveMeasurementMetric' module: 'LocalePlugin'>
	^true
!

----- Method: Locale>>primShortDateFormat (in category 'system primitives') -----
primShortDateFormat
	"Returns the short date format
	d day, m month, y year,
	double symbol is null padded, single not padded (m=6, mm=06)
	dddd weekday
	mmmm month name"
	<primitive:'primitiveShortDateFormat' module: 'LocalePlugin'>
	^'m/d/yy'!

----- Method: Locale>>primTimeFormat (in category 'system primitives') -----
primTimeFormat
	"Returns string time format
	Format is made up of 
	h hour (h 12, H 24), m minute, s seconds, x (am/pm String)
	double symbol is null padded, single not padded (h=6, hh=06)"
	<primitive:'primitiveTimeFormat' module: 'LocalePlugin'>
	^'h:mmx'!

----- Method: Locale>>primTimezone (in category 'system primitives') -----
primTimezone
	"The offset from UTC in minutes, with positive offsets being towards the east.
	(San Francisco is in UTC -07*60 and Paris is in UTC +02*60 when daylight savings is in effect)."
	<primitive:'primitiveTimezoneOffset' module: 'LocalePlugin'>
	^0!

----- Method: Locale>>primVMOffsetToUTC (in category 'system primitives') -----
primVMOffsetToUTC
	"Returns the offset in minutes between the VM and UTC.
	If the VM does not support UTC times, this is 0.
	Also gives us backward compatibility with old VMs as the primitive will fail and we then can return 0."
	<primitive:'primitiveVMOffsetToUTC' module: 'LocalePlugin'>
	^0!

----- Method: Locale>>printOn: (in category 'accessing') -----
printOn: aStream 
	super printOn: aStream.
	aStream nextPutAll: '(' , id printString , ')'!

Object subclass: #LocaleID
	instanceVariableNames: 'isoLanguage isoCountry'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GetText-Localization'!

----- Method: LocaleID class>>current (in category 'accessing') -----
current
	^Locale current localeID!

----- Method: LocaleID class>>isoLanguage: (in category 'instance creation') -----
isoLanguage: langString
	^self isoLanguage: langString isoCountry: nil!

----- Method: LocaleID class>>isoLanguage:isoCountry: (in category 'instance creation') -----
isoLanguage: langString isoCountry: countryStringOrNil
	^self new isoLanguage: langString isoCountry: countryStringOrNil!

----- Method: LocaleID class>>isoString: (in category 'instance creation') -----
isoString: isoString
	"Parse the isoString (<language>-<country>) into its components and return the matching LocaleID"
	"LocaleID isoString: 'en' "
	"LocaleID isoString: 'en-us' "

	| parts language country |
	parts := isoString findTokens: #($- ).
	language := parts first.
	parts size > 1
		ifTrue: [country := parts second].
	^self isoLanguage: language isoCountry: country!

----- Method: LocaleID class>>posixName: (in category 'instance creation') -----
posixName: aString 
	^ self
		isoString: (aString copyReplaceAll: '_' with: '-')!

----- Method: LocaleID class>>previous (in category 'accessing') -----
previous
	^Locale previous localeID!

----- Method: LocaleID>>= (in category 'comparing') -----
= anotherObject
	self class == anotherObject class
		ifFalse: [^false].
	^self isoLanguage = anotherObject isoLanguage
		and: [self isoCountry = anotherObject isoCountry]!

----- Method: LocaleID>>displayCountry (in category 'accessing') -----
displayCountry
	^(ISOLanguageDefinition iso2Countries at: self isoCountry asUppercase ifAbsent: [ self isoCountry ]) !

----- Method: LocaleID>>displayLanguage (in category 'accessing') -----
displayLanguage
	| language |
	language := (ISOLanguageDefinition iso2LanguageTable
				at: self isoLanguage
				ifAbsent: [^ self isoLanguage]) language.
	^ self isoCountry
		ifNil: [language]
		ifNotNil: [language , ' (' , self displayCountry , ')']!

----- Method: LocaleID>>displayName (in category 'accessing') -----
displayName
	"Answer a proper name to represent the receiver in GUI. 
	 
	The wording is provided by translations of the magic value 
	'<language display name>'. 
	 
	'English' -> 'English'  
	'German' -> 'Deutsch'  
	"
	| magicPhrase translatedMagicPhrase |
	magicPhrase := '<language display name>'.
	translatedMagicPhrase := NaturalLanguageTranslator translateWithoutLoading: magicPhrase toLocaleID: self.
	^ translatedMagicPhrase = magicPhrase
		ifTrue: [self displayLanguage]
		ifFalse: [translatedMagicPhrase]!

----- Method: LocaleID>>hasParent (in category 'testing') -----
hasParent
	^self isoCountry notNil!

----- Method: LocaleID>>hash (in category 'comparing') -----
hash
	^self isoLanguage hash bitXor: self isoCountry hash!

----- Method: LocaleID>>isoCountry (in category 'accessing') -----
isoCountry
	^isoCountry!

----- Method: LocaleID>>isoLanguage (in category 'accessing') -----
isoLanguage
	^isoLanguage!

----- Method: LocaleID>>isoLanguage:isoCountry: (in category 'initialize') -----
isoLanguage: langString isoCountry: countryStringOrNil
	isoLanguage := langString.
	isoCountry := countryStringOrNil!

----- Method: LocaleID>>isoString (in category 'accessing') -----
isoString
	^self asString!

----- Method: LocaleID>>parent (in category 'accessing') -----
parent
	^self class isoLanguage: self isoLanguage!

----- Method: LocaleID>>posixName (in category 'printing') -----
posixName
	"(LocaleID isoString: 'es-MX') posixName"
	"(LocaleID isoString: 'es') posixName"
	"language[_territory]"
	^ self isoCountry
		ifNil: [self isoLanguage]
		ifNotNil: [self isoLanguage , '_' , self isoCountry]!

----- Method: LocaleID>>printOn: (in category 'printing') -----
printOn: stream
	"<language>-<country>"
	stream nextPutAll: self isoLanguage.
	self isoCountry
		ifNotNil: [stream nextPut: $-; nextPutAll: self isoCountry]!

----- Method: LocaleID>>storeOn: (in category 'printing') -----
storeOn: aStream 
	aStream nextPut: $(.
	aStream nextPutAll: self class name.
	aStream nextPutAll: ' isoString: '.
	aStream nextPutAll: '''' , self printString , ''''.
	aStream nextPut: $).
!

----- Method: LocaleID>>translator (in category 'accessing') -----
translator
	^ InternalTranslator localeID: self !

Object subclass: #MOFile
	instanceVariableNames: 'localeID fileName isLittleEndian magic revision nStrings originalTableOffset translatedTableOffset hashTableSize hashTableOffset hashTable originalStrings translatedStrings translations'
	classVariableNames: 'Cr Lf'
	poolDictionaries: ''
	category: 'GetText-Localization'!

!MOFile commentStamp: '<historical>' prior: 0!
Wrapper for MO file of gettext.
Known limitation:  
	currently don't support prural form.
	translation strings have to be encoded in utf-8.

Implementation notes:
	Testing on XO showed emulation of hash search without plugin + on demand loading is slow.
	The test also showed conversion of utf8 string to Squeak's String is really slow (especially for non-latin language).
	so in this version, all of original/translated strings are loaded on initiaization,
	but "translated strings" is left as ByteString on loading time, to reduce loading time.
	After that the translated string is converted on demand. 
!

----- Method: MOFile class>>fileName:localeID: (in category 'instance creation') -----
fileName: path localeID: id
	^self new 
			load:path localeID: id!

----- Method: MOFile class>>initialize (in category 'class initialization') -----
initialize
	Cr := Character cr.
	Lf := Character lf.
!

----- Method: MOFile>>atRandom (in category 'public') -----
atRandom

	^ self translatedString:nStrings atRandom.
!

----- Method: MOFile>>fileName (in category 'public') -----
fileName
	^fileName!

----- Method: MOFile>>fileName: (in category 'public') -----
fileName: path
	fileName _ path!

----- Method: MOFile>>hashPjw: (in category 'experimental') -----
hashPjw: aString
	"So called `hashpjw' function by P.J. Weinberger
   	[see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
   	1986, 1987 Bell Telephone Laboratories, Inc.] "
	| stringSize hash g |
	stringSize _ aString size.
	hash _ 0.
	1 to: stringSize do: [:pos |
		hash _ hash bitShift: 4.
		hash _ hash + ((aString at: pos) asInteger).
		g _ hash bitAnd: 16rF0000000.
		g = 0 ifFalse: [
			hash _ hash  bitXor: (g bitShift: -24).
			hash _ hash bitXor: g.
		]
	].
	^hash.
!

----- Method: MOFile>>load1:localeID: (in category 'experimental') -----
load1: aFileName localeID: id
	"CASE1: 
		all of strings are loaded. 
		translation strings are converted to Squeak format on load time.
		original-string/index pairs are registerd to Dictionary on load time.
		hash search can't be used"
	| strm originalTable translatedTable |
	localeID _ id.
	strm_ FileStream readOnlyFileNamed: aFileName.
	fileName _ aFileName.
	[
		self loadHeader: strm.
		originalTable _ self loadStringPointers: strm 
								offset: originalTableOffset.

		originalStrings _ self loadStrings: strm 
								pointers: originalTable.

		translatedTable _ self loadStringPointers: strm 
								offset: translatedTableOffset.

		translatedStrings _ self loadStrings: strm 
								pointers: translatedTable
								encoding: 'utf8'
								languageEnvironment: (Locale localeID: localeID) languageEnvironment .

		translations _ Dictionary new.
		1 to: nStrings do: [:index |
			| key |
			key _ originalStrings at: index.
			translations at: key put: index.
		].
		originalTable _ nil.
	] ensure: [strm close].!

----- Method: MOFile>>load4:localeID: (in category 'experimental') -----
load4: aFileName localeID: id
	"CASE4: 
		all of strings are loaded. 
		loading and conversion of translation strings to Squeak format is executed on initialization time.
		only hash search can be used"
	| strm originalTable translatedTable |
	localeID _ id.
	strm_ FileStream readOnlyFileNamed: aFileName.
	fileName _ aFileName.
	[
		self loadHeader: strm.
		self loadHashTable: strm.
		originalTable _ self loadStringPointers: strm 
								offset: originalTableOffset.

		originalStrings _ self loadStrings: strm 
								pointers: originalTable.

		translatedTable _ self loadStringPointers: strm 
								offset: translatedTableOffset.

		translatedStrings _ self loadStrings: strm 
								pointers: translatedTable
								encoding: 'utf-8'
								languageEnvironment: (Locale localeID: localeID) languageEnvironment .
	] ensure: [strm close].!

----- Method: MOFile>>load:localeID: (in category 'public') -----
load: aFileName localeID: id
	"all of original/translated strings are loaded. 
		but conversion of translation string (in utf-8 bytestring) to Squeak format will be defered.
		original-string/index pairs are registerd to Dictionary on load time.
		hash search can't be used"
	| strm originalTable translatedTable |
	localeID _ id.
	strm_ FileStream readOnlyFileNamed: aFileName.
	fileName _ aFileName.
	[
		self loadHeader: strm.
		originalTable _ self loadStringPointers: strm 
								offset: originalTableOffset.

		originalStrings _ self loadStrings: strm 
								pointers: originalTable.

		translatedTable _ self loadStringPointers: strm 
								offset: translatedTableOffset.

		translatedStrings _ self loadStrings: strm 
								pointers: translatedTable.

		translations _ Dictionary new: nStrings * 2.  "make too enough room to avoid #grow"
		1 to: nStrings do: [:index |
			| key |
			key _ originalStrings at: index.
			translations at: key put: index.
		].
		originalStrings _ nil.
	] ensure: [strm close].!

----- Method: MOFile>>loadHashTable: (in category 'experimental') -----
loadHashTable: strm
	| entry |
	hashTable _ IntegerArray  ofSize: hashTableSize.
	strm binary.
	strm position: hashTableOffset.
	1 to: hashTableSize do: [:index |
		entry _ self nextInt32From: strm.
		hashTable at:  index put: entry.
	]!

----- Method: MOFile>>loadHeader: (in category 'private') -----
loadHeader: strm
	strm binary.
	magic _  strm uint32.
	magic = 16rDE120495 
		ifTrue: [isLittleEndian _ true]
		ifFalse: [
			magic = 16r950412DE 
				ifTrue: [isLittleEndian _ false]
				ifFalse: [ self error: 'invalid MO']
		].
	revision _ self nextInt32From: strm.
	nStrings _ self nextInt32From: strm.
	originalTableOffset _ self nextInt32From: strm.
	translatedTableOffset _ self nextInt32From: strm.
	hashTableSize _ self nextInt32From: strm.
	hashTableOffset _ self nextInt32From: strm.
!

----- Method: MOFile>>loadString:pointer:length: (in category 'private') -----
loadString: strm pointer: top  length: len
	| str |
	str _ ByteString new: len.
	strm position:  top.
	strm nextInto: str.
	^str replaceAll: Lf with: Cr.
!

----- Method: MOFile>>loadStringPointers:offset: (in category 'private') -----
loadStringPointers: strm offset: tableOffset
	"returns tupple {arrayOfOffsetToString  arrayOfLengthOfString}"
	| offsetTable lenTable len offset tupple |
	offsetTable _ IntegerArray new: nStrings.
	lenTable _ IntegerArray new: nStrings.
	strm binary.
	strm position: tableOffset.
	1 to: nStrings do: [:index |
		len _ self nextInt32From: strm.
		offset _ self nextInt32From: strm.
		offsetTable at: index put: offset.
		lenTable at: index put: len.
	].
	tupple _ Array new: 2.
	tupple at: 1 put: offsetTable.
	tupple at: 2 put:  lenTable.
	^tupple
!

----- Method: MOFile>>loadStrings:pointers: (in category 'private') -----
loadStrings: strm pointers: table
	^self loadStrings: strm pointers: table encoding: nil languageEnvironment: nil
!

----- Method: MOFile>>loadStrings:pointers:encoding:languageEnvironment: (in category 'private') -----
loadStrings: strm pointers: tupple encoding: encodingName languageEnvironment: env
	| strings rawStr str offsetTable lenTable |
	offsetTable _  tupple first.
	lenTable _ tupple second.
	strings _ Array new: nStrings.
	1 to: nStrings do: [:index |
		rawStr _ self loadString: strm 
					pointer:  (offsetTable at: index)
					length: (lenTable at: index).
		str _ encodingName isNil ifTrue: [rawStr] 
						ifFalse: [ encodingName = 'utf8' 
									ifTrue: [rawStr utf8ToSqueak applyLanguageInfomation: env]
									ifFalse: [self error: 'this encoding isn''t supported']
						].
		strings at: index put: str.
	].
	^strings.!

----- Method: MOFile>>nextInt32From: (in category 'private') -----
nextInt32From: strm
	^isLittleEndian 
			ifTrue: [^strm nextLittleEndianNumber: 4]
			ifFalse: [^strm nextInt32]!

----- Method: MOFile>>originalString: (in category 'private') -----
originalString: index
	^originalStrings at: index.
!

----- Method: MOFile>>searchByDictionary: (in category 'public') -----
searchByDictionary: aString
	| index |
	index _ translations at: aString ifAbsent: [^nil].
	^self translatedString: index
	
!

----- Method: MOFile>>searchByHash: (in category 'experimental') -----
searchByHash: aString
	| hashValue nstr index incr key |
	hashValue _  self hashPjw: aString.
	incr _ 1 + (hashValue \\ (hashTableSize -2)).
	index _ (hashValue \\ hashTableSize) .
	[ 	nstr _ (hashTable at: index +1 ).
		nstr = 0 ifTrue: [^nil].
		key _ self originalString: nstr.
		key = aString ifTrue: [^self translatedString: nstr].
		index >= (hashTableSize - incr) 
				ifTrue: [index _ index - (hashTableSize - incr)  ]
				ifFalse:[index _ index + incr].	
	] doWhileTrue: true.!

----- Method: MOFile>>testSearchByDictionary (in category 'experimental') -----
testSearchByDictionary
	InternalTranslator allKnownPhrases 
		do: [:each |
			self searchByDictionary: each
		].
	!

----- Method: MOFile>>testSearchByHash (in category 'experimental') -----
testSearchByHash
	InternalTranslator allKnownPhrases 
		do: [:each |
			self searchByHash: each
		].
	!

----- Method: MOFile>>translateByHash: (in category 'experimental') -----
translateByHash: aString
	| trans |
	trans _ self searchByHash: aString.
	trans isNil ifTrue: [^aString]
			ifFalse: [^trans].
!

----- Method: MOFile>>translatedString: (in category 'private') -----
translatedString: index
	"KNOWN PROBLEM: conversion is executed everytimes this method called"
	| str |
	str _ translatedStrings at: index.

	^str utf8ToSqueak applyLanguageInfomation: (Locale localeID: localeID) languageEnvironment.
!

----- Method: MOFile>>translationFor: (in category 'public') -----
translationFor: aString 
	| file |
	aString size = 0 ifTrue: [^ '']. "Gettext header"
	^ (self searchByDictionary: aString) ifNil: [
file := (fileName subStrings: '\').
Transcript show: aString printString, ' not found in ', "(file atLast: 3),'\', "(file last); cr.^aString]
!

Object subclass: #NaturalLanguageFormTranslator
	instanceVariableNames: 'id generics'
	classVariableNames: 'CachedTranslations'
	poolDictionaries: ''
	category: 'GetText-Localization'!

----- Method: NaturalLanguageFormTranslator class>>bitmapForJapanese (in category 'as yet unclassified') -----
bitmapForJapanese

	^ (Form
	extent: 54 at 17
	depth: 16
	fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65536 0 1 65537 65537 65537 65537 0 0 0 0 1 0 0 0 0 65537 65537 1 65537 65537 65536 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 65536 0 0 65536 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 65537 65537 65536 1 0 0 0 1 0 0 65536 0 0 1 0 0 0 1 0 1 65537 65537 65537 65537 65537 65537 0 0 0 1 65537 65537 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 65537 65536 0 0 0 65537 65537 0 65536 1 0 0 0 65536 1 0 0 0 1 65537 65537 65537 65537 0 0 0 1 1 1 0 0 0 0 0 0 65536 1 0 0 0 65536 1 0 0 0 1 0 0 0 1 0 0 0 1 1 1 0 0 0 65537 65537 65537 65537 65537 65537 0 0 65536 1 0 0 0 1 0 0 0 1 0 0 0 65536 1 0 65536 0 0 0 0 0 0 0 0 0 0 65536 1 0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 0 0 65537 65537 1 65537 65537 65536 0 0 65536 1 0 0 0 1 0 0 0 1 0 0 65536 65537 65537 65537 65536 65536 0 65536 1 1 0 0 65536 0 0 65536 0 65536 0 0 1 0 0 0 1 0 1 0 0 1 0 0 1 0 65536 1 1 0 0 65536 0 1 0 0 65536 0 0 1 65537 65537 65537 65537 0 0 0 0 1 0 0 0 0 65537 65537 1 65537 65537 65536 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 65536 0 0 0 65536 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0)
	offset: 0 at 0)!

----- Method: NaturalLanguageFormTranslator class>>cachedTranslations (in category 'accessing') -----
cachedTranslations
	"CachedTranslations := nil" 
	^CachedTranslations ifNil: [CachedTranslations := Dictionary new]!

----- Method: NaturalLanguageFormTranslator class>>cleanUp (in category 'class initialization') -----
cleanUp
	"Flush caches"

	CachedTranslations := nil!

----- Method: NaturalLanguageFormTranslator class>>initializeJapaneseBitmap (in category 'as yet unclassified') -----
initializeJapaneseBitmap

	(self localeID: (LocaleID isoString: 'ja')) name: 'LanguageNameInNativeLanguage' form: self bitmapForJapanese.
!

----- Method: NaturalLanguageFormTranslator class>>isoLanguage: (in category 'accessing') -----
isoLanguage: isoLanguage
	"Return the generic language translator as there is no information about the country code"

	^self isoLanguage: isoLanguage isoCountry: nil!

----- Method: NaturalLanguageFormTranslator class>>isoLanguage:isoCountry: (in category 'accessing') -----
isoLanguage: isoLanguage isoCountry: isoCountry
	^self localeID: (LocaleID  isoLanguage: isoLanguage isoCountry: isoCountry)!

----- Method: NaturalLanguageFormTranslator class>>loadFormsFrom: (in category 'i/o') -----
loadFormsFrom: aStream

	| rr pair inst |
	rr _ ReferenceStream on: aStream.
	pair _ rr next.
	inst _ self localeID: (LocaleID isoString: pair first).
	pair second associationsDo: [:assoc |
		inst name: assoc key form: assoc value.
	].
	^ inst.
!

----- Method: NaturalLanguageFormTranslator class>>localeID: (in category 'accessing') -----
localeID: localeID 
	^ self cachedTranslations
		at: localeID
		ifAbsentPut: [self new localeID: localeID]!

----- Method: NaturalLanguageFormTranslator>>generics (in category 'accessing') -----
generics
	^generics ifNil: [generics := Dictionary new]!

----- Method: NaturalLanguageFormTranslator>>localeID (in category 'accessing') -----
localeID
	^id!

----- Method: NaturalLanguageFormTranslator>>localeID: (in category 'accessing') -----
localeID: anID
	id := anID!

----- Method: NaturalLanguageFormTranslator>>name:form: (in category 'accessing') -----
name: formName form: translatedForm 
	self generics at: formName put: translatedForm.
!

----- Method: NaturalLanguageFormTranslator>>saveFormsOn: (in category 'i/o') -----
saveFormsOn: aStream

	| rr |
	rr _ ReferenceStream on: aStream.
	rr nextPut: {id isoString. generics}.
	rr close.
!

----- Method: NaturalLanguageFormTranslator>>translate: (in category 'utilities') -----
translate: aString

	^ (self generics
		at: aString ifAbsent: [nil]) deepCopy.

	"Do you like to write 'form ifNotNil: [form deepCopy]'?"
!

Object subclass: #NaturalLanguageTranslator
	instanceVariableNames: 'id'
	classVariableNames: 'Translators'
	poolDictionaries: ''
	category: 'GetText-Localization'!

!NaturalLanguageTranslator commentStamp: '<historical>' prior: 0!
abstract class of natural language translator.
Class side manages and holds loaded instances of concrete classes.!

NaturalLanguageTranslator subclass: #GetTextTranslator
	instanceVariableNames: 'moFiles'
	classVariableNames: 'LocaleDirsForDomain SystemDefaultLocaleDirs UserDefaultLocaleDirs'
	poolDictionaries: ''
	category: 'GetText-Localization'!

!GetTextTranslator commentStamp: '<historical>' prior: 0!
emulation of gettext runtime
Known limitation:  
     currently don't support prural form.
!

----- Method: GetTextTranslator class>>addSystemDefaultLocaleDir: (in category 'translation data layout') -----
addSystemDefaultLocaleDir: dir
	"new dir will be put as first"
 	self systemDefaultLocaleDirs addFirst: dir!

----- Method: GetTextTranslator class>>availableLanguageLocaleIDs (in category 'accessing') -----
availableLanguageLocaleIDs
	"GetTextTranslator availableLanguageLocaleIDs"
	| ids dirs localeDirForLang directoryNames |
	ids := Set new.
	dirs := Set new.
	dirs addAll: LocaleDirsForDomain values.
	dirs addAll: self defaultLocaleDirs.
	dirs do: [:dir |
		| localesDir |
		localesDir := FileDirectory on: dir. 
		directoryNames := [localesDir directoryNames] on: InvalidDirectoryError do: [:e | #()].
		directoryNames
				do: [:langDirName | 
					| localeID  |
					localeID := LocaleID posixName: langDirName.
					localeDirForLang := localesDir directoryNamed: (self langDirNameForLocaleID: localeID).
					localeDirForLang ifNotNil: [
						(localeDirForLang fileNamesMatching: '*.mo') ifNotEmpty: [ids add: localeID]
					]
				].
	].
	^ids!

----- Method: GetTextTranslator class>>defaultLocaleDirs (in category 'translation data layout') -----
defaultLocaleDirs
	| dirs |
	dirs _ OrderedCollection new.
	UserDefaultLocaleDirs ifNotNil: [dirs addAll: UserDefaultLocaleDirs].
	dirs addAll: self systemDefaultLocaleDirs.
	^dirs
!

----- Method: GetTextTranslator class>>findMOForLocaleID:domain: (in category 'private') -----
findMOForLocaleID: id domain: aDomainName
	| sepa langSubDir path |
	sepa _ FileDirectory slash.
	langSubDir _ self langDirNameForLocaleID: id.
	(self localeDirsForDomain: aDomainName)
		do: [:each |
			path _ each , sepa , langSubDir, sepa , (self moNameForDomain: aDomainName).
			[(FileDirectory default fileExists: path)
				 ifTrue: [^path]] on: InvalidDirectoryError do: [:e | ^nil]].
	^nil.!

----- Method: GetTextTranslator class>>initialize (in category 'class initialization') -----
initialize
	SystemDefaultLocaleDirs _ OrderedCollection new.
	UserDefaultLocaleDirs _ OrderedCollection new.
	LocaleDirsForDomain _ Dictionary new.!

----- Method: GetTextTranslator class>>langDirNameForLocaleID: (in category 'private') -----
langDirNameForLocaleID: id
	"returns relative path from locale directory to actual directory containing MOs"
	^(id posixName) , (FileDirectory slash)  , 'LC_MESSAGES'!

----- Method: GetTextTranslator class>>localeDirForDomain: (in category 'translation data layout') -----
localeDirForDomain: aDomainName
	"returns registered localeDirectory for the textdomain. returns nil if not registered"
	^LocaleDirsForDomain at: aDomainName ifAbsent: [nil]!

----- Method: GetTextTranslator class>>localeDirsForDomain (in category 'private') -----
localeDirsForDomain
	^LocaleDirsForDomain ifNil: [LocaleDirsForDomain _ Dictionary new]!

----- Method: GetTextTranslator class>>localeDirsForDomain: (in category 'translation data layout') -----
localeDirsForDomain: aDomainName
	"returns collection of locale directories for text domain.  
	This includes user defined one for the domain, user defaults and system defaults" 
	| dirs dir |
	dirs _ OrderedCollection new.
	dir _ self localeDirForDomain: aDomainName.
	dir ifNotNil: [dirs add: dir].
	dirs addAll:  self defaultLocaleDirs.
	^dirs!

----- Method: GetTextTranslator class>>moNameForDomain: (in category 'private') -----
moNameForDomain: domainName
	^domainName , '.mo'!

----- Method: GetTextTranslator class>>newForLocaleID: (in category 'instance creation') -----
newForLocaleID: id
	^self new localeID: id!

----- Method: GetTextTranslator class>>privateStartUp (in category 'class initialization') -----
privateStartUp
	self setupLocaleDirs.
	self availableLanguageLocaleIDs do: [ :localeID |
		NaturalLanguageTranslator translators 
				at: localeID 
				put: (self newForLocaleID: localeID).
	]!

----- Method: GetTextTranslator class>>setLocaleDir:forDoamin: (in category 'translation data layout') -----
setLocaleDir: path forDoamin: aDomainName
	self LocaleDirsForDomain
		at: aDomainName
		put: path.!

----- Method: GetTextTranslator class>>setupLocaleDirs (in category 'translation data layout') -----
setupLocaleDirs
	| dirs sepa localesDirName |
	sepa := FileDirectory slash.
	SystemDefaultLocaleDirs := nil.
	dirs := self systemDefaultLocaleDirs.
	localesDirName := 'locale'.
	dirs add:  (SmalltalkImage current imagePath) , sepa , localesDirName.
	dirs add:  (SmalltalkImage current vmPath) , sepa , localesDirName.
	^dirs!

----- Method: GetTextTranslator class>>systemDefaultLocaleDirs (in category 'translation data layout') -----
systemDefaultLocaleDirs
	^SystemDefaultLocaleDirs ifNil: [SystemDefaultLocaleDirs := OrderedCollection new]
!

----- Method: GetTextTranslator class>>userDefaultLocaleDirs (in category 'translation data layout') -----
userDefaultLocaleDirs
	^UserDefaultLocaleDirs ifNil: [UserDefaultLocaleDirs := OrderedCollection new]
!

----- Method: GetTextTranslator>>atRandom (in category 'accessing') -----
atRandom

	| v |
	moFiles ifEmpty: [^ ''].
	(v := moFiles atRandom value) ifNil: [^ ''].
	^ v atRandom.
!

----- Method: GetTextTranslator>>domainRegistered: (in category 'accessing') -----
domainRegistered: aDomainName
	"only current translator actually load the MO, to minimize loading time.
	 other translator will load anyway when it goes current"
	(self class current == self) 
		ifTrue: [self moFileForDomain: aDomainName].
	!

----- Method: GetTextTranslator>>domainUnregistered: (in category 'accessing') -----
domainUnregistered: aDomainName
	moFiles removeKey: aDomainName ifAbsent: [^self]
	!

----- Method: GetTextTranslator>>initialize (in category 'initialize-release') -----
initialize
	moFiles _ Dictionary new.!

----- Method: GetTextTranslator>>isDomainLoaded: (in category 'accessing') -----
isDomainLoaded: aDomainName
	| mo |
	mo _ moFiles at: aDomainName ifAbsent: [nil].
	^mo isNil not.
!

----- Method: GetTextTranslator>>loadMOFileForDomain: (in category 'private') -----
loadMOFileForDomain: aDomainName
	| moName |
	moName _ self class findMOForLocaleID: self localeID 
								domain: aDomainName.
	moName notNil
			 ifTrue: [^MOFile new load: moName
								localeID: self localeID]
			ifFalse: [^nil]
!

----- Method: GetTextTranslator>>loadMOFiles (in category 'accessing') -----
loadMOFiles
	TextDomainManager allKnownDomains 
		do: [:domainName |
			self moFileForDomain: domainName
		].!

----- Method: GetTextTranslator>>moFileForDomain: (in category 'private') -----
moFileForDomain: domainName
	^moFiles at: domainName ifAbsentPut: [self loadMOFileForDomain: domainName]!

----- Method: GetTextTranslator>>reloadMOFiles (in category 'accessing') -----
reloadMOFiles
	moFiles _ Dictionary new.
	self loadMOFiles.!

----- Method: GetTextTranslator>>setCurrent (in category 'language switching') -----
setCurrent
	"ensure actual contents of MOs is loaded on switching language"
	self loadMOFiles!

----- Method: GetTextTranslator>>translate:inDomain: (in category 'translation') -----
translate: aString inDomain: aDomainName
	| mo |
	mo _ self moFileForDomain: aDomainName.
	^mo isNil 
		ifTrue: [aString] 
		ifFalse: [mo translationFor: aString]
!

NaturalLanguageTranslator subclass: #InternalTranslator
	instanceVariableNames: 'generics'
	classVariableNames: 'AllKnownPhrases CachedTranslations'
	poolDictionaries: ''
	category: 'GetText-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
!

----- Method: InternalTranslator class>>allKnownPhrases (in category 'private') -----
allKnownPhrases
	^AllKnownPhrases ifNil: [AllKnownPhrases := Dictionary new: 2051]!

----- Method: InternalTranslator class>>availableLanguageLocaleIDs (in category 'accessing') -----
availableLanguageLocaleIDs
	"Return locale ids for the internal translation dictionary."
	"InternalTranslator availableLanguageLocaleIDs"
	^ self cachedTranslations values collect:[:each | each localeID]!

----- Method: InternalTranslator class>>cachedTranslations (in category 'private') -----
cachedTranslations
	"CachedTranslations := nil" 
	^CachedTranslations ifNil: [CachedTranslations := Dictionary new]!

----- Method: InternalTranslator class>>directoryForLanguage:country:create: (in category 'private loading') -----
directoryForLanguage: isoLanguage country: isoCountry create: createDir
	"Try to locate the <prefs>/locale/<language>{/<country>} folder.
	If createDir is set, create the path down to country or language, depending on wether it's specified..
	Return the directory for country or language depending on specification.
	If neither exists, nil"

	"NaturalLanguageTranslator directoryForLanguage: 'es' country: nil create: true"
	"NaturalLanguageTranslator directoryForLanguage: 'de' country: 'DE' create: true"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: 'US' create: false"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: nil create: true"

	"If this fails, there is nothing we can do about it here"
	| localeDir  countryDir languageDir |
	localeDir := self localeDirCreate: createDir.
	localeDir ifNil: [^nil].

	isoCountry ifNil: [
		languageDir := localeDir directoryNamed: isoLanguage.
		createDir
			ifTrue: [languageDir assureExistence].
		^languageDir exists
			ifTrue: [languageDir]
			ifFalse: [nil]].

	countryDir := languageDir directoryNamed: isoCountry.
	createDir
		ifTrue: [countryDir assureExistence].

	^countryDir exists
		ifTrue: [countryDir]
		ifFalse: [nil]!

----- Method: InternalTranslator class>>directoryForLocaleID:create: (in category 'private loading') -----
directoryForLocaleID: localeID create: createDir
	"Try to locate the <prefs>/locale/<language>{/<country>} folder.
	If createDir is set, create the path down to country or language, depending on locale.
	Return the directory for country or language depending on locale.
	If neither exists, nil"

	"NaturalLanguageTranslator directoryForLanguage: 'de' country: nil readOnly: true"
	"NaturalLanguageTranslator directoryForLanguage: 'de' country: 'DE' readOnly: true"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: 'US' readOnly: false"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: nil readOnly: true"

	^self directoryForLanguage: localeID isoLanguage country: localeID isoCountry create: createDir!

----- Method: InternalTranslator class>>discardAllTranslations (in category 'class initialization') -----
discardAllTranslations
	AllKnownPhrases := nil.
	self resetCaches.!

----- Method: InternalTranslator class>>fileReaderServicesForFile:suffix: (in category 'file-services') -----
fileReaderServicesForFile: fullName suffix: suffix 
	"Answer the file services associated with given file"
	^ (suffix = self translationSuffix) | (suffix = '*')
		ifTrue: [{self serviceMergeLanguageTranslations}]
		ifFalse: [#()]!

----- Method: InternalTranslator class>>loadAvailableExternalLocales (in category 'private loading') -----
loadAvailableExternalLocales
	"private - register locales IDs based on the content of the <prefs>/locale/ directory"
	| localeDir |
	localeDir := self localeDirCreate: false.
	localeDir ifNil: [^ #()].

	localeDir directoryNames
		do: [:langDirName | 
			| langDir | 
			langDir := localeDir directoryNamed: langDirName.

			(langDir fileNamesMatching: '*.' , self translationSuffix)
				ifNotEmpty: [self loadTranslatorForIsoLanguage: langDirName isoCountry: nil].

			langDir directoryNames
				do: [:countryDirName | 
					| countryDir | 
					countryDir := langDirName directoryNamed: countryDirName.
					(countryDir fileNamesMatching: '*.' , self translationSuffix)
						ifNotEmpty: [self loadTranslatorForIsoLanguage: langDirName isoCountry: countryDirName]
			]
		].
!

----- Method: InternalTranslator class>>loadExternalTranslationsFor: (in category 'private loading') -----
loadExternalTranslationsFor: translator
	"Try to load translations from external external files.
	The files are located in the <prefs>/locale/<language>{/<country>} folder.
	There can be more than one file for each location, so applications can install their own partial translation tables. All files in the specific folder are loaded."

	| translationDir |
	translationDir := self directoryForLocaleID: translator localeID create: false.
	translationDir ifNil: [ ^nil ]. 
	(translationDir fileNamesMatching: '*.' , self translationSuffix)
		do: [:fileName | translator loadFromFileNamed: (translationDir fullNameFor: fileName)]!

----- Method: InternalTranslator class>>loadForLocaleIsoString:fromGzippedMimeLiteral: (in category 'file-services') -----
loadForLocaleIsoString: localeString fromGzippedMimeLiteral: mimeString 
	"merge the translation from the mime literal."
	| stream localeID translator gs rbStream s currentPlatform |
	s := Base64MimeConverter mimeDecodeToBytes: mimeString readStream.
	s reset.
	gs := GZipReadStream on: s.
	rbStream := MultiByteBinaryOrTextStream with: gs contents asString.
	rbStream converter: UTF8TextConverter new.
	rbStream reset.
	localeID := LocaleID isoString: localeString.
	currentPlatform := Locale currentPlatform.
	[Locale
		currentPlatform: (Locale localeID: localeID).
	stream := ReadStream on: rbStream contents]
		ensure: [Locale currentPlatform: currentPlatform].
	translator := self localeID: localeID.
	translator loadFromStream: stream.
	LanguageEnvironment resetKnownEnvironments!

----- Method: InternalTranslator class>>loadTranslatorForIsoLanguage:isoCountry: (in category 'private loading') -----
loadTranslatorForIsoLanguage: isoLanguage isoCountry: isoCountry 
	"private - load the translations from <prefs>/locale/ directory  
	the procedure is to assure the existence of a translator for the  
	given language/country and then load the external translations for this translator"

	| translator |
	translator := self newLocaleID: (LocaleID isoLanguage: isoLanguage isoCountry: isoCountry).

	self loadExternalTranslationsFor: translator!

----- Method: InternalTranslator class>>localeDirCreate: (in category 'private loading') -----
localeDirCreate: createDir
	"Try to locate the <prefs>/locale/ folder.
	If createDir is set, try to create the path.
	If it doesn't exist, return nil"

	"If this fails, there is nothing we can do about it here"
	| prefDir  localeDir |
	(createDir not
			and: [ExternalSettings preferenceDirectory isNil])
		ifTrue: [^ nil].

	prefDir := ExternalSettings assuredPreferenceDirectory.
	prefDir exists
		ifFalse: [^nil].


	localeDir := prefDir directoryNamed: 'locale'.
	createDir
		ifTrue: [localeDir assureExistence].
	^localeDir exists
		ifTrue: [localeDir]
		ifFalse: [nil]!

----- Method: InternalTranslator class>>localeID: (in category 'accessing') -----
localeID: localeID 
	"For backward compatibility, see NaturalLanguageTranslator >> fileOutHeaderOn:."
	^ self newLocaleID: localeID!

----- Method: InternalTranslator class>>mergeLegacyTranslators (in category 'accessing') -----
mergeLegacyTranslators
	self availableLanguageLocaleIDs
		do: [:localeID | (NaturalLanguageTranslator translators includesKey: localeID)
				ifFalse: [NaturalLanguageTranslator translators
						at: localeID
						put: (self newLocaleID: localeID)]]!

----- Method: InternalTranslator class>>mergeTranslationFileNamed: (in category 'file-services') -----
mergeTranslationFileNamed: fileFullNameString 
	"merge the translation in the file named fileFullNameString"

	| stream localeID translator |
	stream := FileStream readOnlyFileNamed: fileFullNameString.
	[localeID := LocaleID isoString: stream localName sansPeriodSuffix.
	translator := self localeID: localeID.
	translator loadFromStream: stream]
		ensure: [stream close].
	LanguageEnvironment resetKnownEnvironments.

!

----- Method: InternalTranslator class>>newLocaleID: (in category 'accessing') -----
newLocaleID: localeID 
	^ self cachedTranslations
		at: localeID
		ifAbsentPut: [self new localeID: localeID]!

----- Method: InternalTranslator class>>privateStartUp (in category 'class initialization') -----
privateStartUp
	self loadAvailableExternalLocales.
	self mergeLegacyTranslators.
!

----- Method: InternalTranslator class>>registerPhrase: (in category 'private') -----
registerPhrase: phrase
	"Using a Dictionary so we can lookup existing string instead of creating needless copies when loading a translation."
	self allKnownPhrases at: phrase put: phrase!

----- Method: InternalTranslator class>>registeredPhraseFor: (in category 'private') -----
registeredPhraseFor: phrase
	"Using a Dictionary so we can lookup existing string instead of creating needless copies when loading a translation."
	^self allKnownPhrases at: phrase ifAbsentPut: [phrase]!

----- Method: InternalTranslator class>>removeLocaleID: (in category 'accessing') -----
removeLocaleID: localeID 
	"self removeLocaleID: (LocaleID isoString: 'ja-kids')"
	self cachedTranslations
		removeKey: localeID
		ifAbsent: [].
	NaturalLanguageTranslator privateStartUp!

----- Method: InternalTranslator class>>resetCaches (in category 'class initialization') -----
resetCaches
	CachedTranslations := nil.
!

----- Method: InternalTranslator class>>serviceMergeLanguageTranslations (in category 'file-services') -----
serviceMergeLanguageTranslations
	"Answer a service for merging of translation files"
	^ SimpleServiceEntry
		provider: self
		label: 'merge the translation file' translatedNoop
		selector: #mergeTranslationFileNamed:
		description: 'merge the translation file into the language named like the file' translatedNoop
		buttonLabel: 'merge' translatedNoop!

----- Method: InternalTranslator class>>services (in category 'file-services') -----
services
	"Answer potential file services associated with this class"
	^ {self serviceMergeLanguageTranslations}!

----- Method: InternalTranslator class>>translationSuffix (in category 'private') -----
translationSuffix
	^'translation'!

----- Method: InternalTranslator>>atRandom (in category 'accessing') -----
atRandom

	^ generics atRandom value.
!

----- Method: InternalTranslator>>checkPhrase:translation: (in category 'translation') -----
checkPhrase: phrase translation: translation!

----- Method: InternalTranslator>>defaultBackgroundColor (in category 'user interface') -----
defaultBackgroundColor
	"answer the receiver's defaultBackgroundColor for views"
	^ Color cyan!

----- Method: InternalTranslator>>fileOutHeader (in category 'fileIn/fileOut') -----
fileOutHeader
	^ '''Translation dictionary'''!

----- Method: InternalTranslator>>fileOutHeaderOn: (in category 'user interface') -----
fileOutHeaderOn: aStream 
	aStream binary.
	UTF8TextConverter writeBOMOn: aStream.
	aStream text.
	aStream nextChunkPut: self fileOutHeader;
		 cr.
	aStream timeStamp; cr.
	aStream nextPut: $!!.
	aStream nextChunkPut: '(' , self class name , ' localeID: ' , id storeString , ')'.
	aStream cr!

----- Method: InternalTranslator>>fileOutHeaderOn:withBOM: (in category 'user interface') -----
fileOutHeaderOn: aStream withBOM: bomFlag
	bomFlag ifTrue: [
		aStream binary.
		UTF8TextConverter writeBOMOn: aStream.
		aStream text.
	].
	aStream nextChunkPut: self fileOutHeader;
		 cr.
	aStream timeStamp; cr.
	aStream nextPut: $!!.
	aStream nextChunkPut: '(' , self class name , ' localeID: ' , id storeString , ')'.
	aStream cr!

----- Method: InternalTranslator>>fileOutOn: (in category 'fileIn/fileOut') -----
fileOutOn: aStream 
	"self current fileOutOn: Transcript. Transcript endEntry"
	self fileOutOn: aStream keys: nil withBOM: true.
!

----- Method: InternalTranslator>>fileOutOn:keys:withBOM: (in category 'fileIn/fileOut') -----
fileOutOn: aStream keys: keys withBOM: bomFlag
	"self current fileOutOn: Transcript. Transcript endEntry"
	self fileOutHeaderOn: aStream withBOM: bomFlag.
	(keys
		ifNil: [generics keys asSortedCollection])
		do: [:key | self
				nextChunkPut: (generics associationAt: key)
				on: aStream].
	keys
		ifNil: [self untranslated
				do: [:each | self nextChunkPut: each -> '' on: aStream]].
	aStream nextPut: $!!;
		 cr!

----- Method: InternalTranslator>>generics (in category 'private') -----
generics
	^generics ifNil: [generics := Dictionary new]!

----- Method: InternalTranslator>>isDomainLoaded: (in category 'accessing') -----
isDomainLoaded: aDomainName
	^true
!

----- Method: InternalTranslator>>loadFromFileNamed: (in category 'private store-retrieve') -----
loadFromFileNamed: fileNameString 
	"Load translations from an external file"

	| stream |
	[stream := FileStream readOnlyFileNamed: fileNameString.
	self loadFromStream: stream]
		ensure: [stream close].
	self changed: #translations.
	self changed: #untranslated.
!

----- Method: InternalTranslator>>loadFromRefStream: (in category 'private store-retrieve') -----
loadFromRefStream: stream 
	"Load translations from an external file"
	| loadedArray refStream |
	refStream := ReferenceStream on: stream.
	[loadedArray := refStream next]
		ensure: [refStream close].
	self processExternalObject: loadedArray !

----- Method: InternalTranslator>>loadFromStream: (in category 'private store-retrieve') -----
loadFromStream: stream 
	"Load translations from an external file"
	| header isFileIn |
	header := '''Translation dictionary'''.
	isFileIn := (stream next: header size)
				= header.
	stream reset.
	isFileIn
		ifTrue: [stream fileInAnnouncing: 'Loading ' translated, stream localName]
		ifFalse: [self loadFromRefStream: stream]!

----- Method: InternalTranslator>>mergeTranslations: (in category 'private store-retrieve') -----
mergeTranslations: newTranslations
	"Merge a new set of translations into the exiting table.
	Overwrites existing entries."

	newTranslations keysAndValuesDo: [:key :value |
		self rawPhrase: (self class registeredPhraseFor: key) translation: value].
	self changed: #translations.
	self changed: #untranslated.!

----- Method: InternalTranslator>>nextChunkPut:on: (in category 'fileIn/fileOut') -----
nextChunkPut: anObject on: aStream 
	| i remainder terminator |
	terminator := $!!.
	remainder := anObject storeString.
	[(i := remainder indexOf: terminator) = 0]
		whileFalse: [aStream
				nextPutAll: (remainder copyFrom: 1 to: i).
			aStream nextPut: terminator.
			"double imbedded terminators"
			remainder := remainder copyFrom: i + 1 to: remainder size].
	aStream nextPutAll: remainder.
	aStream nextPut: terminator; cr.!

----- Method: InternalTranslator>>phrase:translation: (in category 'translation') -----
phrase: phraseString translation: translationString 
	self generics at: phraseString put: translationString asString.
	self changed: #translations.
	self changed: #untranslated.!

----- Method: InternalTranslator>>processExternalObject: (in category 'private store-retrieve') -----
processExternalObject: anArray 
	"pivate - process the external object"

	"new format -> {translations. untranslated}"

	anArray second do: [:each | self class registerPhrase: each].

	self mergeTranslations: anArray first!

----- Method: InternalTranslator>>rawPhrase:translation: (in category 'translation') -----
rawPhrase: phraseString translation: translationString 
	self generics at: phraseString put: translationString asString.
!

----- Method: InternalTranslator>>rawRemoveUntranslated: (in category 'translation') -----
rawRemoveUntranslated: untranslated

	self class allKnownPhrases removeKey: untranslated ifAbsent: [].
	self changed: #untranslated.!

----- Method: InternalTranslator>>removeTranslationFor: (in category 'translation') -----
removeTranslationFor: phraseString
	self generics removeKey: phraseString ifAbsent: [].
	self changed: #translations.
	self changed: #untranslated.!

----- Method: InternalTranslator>>removeUntranslated: (in category 'translation') -----
removeUntranslated: untranslated

	self class allKnownPhrases removeKey: untranslated ifAbsent: [].
!

----- Method: InternalTranslator>>saveToFileNamed: (in category 'private store-retrieve') -----
saveToFileNamed: fileNameString 
	"save the receiver's translations to a file named fileNameString"
	| stream |
	"Set true if you need to save as binary"
	false
		ifTrue: [stream := ReferenceStream fileNamed: fileNameString.
			stream nextPut: {self translations. self untranslated}.
			stream close.
			^ self].
	stream := FileStream fileNamed: fileNameString.
	[self fileOutOn: stream]
		ensure: [stream close]!

----- Method: InternalTranslator>>scanFrom: (in category 'fileIn/fileOut') -----
scanFrom: aStream 
	"Read a definition of dictionary.  
	Make sure current locale corresponds my locale id"
	| aString newTranslations assoc currentPlatform |
	newTranslations := Dictionary new.
	currentPlatform := Locale currentPlatform.
	[Locale
		currentPlatform: (Locale localeID: id).
	[aString := aStream nextChunk withSqueakLineEndings.
	aString size > 0]
		whileTrue: [assoc := Compiler evaluate: aString.
			assoc value = ''
				ifTrue: [self class registerPhrase: assoc key]
				ifFalse: [newTranslations add: assoc]]]
		ensure: [Locale currentPlatform: currentPlatform].
	self mergeTranslations: newTranslations!

----- Method: InternalTranslator>>translate:inDomain: (in category 'translation') -----
translate: aString inDomain: aDomainName 
	| translator |
	^ self generics
		at: aString
		ifAbsent: [self localeID hasParent
				ifTrue: [translator := self class cachedTranslations
								at: self localeID parent
								ifAbsent: [^ aString].
					translator translate: aString inDomain: aDomainName]
				ifFalse: [aString]]!

----- Method: InternalTranslator>>translations (in category 'accessing') -----
translations
	^self generics!

----- Method: InternalTranslator>>untranslated (in category 'accessing') -----
untranslated
	| translations |
	translations := self translations.
	^self class allKnownPhrases reject: [:each | translations includesKey: each]!

----- Method: NaturalLanguageTranslator class>>allKnownPhrases (in category 'private') -----
allKnownPhrases
	^AllKnownPhrases ifNil: [AllKnownPhrases := Dictionary new: 2051]!

----- Method: NaturalLanguageTranslator class>>availableForLocaleID: (in category 'accessing') -----
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]]!

----- Method: NaturalLanguageTranslator class>>availableLanguageLocaleIDs (in category 'accessing') -----
availableLanguageLocaleIDs
	"Return the locale ids for the currently available languages.  
	Meaning those which either internally or externally have  
	translations available."
	"NaturalLanguageTranslator availableLanguageLocaleIDs"
	^ self translators values collect:[:each | each localeID]!

----- Method: NaturalLanguageTranslator class>>cachedTranslations (in category 'private') -----
cachedTranslations
	"CachedTranslations := nil" 
	^CachedTranslations ifNil: [CachedTranslations := Dictionary new]!

----- Method: NaturalLanguageTranslator class>>cleanUp (in category 'class initialization') -----
cleanUp
	"Flush caches"

	CachedTranslations := nil.
	AllKnownPhrases := nil.!

----- Method: NaturalLanguageTranslator class>>cleanUpCache (in category 'private') -----
cleanUpCache
	"NaturalLanguageTranslator cleanUpCache"

	self cachedTranslations keys do: [:key |
		key isoLanguage size > 2 ifTrue: [self cachedTranslations removeKey: key]]!

----- Method: NaturalLanguageTranslator class>>current (in category 'accessing') -----
current
	^ self availableForLocaleID: LocaleID current!

----- Method: NaturalLanguageTranslator class>>default (in category 'accessing') -----
default
	"Answer translator for backstop"
	"self default translate: 'test'"
	^ self new
		localeID: (LocaleID isoLanguage: 'en')!

----- Method: NaturalLanguageTranslator class>>directoryForLanguage:country:create: (in category 'private loading') -----
directoryForLanguage: isoLanguage country: isoCountry create: createDir
	"Try to locate the <prefs>/locale/<language>{/<country>} folder.
	If createDir is set, create the path down to country or language, depending on wether it's specified..
	Return the directory for country or language depending on specification.
	If neither exists, nil"

	"NaturalLanguageTranslator directoryForLanguage: 'es' country: nil create: true"
	"NaturalLanguageTranslator directoryForLanguage: 'de' country: 'DE' create: true"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: 'US' create: false"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: nil create: true"

	"If this fails, there is nothing we can do about it here"
	| localeDir  countryDir languageDir |
	localeDir := self localeDirCreate: createDir.
	localeDir ifNil: [^nil].

	isoCountry ifNil: [
		languageDir := localeDir directoryNamed: isoLanguage.
		createDir
			ifTrue: [languageDir assureExistence].
		^languageDir exists
			ifTrue: [languageDir]
			ifFalse: [nil]].

	countryDir := languageDir directoryNamed: isoCountry.
	createDir
		ifTrue: [countryDir assureExistence].

	^countryDir exists
		ifTrue: [countryDir]
		ifFalse: [nil]!

----- Method: NaturalLanguageTranslator class>>directoryForLocaleID:create: (in category 'private loading') -----
directoryForLocaleID: localeID create: createDir
	"Try to locate the <prefs>/locale/<language>{/<country>} folder.
	If createDir is set, create the path down to country or language, depending on locale.
	Return the directory for country or language depending on locale.
	If neither exists, nil"

	"NaturalLanguageTranslator directoryForLanguage: 'de' country: nil readOnly: true"
	"NaturalLanguageTranslator directoryForLanguage: 'de' country: 'DE' readOnly: true"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: 'US' readOnly: false"
	"NaturalLanguageTranslator directoryForLanguage: 'en' country: nil readOnly: true"

	^self directoryForLanguage: localeID isoLanguage country: localeID isoCountry create: createDir!

----- Method: NaturalLanguageTranslator class>>domainRegistered: (in category 'accessing') -----
domainRegistered: aDomainName
	"notify that new TextDomain is registered"
	self translators do: [:each | each domainRegistered: aDomainName]!

----- Method: NaturalLanguageTranslator class>>domainUnregistered: (in category 'accessing') -----
domainUnregistered: aDomainName
	"notify that new TextDomain is unregistered"
	self translators do: [:each | each domainUnregistered: aDomainName]!

----- Method: NaturalLanguageTranslator class>>fileReaderServicesForFile:suffix: (in category 'file-services') -----
fileReaderServicesForFile: fullName suffix: suffix 
	"Answer the file services associated with given file"
	^ (suffix = self translationSuffix) | (suffix = '*')
		ifTrue: [{self serviceMergeLanguageTranslations}]
		ifFalse: [#()]!

----- Method: NaturalLanguageTranslator class>>initialize (in category 'class initialization') -----
initialize
	Smalltalk addToStartUpList: NaturalLanguageTranslator after: FileDirectory.
!

----- Method: NaturalLanguageTranslator class>>isoLanguage: (in category 'accessing') -----
isoLanguage: isoLanguage
	"Return the generic language translator as there is no information about the country code"

	^self isoLanguage: isoLanguage isoCountry: nil!

----- Method: NaturalLanguageTranslator class>>isoLanguage:isoCountry: (in category 'accessing') -----
isoLanguage: isoLanguage isoCountry: isoCountry
	^self localeID: (LocaleID  isoLanguage: isoLanguage isoCountry: isoCountry)!

----- Method: NaturalLanguageTranslator class>>loadAvailableExternalLocales (in category 'private loading') -----
loadAvailableExternalLocales
	"private - register locales IDs based on the content of the <prefs>/locale/ directory"
	| localeDir |
	localeDir := self localeDirCreate: false.
	localeDir ifNil: [^ #()].
	localeDir directoryNames
		do: [:langDirName | 
			| langDir | 
			langDir := localeDir directoryNamed: langDirName.
			(langDir fileNamesMatching: '*.' , self translationSuffix)
				ifNotEmpty: [self loadTranslatorForIsoLanguage: langDirName isoCountry: nil].
			langDir directoryNames
				do: [:countryDirName | 
					| countryDir | 
					countryDir := langDir directoryNamed: countryDirName.
					(countryDir fileNamesMatching: '*.' , self translationSuffix)
						ifNotEmpty: [self loadTranslatorForIsoLanguage: langDirName isoCountry: countryDirName]
			]
		].
!

----- Method: NaturalLanguageTranslator class>>loadAvailableLocales (in category 'private loading') -----
loadAvailableLocales
	"This loads the default locale and all external locales"

	| defaultID |
	defaultID := LocaleID current.
	self cachedTranslations at: defaultID ifAbsent: [self localeID: defaultID].
	self loadAvailableExternalLocales.!

----- Method: NaturalLanguageTranslator class>>loadExternalTranslationsFor: (in category 'private loading') -----
loadExternalTranslationsFor: translator
	"Try to load translations from external external files.
	The files are located in the <prefs>/locale/<language>{/<country>} folder.
	There can be more than one file for each location, so applications can install their own partial translation tables. All files in the specific folder are loaded."

	| translationDir |
	translationDir := self directoryForLocaleID: translator localeID create: false.
	translationDir ifNil: [ ^nil ]. 
	(translationDir fileNamesMatching: '*.' , self translationSuffix)
		do: [:fileName | translator loadFromFileNamed: (translationDir fullNameFor: fileName)]!

----- Method: NaturalLanguageTranslator class>>loadForLocaleIsoString:fromGzippedMimeLiteral: (in category 'file-services') -----
loadForLocaleIsoString: localeString fromGzippedMimeLiteral: mimeString 
	"merge the translation from the mime literal."
	| stream localeID translator gs rbStream s currentPlatform |
	s := Base64MimeConverter mimeDecodeToBytes: mimeString readStream.
	s reset.
	gs := GZipReadStream on: s.
	rbStream := MultiByteBinaryOrTextStream with: gs contents asString.
	rbStream converter: UTF8TextConverter new.
	rbStream reset.
	localeID := LocaleID isoString: localeString.
	currentPlatform := Locale currentPlatform.
	[Locale
		currentPlatform: (Locale localeID: localeID).
	stream := ReadStream on: rbStream contents]
		ensure: [Locale currentPlatform: currentPlatform].
	translator := self localeID: localeID.
	translator loadFromStream: stream.
	LanguageEnvironment resetKnownEnvironments!

----- Method: NaturalLanguageTranslator class>>loadTranslatorForIsoLanguage:isoCountry: (in category 'private loading') -----
loadTranslatorForIsoLanguage: isoLanguage isoCountry: isoCountry 
	"private - load the translations from <prefs>/locale/ directory  
	the procedure is to assure the existence of a translator for the  
	given language/country and then load the external translations for this translator"

	| translator |
	translator := self localeID: (LocaleID isoLanguage: isoLanguage isoCountry: isoCountry).

	self loadExternalTranslationsFor: translator!

----- Method: NaturalLanguageTranslator class>>localeChanged (in category 'accessing') -----
localeChanged
	"notify some project starts to use this locale.
	 this facility may use the event to load translation data dynamically" 
	self current setCurrent
!

----- Method: NaturalLanguageTranslator class>>localeDirCreate: (in category 'private loading') -----
localeDirCreate: createDir
	"Try to locate the <prefs>/locale/ folder.
	If createDir is set, try to create the path.
	If it doesn't exist, return nil"

	"If this fails, there is nothing we can do about it here"
	| prefDir  localeDir |
	(createDir not
			and: [ExternalSettings preferenceDirectory isNil])
		ifTrue: [^ nil].

	prefDir := ExternalSettings assuredPreferenceDirectory.
	prefDir exists
		ifFalse: [^nil].


	localeDir := prefDir directoryNamed: 'locale'.
	createDir
		ifTrue: [localeDir assureExistence].
	^localeDir exists
		ifTrue: [localeDir]
		ifFalse: [nil]!

----- Method: NaturalLanguageTranslator class>>localeID: (in category 'accessing') -----
localeID: localeID 
	^ self cachedTranslations
		at: localeID
		ifAbsentPut: [self new localeID: localeID]!

----- Method: NaturalLanguageTranslator class>>mergeTranslationFileNamed: (in category 'file-services') -----
mergeTranslationFileNamed: fileFullNameString 
	"merge the translation in the file named fileFullNameString"

	FileStream readOnlyFileNamed: fileFullNameString do: [:stream |
		| localeID translator |
		localeID := LocaleID isoString: stream localName sansPeriodSuffix.
		translator := self localeID: localeID.
		translator loadFromStream: stream].
	LanguageEnvironment resetKnownEnvironments.

!

----- Method: NaturalLanguageTranslator class>>privateStartUp (in category 'class initialization') -----
privateStartUp
	self resetCaches.
	GetTextTranslator privateStartUp.
	InternalTranslator privateStartUp.
	self localeChanged.!

----- Method: NaturalLanguageTranslator class>>registerPhrase: (in category 'private') -----
registerPhrase: phrase
	"Using a Dictionary so we can lookup existing string instead of creating needless copies when loading a translation."
	self allKnownPhrases at: phrase put: phrase!

----- Method: NaturalLanguageTranslator class>>registeredPhraseFor: (in category 'private') -----
registeredPhraseFor: phrase
	"Using a Dictionary so we can lookup existing string instead of creating needless copies when loading a translation."
	^self allKnownPhrases at: phrase ifAbsentPut: [phrase]!

----- Method: NaturalLanguageTranslator class>>removeLocaleID: (in category 'accessing') -----
removeLocaleID: localeID 
	"self removeLocaleID: (LocaleID isoString: 'ja-kids')"
	^ self translators
		removeKey: localeID
		ifAbsent: []!

----- Method: NaturalLanguageTranslator class>>resetCaches (in category 'class initialization') -----
resetCaches
	Translators := nil.!

----- Method: NaturalLanguageTranslator class>>serviceMergeLanguageTranslations (in category 'file-services') -----
serviceMergeLanguageTranslations
	"Answer a service for merging of translation files"
	^ SimpleServiceEntry
		provider: self
		label: 'merge the translation file'
		selector: #mergeTranslationFileNamed:
		description: 'merge the translation file into the language named like the file'
		buttonLabel: 'merge'!

----- Method: NaturalLanguageTranslator class>>services (in category 'file-services') -----
services
	"Answer potential file services associated with this class"
	^ {self serviceMergeLanguageTranslations}!

----- Method: NaturalLanguageTranslator class>>startUp: (in category 'class initialization') -----
startUp: resuming 
	resuming
		ifFalse: [^ self].
	self privateStartUp.!

----- Method: NaturalLanguageTranslator class>>translate:toLocaleID: (in category 'translation') -----
translate: aString toLocaleID: localeID
	"translate for default domain"
	^ (self availableForLocaleID: localeID)
		translate: aString!

----- Method: NaturalLanguageTranslator class>>translate:toLocaleID:inDomain: (in category 'translation') -----
translate: aString toLocaleID: localeID inDomain: aDomainName
	^ (self availableForLocaleID: localeID)
		translate: aString inDomain: aDomainName!

----- Method: NaturalLanguageTranslator class>>translateWithoutLoading:toLocaleID: (in category 'translation') -----
translateWithoutLoading: aString toLocaleID: localeID
	"translate for default domain"
	^self translateWithoutLoading: aString toLocaleID: localeID inDomain: TextDomainManager defaultDomain.
!

----- Method: NaturalLanguageTranslator class>>translateWithoutLoading:toLocaleID:inDomain: (in category 'translation') -----
translateWithoutLoading: aString toLocaleID: localeID inDomain: aDomainName
	"try to translate with small footprint:
		if GetTextTranslator hasn't loaded MO, try to use InternalTranslator.
		if InternalTranslator isn't available, then actually load MO and use it"
	| translator |
	translator _ self availableForLocaleID: localeID.
	(translator isDomainLoaded: aDomainName) ifFalse: [
		(InternalTranslator availableLanguageLocaleIDs includes: localeID)
			ifTrue:  [translator _ InternalTranslator localeID: localeID].
	].
	^translator translate: aString inDomain: aDomainName!

----- Method: NaturalLanguageTranslator class>>translationSuffix (in category 'private') -----
translationSuffix
	^'translation'!

----- Method: NaturalLanguageTranslator class>>translators (in category 'accessing') -----
translators
	^ Translators ifNil: [Translators := Dictionary new]	!

----- Method: NaturalLanguageTranslator>>atRandom (in category 'accessing') -----
atRandom

	self subclassResponsibility.
!

----- Method: NaturalLanguageTranslator>>checkPhrase:translation: (in category 'translation') -----
checkPhrase: phrase translation: translation!

----- Method: NaturalLanguageTranslator>>defaultBackgroundColor (in category 'user interface') -----
defaultBackgroundColor
	"answer the receiver's defaultBackgroundColor for views"
	^ Color cyan!

----- Method: NaturalLanguageTranslator>>displayLanguage (in category 'accessing') -----
displayLanguage
	^ id displayLanguage!

----- Method: NaturalLanguageTranslator>>displayName (in category 'accessing') -----
displayName
	^ id displayName!

----- Method: NaturalLanguageTranslator>>domainRegistered: (in category 'accessing') -----
domainRegistered: aDomainName
	"notify that new TextDomain is registered.  Concrete subclass can responds to this event if needed"!

----- Method: NaturalLanguageTranslator>>domainUnregistered: (in category 'accessing') -----
domainUnregistered: aDomainName
	"notify that new TextDomain is unregistered.  Concrete subclass can responds to this event if needed"!

----- Method: NaturalLanguageTranslator>>fileOutHeader (in category 'fileIn/fileOut') -----
fileOutHeader
	^ '''Translation dictionary'''!

----- Method: NaturalLanguageTranslator>>fileOutHeaderOn: (in category 'fileIn/fileOut') -----
fileOutHeaderOn: aStream 
	aStream nextChunkPut: self fileOutHeader;
		 cr.
	aStream timeStamp; cr.
	aStream nextPut: $!!.
	aStream nextChunkPut: '(' , self class name , ' localeID: ' , id storeString , ')'.
	aStream cr!

----- Method: NaturalLanguageTranslator>>fileOutOn: (in category 'fileIn/fileOut') -----
fileOutOn: aStream 
	"self current fileOutOn: Transcript. Transcript endEntry"
	self fileOutHeaderOn: aStream.
	self fileOutOn: aStream keys: nil!

----- Method: NaturalLanguageTranslator>>fileOutOn:keys: (in category 'fileIn/fileOut') -----
fileOutOn: aStream keys: keys 
	"self current fileOutOn: Transcript. Transcript endEntry"
	(keys
		ifNil: [generics keys asArray sort])
		do: [:key | self
				nextChunkPut: (generics associationAt: key)
				on: aStream].
	keys
		ifNil: [self untranslated
				do: [:each | self nextChunkPut: each -> '' on: aStream]].
	aStream nextPut: $!!;
		 cr!

----- Method: NaturalLanguageTranslator>>generics (in category 'private') -----
generics
	^generics ifNil: [generics := Dictionary new]!

----- Method: NaturalLanguageTranslator>>isDomainLoaded: (in category 'accessing') -----
isDomainLoaded: aDomainName
	"ask if translation dictionary for the domain has been loaded and available now for use"
	self subclassResponsibility.!

----- Method: NaturalLanguageTranslator>>isoCountry (in category 'accessing') -----
isoCountry
	^self localeID isoCountry!

----- Method: NaturalLanguageTranslator>>isoLanguage (in category 'accessing') -----
isoLanguage
	^self localeID isoLanguage!

----- Method: NaturalLanguageTranslator>>loadFromFileNamed: (in category 'private store-retrieve') -----
loadFromFileNamed: fileNameString 
	"Load translations from an external file"

	FileStream readOnlyFileNamed: fileNameString do: [ :file |
		self loadFromStream: file ].
	self changed: #translations.
	self changed: #untranslated.
!

----- Method: NaturalLanguageTranslator>>loadFromRefStream: (in category 'private store-retrieve') -----
loadFromRefStream: stream 
	"Load translations from an external file"
	| loadedArray refStream |
	refStream := ReferenceStream on: stream.
	[loadedArray := refStream next]
		ensure: [refStream close].
	self processExternalObject: loadedArray !

----- Method: NaturalLanguageTranslator>>loadFromStream: (in category 'private store-retrieve') -----
loadFromStream: stream 
	"Load translations from an external file"
	| header isFileIn |
	header := '''Translation dictionary'''.
	isFileIn := (stream next: header size)
				= header.
	stream reset.
	isFileIn
		ifTrue: [stream fileInAnnouncing: 'Loading ' translated, stream localName]
		ifFalse: [self loadFromRefStream: stream]!

----- Method: NaturalLanguageTranslator>>localeID (in category 'accessing') -----
localeID
	^id!

----- Method: NaturalLanguageTranslator>>localeID: (in category 'initialize-release') -----
localeID: anID
	id := anID!

----- Method: NaturalLanguageTranslator>>mergeTranslations: (in category 'private store-retrieve') -----
mergeTranslations: newTranslations
	"Merge a new set of translations into the exiting table.
	Overwrites existing entries."

	newTranslations keysAndValuesDo: [:key :value |
		self rawPhrase: (self class registeredPhraseFor: key) translation: value].
	self changed: #translations.
	self changed: #untranslated.!

----- Method: NaturalLanguageTranslator>>nextChunkPut:on: (in category 'fileIn/fileOut') -----
nextChunkPut: anObject on: aStream 
	| i remainder terminator |
	terminator := $!!.
	remainder := anObject storeString.
	[(i := remainder indexOf: terminator) = 0]
		whileFalse: [aStream
				nextPutAll: (remainder copyFrom: 1 to: i).
			aStream nextPut: terminator.
			"double imbedded terminators"
			remainder := remainder copyFrom: i + 1 to: remainder size].
	aStream nextPutAll: remainder.
	aStream nextPut: terminator; cr.!

----- Method: NaturalLanguageTranslator>>phrase:translation: (in category 'translation') -----
phrase: phraseString translation: translationString 
	self generics at: phraseString put: translationString asString.
	self changed: #translations.
	self changed: #untranslated.!

----- Method: NaturalLanguageTranslator>>printOn: (in category 'printing') -----
printOn: aStream
	aStream nextPutAll: self class name; nextPut: $(; print: self localeID; nextPut: $)!

----- Method: NaturalLanguageTranslator>>processExternalObject: (in category 'private store-retrieve') -----
processExternalObject: anArray 
	"pivate - process the external object"

	"new format -> {translations. untranslated}"

	anArray second do: [:each | self class registerPhrase: each].

	self mergeTranslations: anArray first!

----- Method: NaturalLanguageTranslator>>rawPhrase:translation: (in category 'translation') -----
rawPhrase: phraseString translation: translationString 
	self generics at: phraseString put: translationString asString.
!

----- Method: NaturalLanguageTranslator>>rawRemoveUntranslated: (in category 'translation') -----
rawRemoveUntranslated: untranslated

	self class allKnownPhrases removeKey: untranslated ifAbsent: [].
	self changed: #untranslated.!

----- Method: NaturalLanguageTranslator>>removeTranslationFor: (in category 'translation') -----
removeTranslationFor: phraseString
	self generics removeKey: phraseString ifAbsent: [].
	self changed: #translations.
	self changed: #untranslated.!

----- Method: NaturalLanguageTranslator>>removeUntranslated: (in category 'translation') -----
removeUntranslated: untranslated

	self class allKnownPhrases removeKey: untranslated ifAbsent: [].
!

----- Method: NaturalLanguageTranslator>>saveToFileNamed: (in category 'private store-retrieve') -----
saveToFileNamed: fileNameString 
	"save the receiver's translations to a file named fileNameString"
	| stream |
	"Set true if you need to save as binary"
	false
		ifTrue: [stream := ReferenceStream fileNamed: fileNameString.
			stream nextPut: {self translations. self untranslated}.
			stream close.
			^ self].
	stream := FileStream fileNamed: fileNameString.
	[self fileOutOn: stream]
		ensure: [stream close]!

----- Method: NaturalLanguageTranslator>>scanFrom: (in category 'fileIn/fileOut') -----
scanFrom: aStream 
	"Read a definition of dictionary.  
	Make sure current locale corresponds my locale id"
	| newTranslations currentPlatform |
	newTranslations := Dictionary new.
	currentPlatform := Locale currentPlatform.
	[ | aString assoc |
	Locale
		currentPlatform: (Locale localeID: id).
	[aString := aStream nextChunk withSqueakLineEndings.
	aString size > 0]
		whileTrue: [assoc := Compiler evaluate: aString.
			assoc value = ''
				ifTrue: [self class registerPhrase: assoc key]
				ifFalse: [newTranslations add: assoc]]]
		ensure: [Locale currentPlatform: currentPlatform].
	self mergeTranslations: newTranslations!

----- Method: NaturalLanguageTranslator>>setCurrent (in category 'language switching') -----
setCurrent
	"notify locale of the translator become current"
!

----- Method: NaturalLanguageTranslator>>translate: (in category 'translation') -----
translate: aString
	^self translate: aString 
		inDomain: TextDomainManager defaultDomain!

----- Method: NaturalLanguageTranslator>>translate:in: (in category 'translation') -----
translate: aString in: aContext!

----- Method: NaturalLanguageTranslator>>translate:inDomain: (in category 'translation') -----
translate: aString inDomain: aDomainName
	^ aString!

----- Method: NaturalLanguageTranslator>>translationFor: (in category 'translation') -----
translationFor: aString
	^self translate: aString!

----- Method: NaturalLanguageTranslator>>translations (in category 'accessing') -----
translations
	^self generics!

----- Method: NaturalLanguageTranslator>>untranslated (in category 'accessing') -----
untranslated
	| translations |
	translations := self translations.
	^self class allKnownPhrases reject: [:each | translations includesKey: each]!

----- Method: NaturalLanguageTranslator>>writeAsMimeString (in category 'fileIn/fileOut') -----
writeAsMimeString

	| fileName fileStream tmpStream s2 gzs |
	tmpStream := MultiByteBinaryOrTextStream on: ''.
	tmpStream converter: UTF8TextConverter new.
	self fileOutOn: tmpStream.
	s2 := RWBinaryOrTextStream on: ''.
	gzs := GZipWriteStream on: s2.
	tmpStream reset.
	gzs nextPutAll: (tmpStream binary contentsOfEntireFile asString) contents.
	gzs close.
	s2 reset.

	fileName := id isoString, '.translation.gz.mime'.
	fileStream := FileStream newFileNamed: fileName.
	fileStream nextPutAll: (Base64MimeConverter mimeEncode: s2) contents.
	fileStream close.
!

Object subclass: #TextDomainManager
	instanceVariableNames: ''
	classVariableNames: 'ClassCategories Classes DefaultDomain DomainInfos LoneClasses Packages'
	poolDictionaries: ''
	category: 'GetText-Localization'!
TextDomainManager class
	instanceVariableNames: 'defaultDomain'!

!TextDomainManager commentStamp: 'tk 1/4/2008 16:08' prior: 0!
I manages mapping from class category to textdomain.

Class variables:
 ClassCategories	IdentityDictionary -- classCategory -> domainName 
 Classes			IdentityDictionary -- class name (a Symbol) -> domainName   (a cache only!!)
 DefaultDomain	String -- the default domain name
 DomainInfos		Dictionary -- domainName -> a TextDomainInfo
 LoneClasses		IdentityDictionary -- class name (a Symbol) -> domainName.  For classes whose entire category are not all in the same domain (BookMorph and QuickGuideMorph)

TextDomainManager registerCategoryPrefix: 'DrGeoII' domain: 'DrGeoII'.
TextDomainManager unregisterDomain: 'DrGeoII'.

TextDomainManager registerClass: #QuickGuideMorph domain: 'quickguides'.
TextDomainManager registerClass: #QuickGuideHolderMorph  domain: 'quickguides'.
!
TextDomainManager class
	instanceVariableNames: 'defaultDomain'!

----- Method: TextDomainManager class>>allKnownDomains (in category 'accessing') -----
allKnownDomains
"Every package has it's own text domain now so it's not necessary to keep a registry of all domains, we can simply return all the packages in the image.
PROBLEM: If a package doesn't contain translations, it won't have a mo file but the GetTextTranslator will try to load it anyway. This happens when we switch languages. So far I tested it briefly and it seems to work..."
^PackageOrganizer default packageNames!

----- Method: TextDomainManager class>>allMethodsWithTranslations (in category 'accessing') -----
allMethodsWithTranslations
"Look for #translated calls"
| methodsWithTranslations |
methodsWithTranslations := TranslatedReceiverFinder new stringReceiversWithContext: #translated.
methodsWithTranslations := methodsWithTranslations, (TranslatedReceiverFinder new
stringReceiversWithContext: #translatedNoop).

methodsWithTranslations := methodsWithTranslations collect: [:each | each key compiledMethod].

"Look for Etoys tiles and vocabularies"
methodsWithTranslations := methodsWithTranslations, (EToyVocabulary allPhrasesWithContextToTranslate collect: [:r |
	(MethodReference new setStandardClass: r second methodSymbol: r third) compiledMethod.
]).

^methodsWithTranslations!

----- Method: TextDomainManager class>>clearAllDomains (in category 'private') -----
clearAllDomains
	SystemNavigation default
		allCompiledMethodDo: [:each | each
				removeProperty: self textDomainProperty
				ifAbsent: []] !

----- Method: TextDomainManager class>>defaultDomain (in category 'accessing') -----
defaultDomain
"I'm not sure we still need a default domain, AFAIK the default domain will only be used when no domain is found. In that case, wouldn't it be better to just look for a translation in all domains?"
	^defaultDomain!

----- Method: TextDomainManager class>>defaultDomain: (in category 'accessing') -----
defaultDomain: aDomainName
	defaultDomain := aDomainName!

----- Method: TextDomainManager class>>domainForClass: (in category 'accessing') -----
domainForClass: aClass
^'etoys'!

----- Method: TextDomainManager class>>domainForPackage: (in category 'accessing') -----
domainForPackage: aPackageInfo
"Package names and text domains are synonyms now"
	^aPackageInfo name!

----- Method: TextDomainManager class>>domainOfMethod: (in category 'accessing') -----
domainOfMethod: aCompiledMethod 
	^ aCompiledMethod
		propertyValueAt: self textDomainProperty
		ifAbsent: [self updateDomainOfMethod: aCompiledMethod] !

----- Method: TextDomainManager class>>initialize (in category 'class initialization') -----
initialize
	"	TextDomainManager initialize	"
	self defaultDomain: 'Etoys'!

----- Method: TextDomainManager class>>textDomainProperty (in category 'private') -----
textDomainProperty
^#textDomain!

----- Method: TextDomainManager class>>updateDomainOfAllMethodsWithTranslations (in category 'private') -----
updateDomainOfAllMethodsWithTranslations
self allMethodsWithTranslations do: [:each|
	self updateDomainOfMethod: each
]!

----- Method: TextDomainManager class>>updateDomainOfMethod: (in category 'private') -----
updateDomainOfMethod: aCompiledMethod 
	"First it looks for the package of the method reference (using
	the PackageOrganizer: deadly slow). If the method doesn't
	belong to any package it uses the default domain. Finally it
	stores the text domain of the method using a method
	property, this way we gain performance the next time we
	translate the same method because we avoid the use of
	PackageOrganizer. Have I mentioned it is really slow? :)"
	| package |
	package := PackageOrganizer default
				packageOfMethod: aCompiledMethod methodReference
				ifNone: [].
	^ aCompiledMethod
		propertyValueAt: self textDomainProperty
		put: (package isNil
				ifTrue: [TextDomainManager defaultDomain]
				ifFalse: [package name])!

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

----- Method: TranslatedReceiverFinder class>>browseNonLiteralReceivers (in category 'utilities') -----
browseNonLiteralReceivers
	"TranslatedReceiverFinder browseNonLiteralReceivers"
	SystemNavigation default
		browseMessageList: self new nonLiteralReceivers  asSortedCollection
		name: 'Non literal receivers of #translated'
		autoSelect: 'translated'!

----- Method: TranslatedReceiverFinder class>>makeJapaneseTranslationFile (in category 'as yet unclassified') -----
makeJapaneseTranslationFile
	| t n |
	NaturalLanguageTranslator initializeKnownPhrases.
	t := TranslatedReceiverFinder new senders.
	n := NaturalLanguageTranslator
				localeID: (LocaleID isoLanguage: 'ja').
	t
		do: [:w | 
			NaturalLanguageTranslator registerPhrase: w.
			self
				at: w
				ifPresent: [:k | n phrase: w translation: k]].
	n saveToFileNamed: 'ja.translation'!

----- Method: TranslatedReceiverFinder>>arraySearch:fromArray:addTo: (in category 'private') -----
arraySearch: aSymbol fromArray: anArray addTo: aCollection 
	"Find literals ahead of aSymbol from arrays in the method."
	"BUG: it can handle just one occurrence"
	"self new arraySearch: #hello fromArray: #(ignore (ignore detected
	hello ignore)) addTo: Set new"
	| index |
	(index := anArray identityIndexOf: aSymbol) > 1
		ifTrue: [aCollection add: (anArray at: index - 1) asString].
	(anArray
		select: [:each | each isMemberOf: Array])
		do: [:each | self
				arraySearch: aSymbol
				fromArray: each
				addTo: aCollection].
	^ aCollection!

----- Method: TranslatedReceiverFinder>>arraySearch:messageNode:addTo: (in category 'private') -----
arraySearch: aSymbol messageNode: aParseNode addTo: aCollection 
	"Find literals ahead of aSymbol from arrays in the method."
	"self new arraySearch: #hello messageNode: (self
	decompile: #arraySearch:messageNode:addTo:) addTo: Set new"
	self flag: #(#ignore #detected #hello ).
	((aParseNode isMemberOf: LiteralNode)
			and: [aParseNode key isMemberOf: Array])
		ifTrue: [self
				arraySearch: aSymbol
				fromArray: aParseNode key
				addTo: aCollection].
	(aParseNode notNil
			and: [aParseNode isLeaf not])
		ifTrue: [aParseNode getAllChildren
				do: [:child | self
						arraySearch: aSymbol
						messageNode: child
						addTo: aCollection]].
	^ aCollection!

----- Method: TranslatedReceiverFinder>>findWordsWith:in: (in category 'accessing') -----
findWordsWith: aSymbol in: aMethodReference 
	"Find words for translation with the symbol in a method. See
	LanguageEditorTest >>testFindTranslatedWords"
	"| message | 
	message := MethodReference new setStandardClass: Morph class
	methodSymbol: #supplementaryPartsDescriptions.
	self new findWordsWIth: #translatedNoop in: message"
	| messages keywords aParseNode |
	aParseNode := aMethodReference decompile.
	"Find from string literal"
	messages := Set new.
	self
		search: aSymbol
		messageNode: aParseNode
		addTo: messages.
	keywords := OrderedCollection new.
	messages
		select: [:aMessageNode | aMessageNode receiver isMemberOf: LiteralNode]
		thenDo: [:aMessageNode | aMessageNode receiver key
				literalStringsDo: [:literal | keywords add: literal]].
	"Find from array literal"
	self
		arraySearch: aSymbol
		messageNode: aParseNode
		addTo: keywords.
	^ keywords!

----- Method: TranslatedReceiverFinder>>nonLiteralReceivers (in category 'accessing') -----
nonLiteralReceivers
	"self new nonLiteralReceivers"
	| receivers |
	"Answer method references of non literal senders of #translated"
	^ (SystemNavigation default allCallsOn: #translated)
		select: [:message | 
			receivers := OrderedCollection new.
			self search: #translated messageNode: message decompile addTo: receivers.
			receivers
				anySatisfy: [:each | (each receiver isMemberOf: LiteralNode) not]]!

----- Method: TranslatedReceiverFinder>>search:messageNode:addTo: (in category 'private') -----
search: aSymbol messageNode: aParseNode addTo: aCollection 
	"self new search: #translated messageNode: (Project decompile: #updateLocaleDependentsWithPreviousSupplies:gently:) addTo: OrderedCollection new"

	((aParseNode isMemberOf: MessageNode)
			and: [(aParseNode selector isMemberOf: SelectorNode)
					and: [aParseNode selector key = aSymbol]])
		ifTrue: [aCollection add: aParseNode].
	(aParseNode notNil
			and: [aParseNode isLeaf not])
		ifTrue: [aParseNode getAllChildren
				do: [:child | self
						search: aSymbol
						messageNode: child
						addTo: aCollection]].
	^ aCollection!

----- Method: TranslatedReceiverFinder>>searchBlockNode:addTo: (in category 'as yet unclassified') -----
searchBlockNode: aBlockNode addTo: aCollection

	aBlockNode statements do: [:e |
		(e isMemberOf: MessageNode) ifTrue: [self searchMessageNode: e addTo: aCollection].
		(e isMemberOf: ReturnNode) ifTrue: [self searchReturnNode: e addTo: aCollection].
	].
!

----- Method: TranslatedReceiverFinder>>searchMessageNode:addTo: (in category 'as yet unclassified') -----
searchMessageNode: aMessageNode addTo: aCollection

	((aMessageNode receiver isMemberOf: LiteralNode) and: [(aMessageNode selector isMemberOf: SelectorNode) and: [aMessageNode selector key = #translated]]) ifTrue: [
		aCollection add: aMessageNode receiver key.
	].

	(aMessageNode receiver isMemberOf: BlockNode) ifTrue: [self searchBlockNode: aMessageNode receiver addTo: aCollection].
	(aMessageNode receiver isMemberOf: MessageNode) ifTrue: [self searchMessageNode: aMessageNode receiver addTo: aCollection].
	(aMessageNode receiver isMemberOf: ReturnNode) ifTrue: [self searchReturnNode: aMessageNode receiver addTo: aCollection].

	aMessageNode arguments do: [:a |
		(a isMemberOf: BlockNode) ifTrue: [self searchBlockNode: a addTo: aCollection].
		(a isMemberOf: MessageNode) ifTrue: [self searchMessageNode: a addTo: aCollection].
		(a isMemberOf: ReturnNode) ifTrue: [self searchReturnNode: a addTo: aCollection].
	].
!

----- Method: TranslatedReceiverFinder>>searchMethodNode:addTo: (in category 'as yet unclassified') -----
searchMethodNode: aMethodNode addTo: aCollection

	(aMethodNode block isMemberOf: BlockNode) ifTrue: [self searchBlockNode: aMethodNode block addTo: aCollection].
	(aMethodNode block isMemberOf: MessageNode) ifTrue: [self searchMessageNode: aMethodNode block addTo: aCollection].
	(aMethodNode block isMemberOf: ReturnNode) ifTrue: [self searchReturnNode: aMethodNode block addTo: aCollection].
!

----- Method: TranslatedReceiverFinder>>searchReturnNode:addTo: (in category 'as yet unclassified') -----
searchReturnNode: aReturnNode addTo: aCollection

	(aReturnNode expr isMemberOf: BlockNode) ifTrue: [self searchBlockNode: aReturnNode expr addTo: aCollection].
	(aReturnNode expr isMemberOf: MessageNode) ifTrue: [self searchMessageNode: aReturnNode expr addTo: aCollection].
!

----- Method: TranslatedReceiverFinder>>senders (in category 'as yet unclassified') -----
senders

	| m o |
	m := SystemNavigation default allCallsOn: #translated.
	m := m collect: [:e |
		e classIsMeta ifTrue: [
			(Smalltalk at: e classSymbol) class decompile: e methodSymbol.
		] ifFalse: [
			(Smalltalk at: e classSymbol) decompile: e methodSymbol.
		]
	].

	o := OrderedCollection new.
	m do: [:e | self searchMethodNode: e addTo: o].
	^ o sort
!

----- Method: TranslatedReceiverFinder>>stringReceivers (in category 'accessing') -----
stringReceivers
	"TranslatedReceiverFinder new stringReceivers"
	| stringReceivers messages |
	messages := Set new.
	(SystemNavigation default allCallsOn: #translated)
		do: [:message | self search: #translated messageNode: message decompile addTo: messages].
	stringReceivers := messages
				select: [:each | each receiver isMemberOf: LiteralNode]
				thenCollect: [:each | each receiver key].
	^ stringReceivers asArray sort!

----- Method: TranslatedReceiverFinder>>stringReceiversWithContext (in category 'accessing') -----
stringReceiversWithContext
	| mrs results rr cls mn t o |
	mrs _ SystemNavigation default allCallsOn: #translated.
	results _ OrderedCollection new.
	mrs do: [:mr |
		rr _ OrderedCollection new.
		cls _ Smalltalk at: mr classSymbol.
		rr add: cls category.
		rr add: mr classSymbol.
		rr add: mr methodSymbol.
		mr classIsMeta ifTrue: [
			mn _  cls class decompile: mr methodSymbol.
		] ifFalse: [
			mn _ cls decompile: mr methodSymbol.
		].
		o _ OrderedCollection new.
		t _ Set new.
		self searchMessageNode: mn addTo: t.
		t do: [ :te |
			(te receiver isMemberOf: LiteralNode) ifTrue: [ 
			    o add: te receiver key.
			].
		].
		o ifNotEmpty: [
			rr add: o.
			results add: rr.
		].
	].
	^ results.

!

----- Method: TranslatedReceiverFinder>>stringReceiversWithContext: (in category 'accessing') -----
stringReceiversWithContext: aSymbol
	"Find string receivers for a symbol.
	Answer a collection of aMethodReference -> {keyword. keyword...}"
	"self new stringReceiversWithContext: #translated"
	| keywords methodReferences |
	methodReferences _ SystemNavigation default allCallsOn: aSymbol.
	^ methodReferences inject: OrderedCollection new into: [:list :next |
		keywords := self findWordsWith: aSymbol in: next.
		keywords
			ifNotEmpty: [list add: next -> keywords].
		list]
!



More information about the Packages mailing list