|
Class: ResourcePack
Object
|
+--Collection
|
+--Set
|
+--Dictionary
|
+--ResourcePack
|
+--UISettings
|
+--ViewStyle
- Package:
- stx:libview
- Category:
- Interface-Internationalization
- Version:
- rev:
1.171
date: 2017/11/27 14:33:55
- user: cg
- file: ResourcePack.st directory: libview
- module: stx stc-classLibrary: libview
- Author:
- Claus Gittinger
This class supports easy customization of smalltalk code (i.e. internationalization
and viewStyle adaption).
ResourcePacks are class specific, meaning that every subclass of View
and ApplicationModel has an instance of ResourcePack (instVar called 'resources')
which is created when the first instance of the view/app is created,
and cached in a class-instVar (so the file is only read once).
The resourcePack consists of a mapping from strings to values, which are
then used in labels, buttons, menus etc.
The resourcePack initializes itself from a file found in 'resources/<className>.rs',
where 'className' is built by the usual abbreviation mechanism (see abbrev-files).
Conditional mappings are possible, by including lines as:
#if <expression>
#endif
in the resourcefile. Example:
file 'foo.rs':
#if Language == #de
'abort' 'Abbruch'
#endif
#if Language == #fr
'abort' 'canceller'
#endif
the corresponding resource-strings are accessed (from methods within the class)
using:
resources string:'abort'
returning the mapped string (i.e. 'Abbruch' if the global Language is set
to #de)..
If no corresponding entry is found in the resources, the key is returned;
alternatively, use:
resources string:'foo' default:'bar'
which returns 'bar', if no resource definition for 'foo' is found.
Translations can also include arguments, such as:
resources string:'really delete %1' with:fileName
This scheme has the advantage, that you can write your programs using your
native language strings. Later, when new languages are to be supported,
simply create a resource file for the class and add translations for
all those strings. (find the keys by looking at users of resource or senders
of 'string:*').
Notice, that the grammar of different languages may imply a reordering,
so the above string becomes the german 'wollen Sie %1 wirklich löschen';
so using percent-placeholders is much better than simple concatenations of
arguments to the question.
More languages can be added later without any change in the code, or recompilation
or the like. Even by people without access to the source code (i.e. which only have the
applications binary).
Also, unsupported languages' strings are simply left unchanged - if you
write your application in (say) english, and only plan to use it in english,
no additional work is required (i.e you don't even need a resource file then).
Strings for unknown languages will come in english
(which is better than nothing or empty button labels ;-)
Notice, that you can also translate english to english, by providing an en.rs file.
This is sometimes useful to fix typing errors or bad syntax in the english,
as sometimes made by the programmer, without a need to recompile or to also adjust other
language translations.
Finally, this scheme is also compatible to a pure enum-key based translation mechanism,
as typically used in the C-world.
Simple use keys as argument, and provide translations for all languages (incl. english).
For example:
Button label:(resources string:#BTN_FOO_LABEL)
Summary:
in subclasses of View and ApplicationModel, instead of writing:
...
b := Button label:'press me'
...
always write:
...
b := Button label:(resources string:'press me')
...
if your class is not a subclass of one of the above, AND you need
resource translations, you won't inherit the resources variable
(which is automatically initialized).
In this case, you have to ask the ResourcePack class explicitely for
a corresponding package:
ResourcePack for:aClassName
or (even better):
ResourcePack forPackage:aPackageID
as an example, see how the Date class gets the national names of
week & monthnames.
Debugging:
in the past, it happened that strings as returned by me were modified by someone else
(replaceAll:with:) and then lead to invalid presentation in the future.
To detect any bad guy which writes into one of my returned strings, set the DebugModifications
classVar to true. Then I will return ImmutableStrings which trap on writes.
initialization
-
flushCachedResourcePacks
-
forget all cached resources - needed after a style change
usage example(s):
ResourcePack flushCachedResourcePacks
|
-
initialize
-
ResourcePack initialize
instance creation
-
for: aClass
-
get the full resource definitions for aClass (i.e. with super packs).
Also leave the resulting pack in the cache for faster access next time.
usage example(s):
ResourcePack for:TextView
ResourcePack for:CodeView
ResourcePack for:Workspace
ResourcePack for:View
ResourcePack for:ErrorLogger
ResourcePack for:NewLauncher
ResourcePack for:SmallSense::SettingsAppl
Workspace classResources
|
-
for: aClass cached: cached
-
get the full resource definitions for aClass (i.e. with super packs).
Also leave the resulting pack in the cache for faster access next time.
usage example(s):
ResourcePack forPackage:'bosch:dapasx' cached:true
ResourcePack for:TextView
ResourcePack for:CodeView
ResourcePack for:Workspace
ResourcePack for:View
ResourcePack for:ErrorLogger
ResourcePack for:NewLauncher
Workspace classResources
|
-
forPackage: package
-
get the full resource definitions given a package id (such as stx:libbasic').
Also leave the resulting pack in the cache for faster access next time.
usage example(s):
ResourcePack forPackage:'stx:libbasic'
ResourcePack forPackage:'stx:libtool'
|
-
forPackage: package cached: cached
-
get the full resource definitions given a package id (such as stx:libbasic').
Also optionally leave the resulting pack in the cache for faster access next time.
usage example(s):
ResourcePack forPackage:'stx:libbasic' cached:false
|
-
forPackage: package resourceFileName: resourceFileName cached: cached
-
get the full resource definitions given a package id (such as stx:libbasic').
Also optionally leave the resulting pack in the cache for faster access next time.
Remember failed packs, to avoid retrying reading the file again and again
usage example(s):
ResourcePack forPackage:'stx:libbasic' resourceFileName:'resources.rs' cached:false
|
-
fromFile: aFileName
-
get the resource definitions from a file in the default directory.
usage example(s):
ResourcePack fromFile:'SBrowser.rs'
ResourcePack fromFile:'FBrowser.rs'
ResourcePack fromFile:'Smalltalk.rs'
ResourcePack fromFile:'Smalltalk.rs' asFilename
ResourcePack fromFile:'../../libtool/resources/AboutBox_ru.rs' asFilename
|
-
fromFile: aFileName directory: dirName
-
get the resource definitions from a file in a directory.
-
fromFile: aFileName directory: dirName cached: cached
-
get the resource definitions from a file in a directory.
Uncached low-level entry.
private
-
addToCache: aPack
-
throw away oldest
-
searchCacheFor: aClassOrFileName
-
bring to front for LRU
usage example(s):
ResourcePack searchCacheFor:'TextView'
|
utilities
-
extractEncodingFromLine: lineString
-
-
processResourceLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printError for: aResourcePack
-
process a single valid line (i.e. #ifdef & #include has already been processed)
-
processResourceLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printError for: aResourcePack keepUselessTranslations: keepUselessTranslations
-
process a single valid line (i.e. #ifdef & #include has already been processed)
-
resourceFileEntryFor: keyString to: nationalString
-
generate a line for a translation file, which defines a translation
from keyString to nationalString.
Naivly, this could be a simple line containing the two storeStrings
separated by a space. However, it is better to first cut of any leading
and trailing spaces and special characters, such as ':*.,' etc.
-
resourceFileStringFor: aString
-
generate a key or value entry for a translation file, for aString.
Naivly, this could be a simple the storeString.
However, it is better to first cut of any leading
and trailing spaces and special characters, sch as ':*.,' etc.
usage example(s):
self resourceFileStringFor:' foo: '
self resourceFileStringFor:' foo bar: '
|
-
shortenedKeyFor: aKey
-
if
aKey is '(...)', then return '...'
if aKey is '[...]', then return '...'
if aKey is '{...}', then return '...'
if aKey starts or ends with any of '\:=.,?! ', then return aKey without it
This means, that only a single translation is required to provide local translations for
things like
'search'
'search:'
'search...'
usage example(s):
'abcde' findFirst:[:ch | 'bcd' includes:ch]
'abcde' indexOfAny:'bcd'
self shortenedKeyFor:'abc'
self shortenedKeyFor:' abc '
self shortenedKeyFor:'(abc)'
self shortenedKeyFor:'abc...'
self shortenedKeyFor:'(abc...)'
self shortenedKeyFor:'abc:*'
|
accessing
-
array: anArray
-
translate a collection of strings
usage example(s):
Launcher classResources array:#('file' 'classes')
|
-
at: aKey
-
translate a string
-
at: aKey default: default
-
translate a string
-
at: aKey ifAbsent: defaultValue
-
translate a string; search here, in my projects pack and in my superpack(s)
-
localAt: aKey
-
translate a string.
Some special 'intelligence' has been added:
if no value for aKey is found,
lookup aKey with first character caseChanged and change the results first characters case.
or aKey is '(...)', then lookup ... wrap () around the result.
or aKey is '[...]', then lookup ... wrap [] around the result.
or aKey is '{...}', then lookup ... wrap {} around the result.
or aKey starts with a '\', then lookup aKey without '\' and prepend '\' to the result.
or aKey starts with a '*', then lookup aKey without '*' and prepend '*' to the result.
or aKey ends with a '\', then lookup aKey without '\' and append '\' to the result.
or aKey ends with a ':', then lookup aKey without ':' and append ':' to the result.
or aKey ends with a '=', then lookup aKey without '=' and append '=' to the result.
or aKey ends with a '.', then lookup aKey without '.' and append '.' to the result.
or aKey ends with a ',', then lookup aKey without ',' and append ',' to the result.
or aKey ends with a '?', then lookup aKey without '?' and append '?' to the result.
or aKey ends with a '!', then lookup aKey without '!' and append '!' to the result.
or aKey ends with a '*', then lookup aKey without '*' and append '*' to the result.
or aKey ends with a ' ', then lookup aKey without ' ' and append ' ' to the result.
or aKey ends with a ' ...', then lookup aKey without ' ...' and append '...' to the result.
or aKey ends with a '...', then lookup aKey without '...' and append '...' to the result.
or aKey includes '&', then lookup aKey without '&'.
This means, that only a single translation is required to provide local translations for
things like
'search'
'search:'
'search...'
-
name: aKey default: default
-
translate a string.
Obsolete - use #string:default:
** This is an obsolete interface - do not use it (it may vanish in future versions) **
-
string: s
-
translate (retrieve) a string - if not present, return s
usage example(s):
NewLauncher classResources
string:'LICENCEFILE'
|
-
string: s default: defaultString
-
translate (retrieve) a string - if not present, return defaultString
usage example(s):
NewLauncher classResources
string:'fooBar' default:'Hello world'
|
-
string: s default: defaultString with: arg
-
translate and expand arg
usage example(s):
NewLauncher classResources
string:'%1 fooBar' default:'Hello %1' with:'foo'
|
-
string: s default: defaultString with: arg1 with: arg2
-
translate and expand args
-
string: s default: defaultString with: arg1 with: arg2 with: arg3
-
translate and expand args
-
string: s default: defaultString with: arg1 with: arg2 with: arg3 with: arg4
-
translate and expand args
-
string: s default: defaultString withArgs: argArray
-
translate and expand args
-
string: s with: arg
-
translate and expand arg
-
string: s with: arg1 with: arg2
-
translate and expand args
-
string: s with: arg1 with: arg2 with: arg3
-
translate and expand args
-
string: s with: arg1 with: arg2 with: arg3 with: arg4
-
translate and expand args
-
string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5
-
translate and expand args
-
string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6
-
translate and expand args
-
string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7
-
translate and expand args
-
string: s withArgs: argArray
-
translate and expand args - allow text as arguments
-
stringWithCRs: s
-
translate (retrieve) a string - if not present, return s
usage example(s):
NewLauncher classResources
stringWithCRs:'LICENCEFILE'
|
-
stringWithCRs: s with: arg
-
translate, replace \'s with CRs and finally expand arg.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2 with: arg3
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2 with: arg3 with: arg4
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is donw before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
stringWithCRs: s withArgs: argArray
-
translate, replace \'s with CRs and finally expand args.
CR-replacement is done before args are inserted
i.e. if any arg contains a backslash (DOS filenames), those are not translated.
-
whichPackIncludesKey: aKey
-
for debugging: return the pack (alogn the super-pack chain), which
has a translation for a string
accessing-internals
-
forgetUsedKeys
-
stop keeping a statistic on which kes are actually used
-
name: aKey
-
translate a string
-
packsClassName
-
-
packsClassName: aString
-
-
packsClassOrFileName
-
old: should no longer be used to access the filename; see packsFileName
-
packsClassOrFileName: aString
-
old: should no longer be used to access the filename; see packsFileName
-
packsFileName
-
-
projectPack
-
-
projectPack: anotherResourcePack
-
-
rememberUsedKeys
-
start keeping a statistic on which kes are actually used
-
superPack
-
-
superPack: anotherResourcePack
-
file reading
-
fileReadFailed
-
return true, if the pack does not really contain
valid translations, since the fileRead failed.
However, all inherited translations are still available
through the receiver
-
nonexistingFileRead
-
asked to read definitions for a non-existing file.
Here, this is legal and ignored (typically using inherited resources).
However, subclasses (such as styleSheet) may flag it as an error.
-
processLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printError
-
process a single valid line (i.e. #ifdef & #include has already been processed)
-
readFromFile: fileName directory: dirName
-
read definitions from a file in a directory
-
readFromResourceStream: inStream in: dirName
-
read definitions from a stream. The dirName argument is required to
specify where #include files are searched for.
Return true, if the style sheet could be read without errors, false otherwise.
printing & storing
-
displayOn: aGCOrStream
-
what a kludge - Dolphin and Squeak mean: printOn: a stream;
normally, resources are found in files named after their classes sourcefile
For example, the FileBrowsers resources are found in 'FBrowser.rs'.
For the examples below, we process resources from a constant string;
this is NOT representative.
|stream res|
stream := ReadStream on:'
foo ''the translation for foo''
#if Language == #de
bar ''die deutsche uebersetzung von bar''
baz ''baz hat den Wert %1''
#endif
#if Language == #fr
bar ''bar en francaise''
baz ''%1, c''''est baz''
#endif
'.
res := ResourcePack new readFromResourceStream:stream in:nil.
Transcript showCR:'baz is translated to: ' , (res string:'baz' with:'1234').
Transcript showCR:'bar is translated to: ' , (res string:'bar').
Transcript showCR:'foo is translated to: ' , (res string:'foo').
Transcript showCR:'fooBar is translated to: ' , (res string:'fooBar').
| set the Language to french:
and repeat the above.
back to english:
back to german:
|