2010/9/10 Stanislav Paskalev <[email protected]>:
> Seaside had none the last time I asked. Please do so.
I am in a bit hurry, but I hope you can still make sense out of this.
The whole seaside and translation thing is based on the fact that
Seaside render: method is dispatched to objects renderOn: method where
we can access session. On the other hand
we can store language to session. If we implement object that is able
to answer correctly to translate: -message with language as a
parameter we have a simple translation framework.
I created a simple translation framework which is able to return
translation for certain key. Keys are looked from properly named
subclass. Names are form of LanguagePackOfProgramX_fi,
LanguagePackOfProgramX_en. Each containing translations for the given
language. K3LanguagePack and tests should give better idea.
Notice that examples below need language -method in session....
Example to use with seaside:
renderContentOn: html
html render: (LanguegPackOfMyProgram translationFor: #greetingMessge)
Example with magritte:
descriptionEmail
^MAStringDescription new
accessor: #email;
label: (K3CoreLanguagePack translationFor: #email);
addCondition: [:value | value includes: $...@] labelled:
(LanguegPackOfMyProgram translationFor: #emailNotValid);
beRequired; requiredErrorMessage:
(LanguegPackOfMyProgram translationFor: #emailIsRequired);
priority: 10;
yourself.
The magritte part was builded on top of 2.0.5 magritte-seaside.
This code enabled me to build seaside application demo that could
change its user interface on the fly. Meaning that anything else stays
as it is, only the language changes.
But I don't know how useful this is for other people.
Currently the major flaw is that there is no way to set the arguments
for the translation ('Hello user, your name is {1}'). It is not a big
addition but haven't needed it in this proof of concept. However it
should not be very hard to implement.
Other thing that should be discussed is the way translations are
stored. I chose to use symbols that points to methods and thus
translations can be encapsulated in classes. However there seems to be
NaturalLanguageTranslator and string translate already in existence
and I am wondering should I change the implementation to use them
instead.
I am very open to suggestions.
--
Panu
'From Pharo-1.1-11411 of 17 July 2010 [Latest update: #11411] on 10 September 2010 at 3:24:56 pm'!
Object subclass: #K3LanguagePack
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation'!
!K3LanguagePack commentStamp: '<historical>' prior: 0!
K3LanguagePack is superclass of classes containing key based translations. To use K3LanguagePack based translations one should subclass it in the following way:
K3LanguagePack
SpesificLanguagePackForMyProgram
SpesificLanguagePackForMyProgram_fi
SpesificLanguagePackForMyProgram_en
SpesificLanguagePackForMyOtherProgram
SpesificLanguagePackForMyOtherProgram_fi
SpesificLanguagePackForMyOtherProgram_en
To get translation objects use the general class name. For example:
translation := SpesificLanguagePackForMyProgram translationFor: #abcd
To get the translated text call:
translation translate: 'fi'.
!
K3LanguagePack subclass: #K3LanguagePackForTesting
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Test'!
K3LanguagePackForTesting subclass: #K3LanguagePackForTesting_en
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Test'!
K3LanguagePackForTesting subclass: #K3LanguagePackForTesting_fi
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Test'!
Error subclass: #K3LanguagePackNotFound
instanceVariableNames: 'locale languagePack'
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation'!
MAValidationDecoration subclass: #K3TranslatedValidationDecorator
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Magritte-Seaside'!
!K3TranslatedValidationDecorator commentStamp: '<historical>' prior: 0!
This is simple override for MAValidationDecoration to enable translation of error messages returned by Magritte validators.
Decorator only calls render for both the label and the error message.
Maybe this behaviour could be changed to the MAValidationDecoration itself...!
Object subclass: #K3Translation
instanceVariableNames: 'translation item arguments'
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation'!
!K3Translation commentStamp: '<historical>' prior: 0!
K3Translation represents something that can be translated using spesific procedure. That procedure is stored in translation instance variable.
Instance Variables:
translation <Block> Represents the actual translation logic. Block must take 3 parameters (language, item, argumets) and return the translated string.
item <Object> Something that translation block understands. Marks the meaning of translation.
arguments <Collection of: Object> Collection of arguments passed to translation. When doing the translation value message is send to every object of the collection and returned value is passed to translation block.!
TestCase subclass: #K3TranslationTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Test'!
TestCase subclass: #KorppiLanguagePackTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Korppi-Translation-Test'!
!K3LanguagePack methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 12:05'!
doesNotUnderstand: aMessage
^self notTranslated.! !
!K3LanguagePack methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 10:50'!
notTranslated.
"Because this is language pack subclass responsibility means that text is just not translated yet.
This could be handled using exception and by defaul ask translation.
Now we just return something.
"
^'not translated'! !
!K3LanguagePack class methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 14:23'!
for: locale
| actualClassName wantedClassName|
wantedClassName := (self name,'_',locale).
actualClassName := Smalltalk at: wantedClassName asSymbol ifAbsent:
[K3LanguagePackNotFound ofType: self forLocale: locale].
^actualClassName new.! !
!K3LanguagePack class methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 14:51'!
locale
"Returns the locale of the"
^(self name subStrings: '_') at:2 ifAbsent:[Error signal: 'This class is not translated language pack.'].! !
!K3LanguagePack class methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 15:13'!
translationFor: aKey
"TODO: Arguments are not used currently."
^K3Translation new item: aKey; translation: [:language :key :arguments |
(self for: language) perform: key
].
! !
!K3LanguagePackForTesting methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/8/2010 11:42'!
testTranslation
self subclassResponsibility.! !
!K3LanguagePackForTesting_en methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/8/2010 11:58'!
testTranslation
^'in English'.! !
!K3LanguagePackForTesting_fi methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/8/2010 11:58'!
testTranslation
^'suomeksi'! !
!K3LanguagePackNotFound methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/8/2010 12:40'!
locale: aByteString ofLanguegPack: aClass
locale := aByteString.
languagePack := aClass.
self messageText: 'No locale for ',languagePack name. ! !
!K3LanguagePackNotFound class methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/8/2010 12:41'!
ofType: aClass forLocale: aByteString
(self new) locale: aByteString ofLanguegPack: aClass; signal.! !
!K3TranslatedValidationDecorator methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 10:43'!
renderErrorsOn: html
(html unorderedList)
class: 'errors';
with: [ self errors do: [ :e | html listItem with: [ html render: e tag label; text:': ';render: e messageText ] ] ]! !
!K3Translation methodsFor: 'accessing' stamp: 'PanuSuominen 9/9/2010 08:58'!
arguments
^ arguments! !
!K3Translation methodsFor: 'accessing' stamp: 'PanuSuominen 9/9/2010 08:58'!
item
^ item! !
!K3Translation methodsFor: 'accessing' stamp: 'PanuSuominen 9/9/2010 09:02'!
translation
^translation! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 08:48'!
arguments: anArray
"Sets the arguments that are passed to translation block. Arguments can be
blocks or objects. Blocks are evaluated every time before translation mehtod.
"
arguments := anArray.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 10:16'!
do: aBlock
self asString do: aBlock.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 09:04'!
evaluatedArguments
"Returns array of evaluated arguments. Blocks in arguments are
evaluated and regular objects simply returned."
^ self arguments isNil
ifTrue: [ Array new ]
ifFalse: [
arguments
collect: [ :i | i value ]]! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 09:30'!
isEmpty
^false.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 08:40'!
item: anObject
"Item that represents the data to be translated. Item should be understood by
the block set using translation: method."
item := anObject.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 15:02'!
printOn: aStream
"
Prints this translation as is. Currenlty it can not determine language so only item is printed.
Should think some way to lure language here.
"
aStream nextPutAll: self item.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 15:02'!
renderOn: canvas
"Method for enabling seaside translation. To make this work session should contain language method."
canvas text: (self translate: (canvas session language)).! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 09:19'!
translate: language
"Performs translation using translation block."
^self translation value: language value: self item value value: self evaluatedArguments.! !
!K3Translation methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/9/2010 09:32'!
translation: aBlock
"Receives block that can have two parameters.
First parameter is the language.
Second parameter is the item to be translated.
Third is array of parameters.
"
aBlock isBlock ifFalse: [Error signal: 'Currently I only understand blocks!!'].
translation := aBlock.! !
!K3TranslationTest methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 15:06'!
testArgumentBlock
|t a|
a := 1.
t := K3Translation new translation: [:l :s :p |
self should: [2 = p size] description: 'Wrong size argument array. Size was ', p size asString.
p first asString, p second asString
]; item: #string.
t arguments: {[a]. 2}.
self should: ['12' = (t translate: 'fi')].
a:=2.
self should: ['22' = (t translate: 'fi')].! !
!K3TranslationTest methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 15:07'!
testTranslationBlock
|t|
t := K3Translation new translation: [:l :s :p|
self should:[l = 'en'].
s asString]; item: #string.
self should: ['string' = (t translate: 'en')].! !
!KorppiLanguagePackTest methodsFor: 'as yet unclassified' stamp: 'PanuSuominen 9/10/2010 14:43'!
testTranslationFor
| t |
t := K3LanguagePackForTesting translationFor: #testTranslation.
self should:['suomeksi' = (t translate: 'fi')].
self should:['in English' = (t translate: 'en')].! !
_______________________________________________
Pharo-users mailing list
[email protected]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-users