Skip to content

Commit

Permalink
Migrate to Grails 3.x
Browse files Browse the repository at this point in the history
Migrate the plugin to Grails 3.x and usage by Gradle.
  • Loading branch information
dellermann committed Mar 18, 2016
1 parent ac9ba06 commit e755a39
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 66 deletions.
96 changes: 67 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
I18n asset-pipeline plugin
==========================
# I18n asset-pipeline plugin

The Grails plugin `i18n-asset-pipeline` is an asset-pipeline plugin that
generates a JavaScript file with localized texts which can be used for
Expand All @@ -8,21 +7,42 @@ client-side i18n.
For more information on how to use asset-pipeline, visit
[asset-pipeline project page][asset-pipeline].

Version information
-------------------
## Version information

Because `asset-pipeline` 2.0.0 introduced a new API and isn't backward
Because `asset-pipeline` 2.x and 3.x introduced new APIs and aren't backward
compatible, you must use the following versions of this plugin:

* for `asset-pipeline` up to version 1.9.9 use version 0.9.x of this plugin
* for `asset-pipeline` version 2.0.0 or higher use version 1.0.0 or higher of
this plugin
i18n-asset-pipeline version | required for
----------------------------|--------------
0.x | `asset-pipeline` up to version 1.9.9
1.x | `asset-pipeline` version 2.0.0 or higher
2.x | Grails 3.x

Usage
-----
## Installation

`i18n-asset-pipeline` uses special files in your standard
`grails-app/assets/javascripts` folder with extension '.i18n'. The names of
To use this plugin you have to add the following code to your `build.gradle`:

```groovy
buildscript {
dependencies {
classpath 'org.amcworld.plugins:i18n-asset-pipeline:2.0.0'
}
}
dependencies {
runtime 'org.grails.plugins:i18n-asset-pipeline:2.0.0'
}
```

The first dependency declaration is needed to precompile your assets (e. g.
when building a WAR file). The second one provides the necessary
`<asset:i18n>` tag and compiles the assets on the fly (e. g. in development)
mode.

## Usage

`i18n-asset-pipeline` uses special files in your asset folders (we recommend
`grails-app/assets/i18n`) with extension '.i18n'. The names of
these files must contain a language specification separated by underscore, e.
g. `messages_de.i18n` or `messages_en_UK.i18n`. Files without a language
specification (e. g. `messages.i18n`) are files for the default locale. These
Expand All @@ -35,8 +55,7 @@ which can be called to obtain the localized message by a given code, e. g.:
$(".btn").text($L("default.btn.ok"));
```

I18n file syntax
----------------
## I18n file syntax

Each i18n file must be defined according to the following rules:

Expand All @@ -49,15 +68,15 @@ Each i18n file must be defined according to the following rules:
import statements, even circular ones. You may omit file extension `.i18n`
in *`url`*.
* All other lines are treated as messsage codes which are translated to the
required language. Comments after message codes are not allowed.
required language.
* Comments after import statements and message codes are not allowed.

Each i18n file may contain asset-pipeline `require` statements to load other
assets such as JavaScript files. **ATTENTION!** Don't use `require` to load
other i18n files because they will not be processed correctly. Use the
`@import` declaration instead.

Typical file structure
----------------------
## Typical file structure

Typically, you have one i18n file for each language in the application. Given,
you have the following message resources in `grails-app/i18n`:
Expand All @@ -68,7 +87,7 @@ you have the following message resources in `grails-app/i18n`:
* `messages_es.properties`
* `messages_fr.properties`

Then, you should have the same set of files in `grails-app/assets/javascripts`:
Then, you should have the same set of files in e. g. `grails-app/assets/i18n`:

* `messages.i18n`
* `messages_de.i18n`
Expand All @@ -77,7 +96,8 @@ Then, you should have the same set of files in `grails-app/assets/javascripts`:
* `messages_fr.i18n`

Normally, you would have to declare the same set of message codes in each file.
To DRY, add a file `_messages.i18n` to `grails-app/assets/javascripts`:
To DRY, add a file `_messages.i18n` to `grails-app/assets/i18n` (the
leading underscore prevents the i18n file to be compiled itself):

```
#
Expand All @@ -92,7 +112,17 @@ contact.foo.bar
```

Then, you can import this file in all other files, e. g. in `messages_de.i18n`:
Then, you can import this file in all other files, e. g.:

```
#
# messages.i18n
# Client-side i18n, English messages.
#
@import _messages
```

```
#
Expand All @@ -104,14 +134,23 @@ Then, you can import this file in all other files, e. g. in `messages_de.i18n`:
```

Including localized assets
--------------------------
```
#
# messages_es.i18n
# Client-side i18n, Spanish messages.
#
@import _messages
```

## Including localized assets

In order to include a localized asset you can either use an asset-pipeline
`require` directive or the tag `<asset:i18n>`. The tag supports the following
attributes:

* `locale`. Either a string or a `java.util.Locale` object reprenting the
* `locale`. Either a string or a `java.util.Locale` object representing the
locale that should be loaded. This attribute is mandatory.
* `name`. A string indicating the base name of the i18n files to load
(defaults to `messages`).
Expand All @@ -126,17 +165,16 @@ Examples:
<asset:i18n name="texts" locale="${locale}" />
```

Author
------
## Author

This plugin was written by [Daniel Ellermann](mailto:d.ellermann@amc-world.de).
This plugin was written by [Daniel Ellermann](mailto:d.ellermann@amc-world.de)
([AMC World Technologies GmbH][amc-world]).

License
-------
## License

This plugin was published under the
[Apache License, Version 2.0][apache-license].

[amc-world]: http://www.amc-world.de
[apache-license]: http://www.apache.org/licenses/LICENSE-2.0
[asset-pipeline]: http://www.github.com/bertramdev/asset-pipeline

24 changes: 13 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ buildscript {
}
}

version '3.0.0'
group 'asset.pipeline.i18n'
version '2.0.0'
group 'org.grails.plugins'

apply plugin: 'eclipse'
apply plugin: 'idea'
Expand Down Expand Up @@ -84,16 +84,18 @@ groovydoc {
}

grailsPublish {
// TODO: Provide values here
user = 'user'
key = 'key'
githubSlug = 'foo/bar'
user = project.hasProperty('bintrayUser') \
? project.property('bintrayUser')
: System.getenv('BINTRAY_USER')
key = project.hasProperty('bintrayApiKey') \
? project.property('bintrayApiKey')
: System.getenv('BINTRAY_API_KEY')
userOrg = 'amc-world'
githubSlug = 'dellermann/i18n-asset-pipeline'
license {
name = 'Apache-2.0'
}
title = 'My Plugin'
desc = 'Full plugin description'
developers = [johndoe:'John Doe']
portalUser = ''
portalPassword = ''
title = 'i18n-asset-pipeline'
desc = 'asset-pipeline plugin to use localized messages in JavaScript.'
developers = [dellermann: 'Daniel Ellermann']
}
1 change: 0 additions & 1 deletion grails-app/conf/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ info:
version: '@info.app.version@'
grailsVersion: '@info.app.grailsVersion@'
spring:

groovy:
template:
check-template-location: false
Expand Down
2 changes: 2 additions & 0 deletions src/main/groovy/asset/pipeline/i18n/I18nAssetFile.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import asset.pipeline.AssetCompiler
import asset.pipeline.AssetHelper
import asset.pipeline.CacheManager
import asset.pipeline.Processor
import groovy.transform.CompileStatic
import java.util.regex.Pattern


Expand All @@ -35,6 +36,7 @@ import java.util.regex.Pattern
* @author David Estes
* @version 3.0
*/
@CompileStatic
class I18nAssetFile extends AbstractAssetFile {

//-- Class fields ---------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ class I18nAssetPipelineGrailsPlugin extends Plugin {
url: 'https://github.com/dellermann/i18n-asset-pipeline/issues'
]
def scm = [url: 'https://github.com/dellermann/i18n-asset-pipeline']
def pluginExcludes = [
'grails-app/views/error.gsp'
]


//-- Public methods -------------------------
Expand Down
43 changes: 29 additions & 14 deletions src/main/groovy/asset/pipeline/i18n/I18nProcessor.groovy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* I18nProcessor.groovy
*
* Copyright (c) 2014-2015, Daniel Ellermann
* Copyright (c) 2014-2016, Daniel Ellermann
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -54,8 +54,9 @@ import org.springframework.core.io.ResourceLoader
*
* @author Daniel Ellermann
* @author David Estes
* @version 1.0
* @version 3.0
*/
@CompileStatic
class I18nProcessor extends AbstractProcessor {

//-- Constants ------------------------------
Expand All @@ -64,7 +65,7 @@ class I18nProcessor extends AbstractProcessor {
protected static final String XML_SUFFIX = '.xml'


//-- Instance variables ---------------------
//-- Fields ---------------------------------

ResourceLoader resourceLoader = new DefaultResourceLoader()

Expand All @@ -85,11 +86,9 @@ class I18nProcessor extends AbstractProcessor {
//-- Public methods -------------------------

@Override
@CompileStatic
String process(String inputText, AssetFile assetFile) {
AssetFile f = (AssetFile) assetFile
Matcher m = f.name =~ /._(\w+)\.i18n$/
StringBuilder buf = new StringBuilder('grails-app/i18n/messages')
Matcher m = assetFile.name =~ /._(\w+)\.i18n$/
StringBuilder buf = new StringBuilder('messages')
if (m) buf << '_' << m.group(1)
Properties props = loadMessages(buf.toString())

Expand All @@ -114,8 +113,7 @@ class I18nProcessor extends AbstractProcessor {
* @param messages the given messages
* @return the compiled JavaScript code
*/
@CompileStatic
protected String compileJavaScript(Map<String, String> messages) {
private String compileJavaScript(Map<String, String> messages) {
StringBuilder buf = new StringBuilder('''(function (win) {
var messages = {
''')
Expand Down Expand Up @@ -149,8 +147,7 @@ class I18nProcessor extends AbstractProcessor {
* @throws FileNotFoundException if no resource with the required
* localized messages exists
*/
@CompileStatic
protected Properties loadMessages(String fileName) {
private Properties loadMessages(String fileName) {
Resource res = locateResource(fileName)
Properties props = new Properties()
props.load res.inputStream
Expand All @@ -159,20 +156,38 @@ class I18nProcessor extends AbstractProcessor {
}

/**
* Locates the resource containing the localized messages.
* Locates the resource containing the localized messages. The method looks
* in the following places:
* <ul>
* <li>in classpath with extension {@code .properties}</li>
* <li>in classpath with extension {@code .xml}</li>
* <li>in file system in folder {@code grails-app/i18n} with extension
* {@code .properties}</li>
* <li>in file system in folder {@code grails-app/i18n} with extension
* {@code .xml}</li>
* </ul>
*
* @param fileName the given base file name
* @return the resource containing the messages
* @throws FileNotFoundException if no resource with the required
* localized messages exists
*/
@CompileStatic
protected Resource locateResource(String fileName) {
private Resource locateResource(String fileName) {
Resource resource =
resourceLoader.getResource(fileName + PROPERTIES_SUFFIX)
if (!resource.exists()) {
resource = resourceLoader.getResource(fileName + XML_SUFFIX)
}
if (!resource.exists()) {
resource = resourceLoader.getResource(
"file:grails-app/i18n/${fileName}${PROPERTIES_SUFFIX}"
)
}
if (!resource.exists()) {
resource = resourceLoader.getResource(
"file:grails-app/i18n/${fileName}${XML_SUFFIX}"
)
}
if (!resource.exists()) {
throw new FileNotFoundException(
"Cannot find i18n messages file ${fileName}."
Expand Down
Loading

0 comments on commit e755a39

Please sign in to comment.