Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
resources plugin for griffon
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
griffon-app
scripts
src/main/griffon/resourcemanager
test
wrapper
.gitignore
ConfigSlurper.patch
LICENSE.txt
README.md
ResourcesGriffonAddon.groovy
ResourcesGriffonPlugin.groovy
application.properties
griffonw
griffonw.bat
release_notes.md

README.md

i18n and resource manager

Plugin page: http://artifacts.griffon-framework.org/plugin/resources

resources provides internationalization support in a groovy way.

This plugin provides an implementation of i18n-support, but goes beyond that, providing i18n for resources like images and so on aswell.

The used implementation can be configured with the configuration key i18n.provider.
The key for this provider is resources.

Usage

This implementation does support ExtendedMessageSource, so args may not be a Map.
Aswell this implementation does support GString-like replacements.
For more info's see the documentation of i18n-support.

MessageSource (ResourceManger) access

You can get hold of the MessageSource instance with messageSource, i18n, resourceManager or rm on artifacts and the app-instance. If i18n.provider = 'resources' all 4 properties point to the same instance. If e.g. i18n.provider = 'i18n-support' messageSource and i18n point to i18n-support's implementation but resourceManager and rm always point to this plugin's implementation (ResourceManager).

Example with i18n.provider = 'resources'

messages.properties:

key.static = This is just a text

Your Code:

assert 'This is just a text' == rm.key.static
assert 'This is just a text' == resourceManager.key.static
assert 'This is just a text' == i18n.key.static
assert 'This is just a text' == messageSource.key.static

Resource formats

ResourceManager supports two formats for defining messages/resources (resource-files): Properties- and ConfigSlurper-files.
Properties-files have to end with .properties while the extension of a ConfigSlurper-file can be defined by the configuration key resources.extension.

ConfigSlurper-based files cannot only define Strings but any Object.
If getMessage is called and the resolved result is a Closure, it will be called with the following arguments:

  • if three parameters are defined: call(args, defaultMessage, locale)
  • if two parameters are defined, call(args, defaultMessage)
  • if one parameter is defined, call(args)
  • if no parameters are defined, call()

Only if the resolved value or the result of a Closure is of type String, the argument-replacement of getMessage takes place.

Example

messages.properties:

key.static = This is just a text

messages.groovy:

key {
    static = 'This is just a text'
    object = Locale.GERMAN
    closure = { args, defaultMessage, locale ->
        if(!args.value)
            return defaultMessage
        return NumberFormat.getInstance(locale).format(args.value)
    }
    closure2 = { 'The key #key has the value #value'.toLowerCase() }
}

Your Code:

assert 'This is just a text' == rm.key.static
assert Locale.GERMAN == rm.key.object
assert rm.key.closure instanceof Closure
assert '---' == rm.getMessage('key.closure', [], '---', Locale.GERMAN)
assert '1.000,123' = rm.getMessage('key.closure', [value: 1000.123], '---', Locale.GERMAN)
assert 'the key X has the value 100' == rm.getMessage('key.closure2', [key: 'X', value: 100])

Resource customization

You can define customized Resource-files, for e.g. to specify plattform- or application-logic specific resources. Each resource-file can be related to only one custom-name, but the resolving mechanism can use multiple custom-names.

Examples

messages_windows.groovy
messages_windows_de_DE.groovy
messages_linux.groovy
messages_linux_de_DE.groovy
messages.groovy
messages_de_DE.groovy

rm.customSuffixes = ['windows', 'linux']

Resource-file naming

The naming of resource-files fo// URL where documentation can be found String documentation = 'http://griffon.codehaus.org/I18n+Support+Plugin' // URL where source can be found String source = 'https://github.com/griffon/griffon-i18n-support-plugin'llows the basic rules of ResourceBundle:
<bundlename>_<customname>_<language>_<country>_<variant>.<extension> while all but bundlename and extension is optional, along with it's underscores.
e.g.: messages_custom_de.properties

Class-based resources

ResourceManager can aswell fetch class-specific resources.
If a baseclass is set for a ResourceManager-instance, it tries to find a resource-file in the same package as the baseclass named like the baseclass+resourceSuffix.
This works for precompiled GroovyScripts aswell.
e.g.: baseclass = my.pkg.MyClass -> my.pkg.MyClassResources_custom_de.groovy or my.pkg.MyClassResources.properties

For all artifacts, the ResourceManager-instance you receive has already set the artifacts class as baseclass.

Resolving

The ResourceManager looks for resource-files in the classPath of the ClassLoader specified by resources.loader, that defaults to the app-instance's ClassLoader.
All .properties-files in griffon-app/i18n and griffon-app/resources will be coverted via native2ascii automatically.

The ResourceManager tries to find a key's value in the following order (top most has highest precedence): 1. . 2. .properties 3. . 4. .properties 5. . 6. .properties 7. . 8. .properties 9. . 10. .properties 11. . 12. .properties

If multiple bundlenames exist, 7. to 12. loop in the order of the bundlenames specified in i18n.basenames
If multiple customizations exist, 1. to 2. and 7. to 8. loop in the order of the customizations specified in resources.customSuffixes

Resource access

Additional to the i18n-support's getMessage function ResourceManager can be accessed with the following methods:

  • rm.getResource(String key, String defaultMessage = null, def args = null,) where args can be of the types Object[], Collection<?> and Map<String, ?>
    Similar to getMessage but delivers the pure Object. Only a String's argument-replacement takes place. Another difference is, that getResource only does not descend into child hirarchie.
  • The more groovy way to access is via getProperty and invokeMethod:
    rm.test.key is the same as getMessage('test.key') without calling a Closure result
    rm.test.key ?: 'abc' is the same as getMessage('test.key', 'abc') without calling a Closure result
    rm.test.key([1,2,3]) is the same as getMessage('test.key', [1,2,3]) without calling a Closure result
    rm.test.key([a:1, b:2]) ?: 'abc' is the same as getMessage('test.key', [a:1, b:2], 'abc') without calling a Closure result
  • To access a ResourceManager for a different locale, you can use
    rm[Locale.GERMANY] or rm['de_DE']
  • To access a ResourceManager for a baseclass, you can use
    rm[MyClass] or rm[instanceOfMyClass]

ResourceBuilder

In ConfigSlurper-based resource-files, all nodes from your UberBuilder can be accessed. This allows to specify i18nable codeparts or the like.
Additionally the ResourceBuild provided by this plugin helps defining often localized resources like images, fonts or colors. The following nodes exist (with examples):

  • url
    • url('url specification')
    • url(source: 'url specification')
    • url(url: 'url specification')
    • url(src: 'url specification')
  • uri
    • uri('url specification')
    • uri(source: 'url specification')
    • uri(url: 'url specification')
    • uri(src: 'url specification')
  • dimension
    • dimension('100, 200')
    • dimension([100, 200])
    • dimension(width: 100, height: 200)
  • insets
    • insets([10, 20, 30, 40])
    • insets('10, 20, 30, 40')
    • insets(t: 10, l: 20, b: 30, r: 40)
    • insets(top: 10, left: 20, bottom: 30, right: 40)
  • point
    • point('10, 20')
    • point([10, 20])
    • point(x: 10, y: 20)
  • locale
    • locale('de_DE_BW')
    • locale(locale: 'de_DE_BW')
  • rectangle
    • rectangle('10,20,30,40')
    • rectangle([10, 20, 30, 40])
    • rectangle(x: 10, y: 20, w: 30, h: 40)
    • rectangle(x: 10, y: 20, width: 30, height: 40)
    • rectangle(point: point(10, 20), dimension: dimension(30, 40))
  • color
    • color('#4488bbff') // RGBA
    • color('#4488bb') // RGB
    • color('#48b') // RGB CSS notation
    • color('#48bf') // RGBA CSS notation
    • color('WHITE') // java.awt.Color-names
    • color(0x44, 0x88, 0xBB, 0xFF) // RGBA
    • color(0x44, 0x88, 0xBB) // RGB
    • color(r:0x44, g:0x88, b:0xBB, a:0xFF)
    • color(red:0x44, green:0x88, blue:0xBB, alpha:0xFF)
  • font
    • font("Arial, 12")
    • font("'Times New Roman', 12, bold, italic, underline, strikethrough, [kerning: on, ligatures: on]")
    • font(['Times New Roman', 12, 'bold', 'italic', 'underline', 'strikethrough', [kerning: TextAttribute.KERNING_ON, ligatures: TextAttribute.LIGATURES_ON]])
    • font([FAMILY: 'Times New Roman', SIZE: 12, WEIGHT: TextAttribute.WEIGHT_BOLD, POSTURE: 'OBLIQUE'])
  • image
    • image('testImage.png')
    • image(url('testImage.png'))
    • image(uri('testImage.png'))
    • image(MyClass.getResource('testImage.png').newInputStream())
    • image(MyClass.getResource('testImage.png').getBytes())
    • image(new File('test/unit/resourcemanager/testImage.png'))
    • image(source: 'testImage.png')
    • ...
    • image(src: 'testImage.png')
    • ...
  • icon
    • icon('testImage.png')
    • icon(url('testImage.png'))
    • icon(uri('testImage.png')) f possible i
    • icon(MyClass.getResource('testImage.png').newInputStream())
    • icon(MyClass.getResource('testImage.png').getBytes())
    • icon(new File('test/unit/resourcemanager/testImage.png'))
    • icon(source: 'testImage.png')
    • ...
    • icon(src: 'testImage.png')
    • ...
  • gradientPaint
    • gradientPaint('10, 20, WHITE, 100, 200, #AA5500')
    • gradientPaint([10, 20, Color.WHITE, 100, 200, '#AA5500'])
    • gradientPaint(x1:10, y1:20, startColor:Color.WHITE, x2:100, y2:200, endColor:'#AA5500')
    • gradientPaint(x1:10, y1:20, color1:Color.WHITE, x2:100, y2:200, color2:'#AA5500')
    • gradientPaint(x1:10, y1:20, c1:Color.WHITE, x2:100, y2:200, c2:'#AA5500')
  • linearGradientPaint
    • linearGradientPaint("10, 20, 100, 200, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK]")
    • linearGradientPaint("10, 20, 100, 200, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK], REPEAT")
    • linearGradientPaint([10, 20, 100, 200, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK]])
    • linearGradientPaint([10, 20, 100, 200, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], CycleMethod.REPEAT])
    • linearGradientPaint(x1:10, y1:20, x2:100, y2:200, colors: [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], cycleMethod: CycleMethod.REPEAT)
    • linearGradientPaint(x1:10, y1:20, x2:100, y2:200, cols: [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], cycle: CycleMethod.REPEAT)
  • radialGradientPaint
    • radialGradientPaint("100, 200, 50, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK]")
    • radialGradientPaint("100, 200, 50, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK], REPEAT")
    • radialGradientPaint([100, 200, 50, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK]])
    • radialGradientPaint([100, 200, 50, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], CycleMethod.REPEAT])
    • radialGradientPaint(cx: 100, cy:200, r:50, cols:[0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], cycle:CycleMethod.REPEAT)
    • radialGradientPaint("100, 200, 10, 20, 50, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK]")
    • radialGradientPaint("100, 200, 10, 20, 50, [0.0: WHITE, 0.5: #AAAAAA, 1.0: BLACK], REPEAT")
    • radialGradientPaint([100, 200, 10, 20, 50, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK]])
    • radialGradientPaint([100, 200, 10, 20, 50, [0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], CycleMethod.REPEAT])
    • radialGradientPaint(cx: 100, cy:200, fx:10, fy:20, radius:50, colors:[0.0: Color.WHITE, 0.5: '#AAAAAA', 1.0: Color.BLACK], cycleMethod:CycleMethod.REPEAT)
  • texturePaint
    • texturePaint('testImage.png', 10, 20, 50, 100")
    • texturePaint(image('testImage.png'), [10, 20, 50, 100]])
    • texturePaint(['testImage.png', new Rectangle(10, 20, 50, 100)])
    • texturePaint([image('testImage.png'), rectangle(10, 20, 50, 100)])
    • texturePaint(image: image('testImage.png'), anchor: rectangle(10, 20, 50, 100))
    • texturePaint(source: image('testImage.png'), rectangle: rectangle(10, 20, 50, 100))
    • texturePaint(src: this.getClass().getResource('testImage.png'), rect: rectangle(10, 20, 50, 100))

Injection

ResourceManager provides the ability to inject data into any Object via getters and setter.
rm.inject(Object bean, String prefix = 'injections') takes all keys that reside in the key defined by prefix and iterates over its children. If the bean has a setter to set the property with the name of the childrens key, it sets the value.
It tries to descend into the beans hirarchy to set subkeys.
If the bean is a java.awt.Component, it tries to identify subcomponents by its name property.

It tries to convert the value to the right type before setting it by using java.beans.PropertyEditor's.
By default, ResourceManager supports PropertyEditors for all types specified under ResourceBuilder (with the same Parameters) but custom implementations can be registered via ResourceManager.registerEditor(Class cls, PropertyEditor editor).

Example:

// Bean-Class
class A {
    Closure property1
    int property2
    B property3 = new B()
    URL url
}

class B {
    String sub1
}

// ConfigSlurper-based resource-file
injections {
    property1 = { 'Test' }
    property2 = '2'
    property3.sub1 = 'Value3'
    url = 'http://www.google.de'
}

// Call of inject e.g. in a Controller
def a = new A()
rm.inject(a)
assert a.property1 instanceof Closure
assert a.property2 == 2
assert a.property3.sub1 == 'Value3'
assert a.url == new URL(http://www.google.de)

Configuration

i18n.basenames = ['messages']                 // Bundlename for resource-files
resources.customSuffixes = []                 // Bundlesuffixes for customizing files
resources.resourceSuffix = 'Resources'        // Suffix to the classname for classspecific resources
resources.locale = Locale.default             // The ResourceManagers default locale
resources.loader = app.getClass().classLoader // The ClassLoader to load the resource-files from
resources.extension = 'groovy'                // Extension for ConfigSlurper-based resource-files
Something went wrong with that request. Please try again.