eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'ResourcePack':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: ResourcePack


Inheritance:

   Object
   |
   +--Collection
      |
      +--Set
         |
         +--Dictionary
            |
            +--ResourcePack
               |
               +--ViewStyle

Package:
stx:libview
Category:
Interface-Internationalization
Version:
rev: 1.310 date: 2024/04/09 12:22:07
user: stefan
file: ResourcePack.st directory: libview
module: stx stc-classLibrary: libview

Description:


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).

In addition, every resourcePack has references to one or multiple so called 'superPacks',
which are typically shared among all classees within a package (eg. libwidg, libtool, app-package),
and which themself usually have superpacks.

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
application's 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.

Tracking Unresolved Translations:
    set:
        LanguagesMonitoredForMissing := { 'fr-fr' }
    then run your app,
    and look into
        ListOfMissingTranslations at:'fr-fr'
    to get a list of all missing xlations for french.

copyright

COPYRIGHT (c) 1993 by Claus Gittinger All Rights Reserved This software is furnished under a license and may be used only in accordance with the terms of that license and with the inclusion of the above copyright notice. This software may not be provided or otherwise made available to, or used by, any other person. No title to or ownership of the software is hereby transferred.

Class protocol:

accessing
o  translationHook

o  translationHook: aBlock
if set, aBlock gets a chance to translate a string,
before the regular translation takes place

initialization
o  flushCachedResourcePacks
forget all cached resources - needed after a style change

Usage example(s):

     ResourcePack flushCachedResourcePacks

o  initialize
ResourcePack initialize
ResourcePack flushCachedResourcePacks.
ApplicationModel flushAllClassResources.
SimpleView flushAllClassResources.

SimpleView classResources

instance creation
o  addAdditionalSuperPacksForPackage: packageID to: aResourcePack

o  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

o  for: aClass cached: cached
get the full resource definitions for aClass (i.e. with super packs).
Also optionally leave the resulting pack in the cache for faster access next time.

Usage example(s):

pack superPack:(self for:(aClass superclass) cached:cached).

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

o  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'
     ResourcePack forPackage:'stx:libView'

o  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.
Look first for a file named 'resources.rs',
then for '<lang>_<territory>.rs'
then for '<lang>.rs'

Usage example(s):

     ResourcePack forPackage:'stx:libbasic' cached:false
     ResourcePack forPackage:'exept:expecco/plugin/windowsAutomation2' cached:false

o  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 flushCachedResourcePacks.

     ResourcePack
	forPackage:'stx:libbasic'
	resourceFileName:'resources.rs'
	cached:false

o  forPackage: package resourceFileName: resourceFileName cached: cached defaultIfAbsent: defaultIfAbsentBoolean
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 flushCachedResourcePacks.

     ResourcePack
        forPackage:'stx:libbasic'
        resourceFileName:'resources.rs'
        cached:false

o  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

o  fromFile: aFileName directory: dirName
get the resource definitions from a file in a directory.

o  fromFile: aFileName directory: dirName cached: cached
get the resource definitions from a file in a directory.
Uncached low-level entry.

o  newDefaultResourcePackForPackage: package

private
o  addToCache: aPack
throw away oldest

o  searchCacheFor: aPackageName
found it, bring to front for LRU

Usage example(s):

     ResourcePack searchCacheFor:'stx:libview'

statistics
o  autoUpdatingMissingTranslations: aBoolean
enable/disable autoupdate of missing translations

Usage example(s):

     LanguagesMonitoredForMissing := nil.
     self autoUpdatingMissingTranslations:false

     ListOfMissingTranslations := nil.
     LanguagesMonitoredForMissing := {Smalltalk language . Smalltalk languageAndTerritory}.
     self autoUpdatingMissingTranslations:true

o  clearMissingTranslations
forget already collected missing translations

Usage example(s):

     self clearMissingTranslations

o  rememberMissingTranslation: aKey language: langKeyOrNil cache: cache inFile: resourceFile
remember that some translation is missing,
and optionally put it into an autoTranslator's task queue
(who will ask google and write it to a separate new resource file)

o  rememberMissingTranslation: aKey language: langKeyOrNil cache: cache inFile: resourceFile resourcePack: resourcePackOrNil
remember that some translation is missing,
and optionally put it into an autoTranslator's task queue
(who will ask google and write it to a separate new resource file)

utilities
o  defineResourceFor: aKey package: package
a developer utility:
ask for a resource string and add it to package's resources.
Called via CTRL-menu of labels or CTRL-SHIFT while a tooltip is shown

o  extractEncodingFromLine: lineString

o  processResourceLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printErrorActionOrNil for: aResourcePack
process a single valid line (i.e. #ifdef & #include has already been processed)

o  processResourceLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printErrorActionOrNil for: aResourcePack keepUselessTranslations: keepUselessTranslations
process a single valid line (i.e. #ifdef & #include has already been processed)

Usage example(s):

     |p|
     p := ResourcePack new.
     self processResourceLine:'''foo'' ''xlation_of_foo''' encoding:nil file:'dummy.rc' printErrorWith:[:err | Logger warn:err] for:p keepUselessTranslations:false.
     p inspect.

o  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.

o  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:   '

o  shortenedKeyFor: aKey
compute the minimal key, which could resolve the given key, or nil if there is no xlation.
Although this method is not used/needed here, it is provided for
the internationalLanguageEditor's checker, in order to prevent having the
knowledge about this optimization algorithm at least only in one class,
and not spread across.
The 'intelligence' here MUST follow the one in the localAt: method;
so please update here whenever the code there is changed.

Usage example(s):

     'abcde' findFirst:[:ch | 'bcd' includes:ch]
     'abcde' indexOfAny:'bcd'

     self shortenedKeyFor:'ab && cd'
     self shortenedKeyFor:'abc'
     self shortenedKeyFor:'   abc    '
     self shortenedKeyFor:'(abc)'
     self shortenedKeyFor:'abc...'
     self shortenedKeyFor:'(abc...)'
     self shortenedKeyFor:'abc:*'


Instance protocol:

accessing
o  language

o  language: aLanguageString
Modified (format): / 24-03-2021 / 22:51:27 / cg

o  resourcePackContainingKey: aKey
returns the resolving resourcePack or nil.

Usage example(s):

     Workspace classResources resourcePackContainingKey:'Cancel'
     WorkspaceApplication classResources resourcePackContainingKey:'Cancel'

accessing-internals
o  addSuperPack: anotherResourcePack

o  flushCache

o  name: aKey
translate a string

o  packsFileName

o  packsPackage

o  packsPackage: aPackage

o  projectPack

o  superPacks

enumeration
o  superPackHierarchyDo: aBlock
evaluate aBlock for all of the searched super packs;
that is my project's pack and my superpack(s)

o  whichPackIncludesKey: aKey
for debugging: return the pack (alogn the super-pack chain), which
has a translation for a string

file reading
o  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

o  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.

o  processLine: lineString encoding: encodingSymbolOrEncoder file: fileName printErrorWith: printErrorActionOrNil
process a single valid line (i.e. #ifdef & #include has already been processed)

o  readFromFile: fileName directory: dirName
read definitions from a file in a directory

o  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.

inspecting
o  inspector2TabFileEditor
( an extension from the stx:libtool package )
provide an additional tab, which presents the raw resource file

o  inspector2TabTranslationView
( an extension from the stx:libtool package )
provide an additional tab, which presents the translations

printing & storing
o  displayOn: aGCOrStream
what a kludge - Dolphin and Squeak mean: printOn: a stream;

o  printOn: aStream
redefined to not print the elements

statistics
o  forgetUsedKeys
stop keeping a statistic on which keys are actually used

o  rememberUsedKeys
start keeping a statistic on which keys are actually used

o  usedKeys
get keys which were actually used.
(must call #rememberUsedKeys before)

translating
o  array: anArray
translate a collection of strings

Usage example(s):

     Launcher classResources array:#('file' 'classes') => #('Datei' 'Klassen')

o  at: aKey
translate a string

Usage example(s):

     Launcher classResources at:'file'  => 'Datei'

o  at: aKey default: default
translate a string

Usage example(s):

     Launcher classResources at:'fixlexxx' default:'foo' => 'foo'

o  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) **

o  string: anUntranslatedString
translate (retrieve) a string - if not present, return s

Usage example(s):

     NewLauncher classResources
        string:'LICENCEFILE'

o  string: s default: defaultString
translate (retrieve) a string - if not present, return defaultString

Usage example(s):

     NewLauncher classResources
	string:'fooBar' default:'Hello world'

o  string: s default: defaultString with: arg
translate and expand arg

Usage example(s):

     NewLauncher classResources
	string:'%1 fooBar' default:'Hello %1' with:'foo'

o  string: s default: defaultString with: arg1 with: arg2
translate and expand args

o  string: s default: defaultString with: arg1 with: arg2 with: arg3
translate and expand args

o  string: s default: defaultString with: arg1 with: arg2 with: arg3 with: arg4
translate and expand args

o  string: s default: defaultString withArgs: argArray
translate and expand args

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  string: s default: defaultString withArguments: argArray
translate and expand args

o  string: s with: arg
translate and expand arg

o  string: s with: arg1 with: arg2
translate and expand args

o  string: s with: arg1 with: arg2 with: arg3
translate and expand args

o  string: s with: arg1 with: arg2 with: arg3 with: arg4
translate and expand args

o  string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5
translate and expand args

o  string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6
translate and expand args

o  string: s with: arg1 with: arg2 with: arg3 with: arg4 with: arg5 with: arg6 with: arg7
translate and expand args

o  string: s withArgs: argArray
translate and expand args - allow text as arguments

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  string: s withArguments: argArray
translate and expand args - allow text as arguments

o  stringWithCRs: s
translate (retrieve) a string - if not present, return s.
replaces \'s with CRs

Usage example(s):

     NewLauncher classResources
        stringWithCRs:'LICENCEFILE'

o  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.

o  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.

o  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.

o  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.

o  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.

o  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.

o  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.

o  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.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  stringWithCRs: s withArguments: 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.
If there is no xlation for the whole string,
try xlations for individual line.

Usage example(s):

     stx_libview classResources string:'Copy\Paste'
     stx_libview classResources stringWithCRs:'Copy\Paste'
     stx_libtool classResources stringWithCRs:'copy\paste'

translating - basic
o  at: aKey ifAbsent: defaultValue
translate a string;
search here, in my project's pack and in my superpack(s)

Usage example(s):

     Launcher classResources at:'fixlexxx' ifAbsent:'foo' => 'foo'

o  localAt: aKey
translate a string from the local resourcePack; return nil if there is no xlation.
Some special 'intelligence' has been added:
if no value for aKey is found,
lookup aKey with first character caseChanged and change the result's first characters case.
lookup aKey all lowercase and change the result's 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 any of '\*<« ', then lookup aKey without the prefix and prepend prefix to the result.
or aKey ends with ''%1'' , then lookup aKey without ''%1'' and append ''%1'' to the result.
or aKey ends with any of '>»\:=.,?!* ', then lookup aKey without the suffix and append suffix 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 '&'.
or aKey includes c'\n', then lookup aKey with c'\n' replaced by '\' and replace '\' by c'\n' in the result if found'.

This means, that only a single translation is required to provide local translations for
things like
'search'
'search:'
'search...'

ATTENTION: the 'intelligence' in minimizedKey: MUST follow the one here;
so please update there whenever the code here is changed.


Private classes:

    AutoUpdater
    DuplicateTranslation
    TranslationConflict

Examples:


normally, resources are found in files named after their classes sourcefile, or in 'resources.rs' under the package's 'resources' folder. For example, the FileBrowsers resources might be found in 'FBrowser.rs', or in the package's summary resources 'libwidg/resources/resources.rs'. Notice: in previous older ST/X versions, there where individual resource files as per class; this lead to many duplicate entries and many small resource files. Therefore, these have been concentrated into one resource file as per package, which typically dispatches to a specific resource files named <lang>.rs. See the libwidg/resources or libview/resources folders as examples. 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:
    Language := #fr
and repeat the above. back to english:
    Language := #en
back to german:
    Language := #de


ST/X 7.7.0.0; WebServer 1.702 at 20f6060372b9.unknown:8081; Tue, 03 Dec 2024 17:22:06 GMT