You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/02/18 15:31:33 UTC
[23/29] isis git commit: ISIS-903: allow translations to be read from
externalized config location.
ISIS-903: allow translations to be read from externalized config location.
In addition, pick up value of deploymentType by reading IsisContext.getDeploymentType() rather than the hacky way of reading config; this should work in all situations (org.apache.isis.WebServer, integtetss, tomcat etc). This necessitates moving the TranslatioServiePo implementation (and supporting classes) from core/metamodel to core/runtime.
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/362a5bfc
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/362a5bfc
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/362a5bfc
Branch: refs/heads/master
Commit: 362a5bfcc7f5a8f45c15c9f5c242a3b4618ce3e5
Parents: 2cf70db
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Wed Feb 18 11:32:17 2015 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Wed Feb 18 14:07:54 2015 +0000
----------------------------------------------------------------------
.../services/TranslationsResolverWicket.java | 28 ++-
.../TranslationsResolverWicketTest.java | 37 ++++
.../all/i18n/TranslationFacetFactory.java | 3 +-
.../core/metamodel/services/i18n/po/Block.java | 114 ----------
.../services/i18n/po/ContextAndMsgId.java | 90 --------
.../metamodel/services/i18n/po/PoAbstract.java | 43 ----
.../metamodel/services/i18n/po/PoReader.java | 190 -----------------
.../metamodel/services/i18n/po/PoWriter.java | 141 ------------
.../services/i18n/po/TranslationServicePo.java | 138 ------------
.../i18n/po/TranslationServicePoMenu.java | 94 --------
.../services/i18n/po/PoReaderTest.java | 212 -------------------
.../core/runtime/services/i18n/po/Block.java | 114 ++++++++++
.../services/i18n/po/ContextAndMsgId.java | 90 ++++++++
.../runtime/services/i18n/po/PoAbstract.java | 43 ++++
.../core/runtime/services/i18n/po/PoReader.java | 190 +++++++++++++++++
.../core/runtime/services/i18n/po/PoWriter.java | 141 ++++++++++++
.../services/i18n/po/TranslationServicePo.java | 149 +++++++++++++
.../i18n/po/TranslationServicePoMenu.java | 94 ++++++++
.../core/webapp/IsisWebAppBootstrapper.java | 6 +-
.../runtime/services/i18n/po/PoReaderTest.java | 212 +++++++++++++++++++
20 files changed, 1100 insertions(+), 1029 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicket.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicket.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicket.java
index 0a104b8..110854a 100644
--- a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicket.java
+++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicket.java
@@ -18,18 +18,22 @@
*/
package org.apache.isis.viewer.wicket.viewer.services;
+import java.io.File;
import java.io.IOException;
import java.net.URL;
+import java.nio.file.Path;
import java.util.List;
import javax.servlet.ServletContext;
import com.google.common.base.Charsets;
import com.google.common.io.CharSource;
+import com.google.common.io.Files;
import com.google.common.io.Resources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.isis.applib.annotation.DomainService;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.services.i18n.TranslationsResolver;
+import org.apache.isis.core.webapp.WebAppConstants;
import org.apache.isis.viewer.wicket.viewer.IsisWicketApplication;
@@ -44,15 +48,33 @@ public class TranslationsResolverWicket implements TranslationsResolver {
@Override
@Programmatic
public List<String> readLines(final String file) {
+ final ServletContext servletContext = getServletContext();
+
+ final String configLocation = servletContext.getInitParameter(WebAppConstants.CONFIG_DIR_PARAM);
try {
- final ServletContext servletContext = getIsisWicketApplication().getServletContext();
- final URL url = servletContext.getResource("/WEB-INF/" + file);
- return readLines(url);
+ if(configLocation != null) {
+ LOG.info( "Reading translations relative to config override location: " + configLocation );
+ return Files.readLines(newFile(configLocation, file), Charsets.UTF_8);
+ } else {
+ final URL url = servletContext.getResource("/WEB-INF/" + file);
+ return readLines(url);
+ }
} catch (final RuntimeException | IOException ignored) {
return null;
}
}
+ static File newFile(final String dir, final String file) {
+ final File base = new File(dir);
+ final Path path = base.toPath();
+ final Path resolve = path.resolve(file);
+ return resolve.toFile();
+ }
+
+ protected ServletContext getServletContext() {
+ return getIsisWicketApplication().getServletContext();
+ }
+
private static List<String> readLines(final URL url) throws IOException {
if(url == null) {
return null;
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/component/viewer/wicket/impl/src/test/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicketTest.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/test/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicketTest.java b/component/viewer/wicket/impl/src/test/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicketTest.java
new file mode 100644
index 0000000..2190a62
--- /dev/null
+++ b/component/viewer/wicket/impl/src/test/java/org/apache/isis/viewer/wicket/viewer/services/TranslationsResolverWicketTest.java
@@ -0,0 +1,37 @@
+package org.apache.isis.viewer.wicket.viewer.services;
+
+import java.io.File;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assume.assumeThat;
+
+public class TranslationsResolverWicketTest {
+
+ public static class NewFile extends TranslationsResolverWicketTest {
+
+ @Before
+ public void setUp() throws Exception {
+ assumeThat(System.getProperty("os.name").startsWith("Windows"), is(true));
+ }
+
+ @Test
+ public void simple() throws Exception {
+ final File file = TranslationsResolverWicket.newFile("c:/foo", "bar");
+ final String absolutePath = file.getAbsolutePath();
+ assertThat(absolutePath, is("c:\\foo\\bar"));
+ }
+
+ @Test
+ public void nestedChild() throws Exception {
+ final File file = TranslationsResolverWicket.newFile("c:/foo", "bar/baz");
+ final String absolutePath = file.getAbsolutePath();
+ assertThat(absolutePath, is("c:\\foo\\bar\\baz"));
+ }
+
+ }
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/TranslationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/TranslationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/TranslationFacetFactory.java
index 4200a95..bcadd2c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/TranslationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/TranslationFacetFactory.java
@@ -127,7 +127,8 @@ public class TranslationFacetFactory extends FacetFactoryAbstract implements Con
* Looks up from {@link org.apache.isis.core.metamodel.runtimecontext.ServicesInjector}.
*
* <p>
- * There is guaranteed to be an instance because {@link org.apache.isis.core.metamodel.services.i18n.po.TranslationServicePo} is annotated as a {@link org.apache.isis.applib.annotation.DomainService @DomainService}.
+ * There is guaranteed to be an instance because <code>TranslationServicePo</code> (in runtime) is annotated
+ * as a {@link org.apache.isis.applib.annotation.DomainService @DomainService}.
* </p>
*/
TranslationService lookupTranslationService() {
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/Block.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/Block.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/Block.java
deleted file mode 100644
index 43870d9..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/Block.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import com.google.common.collect.Lists;
-
-class Block {
-
- private enum State {
- CONTEXT("^#: (?<value>.+)$"),
- MSGID("^msgid \"(?<value>.+)\"$"),
- MSGID_PLURAL("^msgid_plural \"(?<value>.+)\"$"),
- MSGSTR("^msgstr \"(?<value>.+)\"$"),
- MSGSTR0("^msgstr\\[0\\] \"(?<value>.+)\"$"),
- MSGSTR1("^msgstr\\[1\\] \"(?<value>.+)\"$");
-
- private final Pattern pattern;
-
- private State(final String regex) {
- pattern = Pattern.compile(regex);
- }
- }
-
- State state = State.CONTEXT;
-
- List<String> contextList = Lists.newArrayList();
- String msgid = null;
- String msgid_plural = null;
- String msgstr = null; // either from msgstr or msgstr[0] if there is a plural
- String msgstr_plural = null; // from msgstr[1]
-
- Block parseLine(final String line, final Map<ContextAndMsgId, String> translationsByKey) {
- if (state == State.CONTEXT) {
- final Matcher contextMatcher = state.pattern.matcher(line);
- if (contextMatcher.matches()) {
- final String context = contextMatcher.group("value");
- contextList.add(context);
- return this;
- } else {
- state = State.MSGID;
- // fallthrough (there may not have been any more context)
- }
- }
-
- if (state == State.MSGID) {
- final Matcher msgidMatcher = state.pattern.matcher(line);
- if (msgidMatcher.matches()) {
- msgid = msgidMatcher.group("value");
- state = State.MSGID_PLURAL; // found, next time look for plurals
- } else {
- return new Block();
- }
- return this;
- }
-
- if (state == State.MSGID_PLURAL) {
- final Matcher msgIdPluralMatcher = state.pattern.matcher(line);
- if (msgIdPluralMatcher.matches()) {
- msgid_plural = msgIdPluralMatcher.group("value");
- state = State.MSGSTR0; // next time look for msgstr[0]
- return this;
- } else {
- state = State.MSGSTR; // fall through (there may not have been any plural form)
- }
- }
-
- if (state == State.MSGSTR) {
- final Matcher msgStrMatcher = state.pattern.matcher(line);
- if (msgStrMatcher.matches()) {
- msgstr = msgStrMatcher.group("value");
- }
- append(translationsByKey);
- return new Block();
- }
-
- if (state == State.MSGSTR0) {
- final Matcher msgStr0Matcher = state.pattern.matcher(line);
- if (msgStr0Matcher.matches()) {
- msgstr = msgStr0Matcher.group("value");
- state = State.MSGSTR1; // next time, look for plural
- } else {
- append(translationsByKey);
- return new Block();
- }
- return this;
- }
-
- if (state == State.MSGSTR1) {
- final Matcher msgStr1Matcher = state.pattern.matcher(line);
- if (msgStr1Matcher.matches()) {
- msgstr_plural = msgStr1Matcher.group("value");
- }
- append(translationsByKey);
- return new Block();
- }
- return this;
- }
-
- void append(final Map<ContextAndMsgId, String> translationsByKey) {
- for (String context : contextList) {
- if(msgid != null && msgstr != null) {
- final ContextAndMsgId mc = new ContextAndMsgId(context, msgid, ContextAndMsgId.Type.REGULAR);
- translationsByKey.put(mc, msgstr);
- }
- if(msgid_plural != null && msgstr_plural != null) {
- final ContextAndMsgId mc = new ContextAndMsgId(context, msgid_plural, ContextAndMsgId.Type.PLURAL_ONLY);
- translationsByKey.put(mc, msgstr_plural);
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/ContextAndMsgId.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/ContextAndMsgId.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/ContextAndMsgId.java
deleted file mode 100644
index 2077c11..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/ContextAndMsgId.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-/**
- * The combination of a <tt>msgId</tt> and context (optionally null) that represents a key to a translatable resource.
- *
- * <p>
- * For example, with this <i>.pot</i> file:
- * </p>
- * <pre>
- * #: org.isisaddons.module.sessionlogger.dom.SessionLoggingServiceMenu#activeSessions()
- msgid: "Active Sessions"
-
- #: org.isisaddons.module.audit.dom.AuditingServiceMenu
- #: org.isisaddons.module.command.dom.CommandServiceMenu
- #: org.isisaddons.module.publishing.dom.PublishingServiceMenu
- msgid: "Activity"
-
- * </pre>
- *
- * <p>
- * the combination of <code>{org.isisaddons.module.sessionlogger.dom.SessionLoggingServiceMenu#activeSessions(), "Active Sessions"}</code> represents such a key, as does <code>{org.isisaddons.module.audit.dom.AuditingServiceMenu, "Activity"}</code>
- * </p>
- */
-public class ContextAndMsgId implements Comparable<ContextAndMsgId> {
-
- public enum Type {
- /**
- * The text to use when there is no plural form, or the text to use for singular pattern when there is also a plural form.
- */
- REGULAR,
- /**
- * The text to use for plural form.
- */
- PLURAL_ONLY
- }
-
- private final String context;
- private final String msgId;
- private final Type type;
-
- public ContextAndMsgId(final String context, final String msgId, final Type type) {
- this.context = context == null? "": context;
- this.msgId = msgId;
- this.type = type;
- }
-
- public String getMsgId() {
- return msgId;
- }
-
- public String getContext() {
- return context;
- }
-
- /**
- * Not part of equals/hashCode impl.
- */
- public Type getType() {
- return type;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- final ContextAndMsgId that = (ContextAndMsgId) o;
-
- if (context != null ? !context.equals(that.context) : that.context != null) return false;
- if (msgId != null ? !msgId.equals(that.msgId) : that.msgId != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = context != null ? context.hashCode() : 0;
- result = 31 * result + (msgId != null ? msgId.hashCode() : 0);
- return result;
- }
-
- @Override
- public int compareTo(final ContextAndMsgId o) {
- final int i = msgId.compareTo(o.msgId);
- if(i != 0) {
- return i;
- }
- return context.compareTo(o.context);
- }
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoAbstract.java
deleted file mode 100644
index e732d46..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoAbstract.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import org.apache.isis.applib.services.i18n.TranslationService;
-
-abstract class PoAbstract {
-
- protected final TranslationServicePo translationServicePo;
- private final TranslationService.Mode mode;
-
- PoAbstract(final TranslationServicePo translationServicePo, final TranslationService.Mode mode) {
- this.translationServicePo = translationServicePo;
- this.mode = mode;
- }
-
-
- abstract void shutdown();
-
- abstract String translate(final String context, final String msgId);
-
- abstract String translate(final String context, final String msgId, final String msgIdPlural, int num);
-
- public TranslationService.Mode getMode() {
- return mode;
- }
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoReader.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoReader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoReader.java
deleted file mode 100644
index e0bb2b7..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoReader.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.isis.applib.services.i18n.TranslationService;
-import org.apache.isis.applib.services.i18n.TranslationsResolver;
-
-class PoReader extends PoAbstract {
-
- public static final String LOCATION_BASE_URL = "isis.services.translation.po.locationBaseUrl";
- public static Logger LOG = LoggerFactory.getLogger(PoReader.class);
-
- private final Map<Locale, Map<ContextAndMsgId, String>> translationByKeyByLocale = Maps.newHashMap();
-
- /**
- * The basename of the translations file, hard-coded to <tt>translations</tt>.
- *
- * <p>
- * This means that the reader will search for <tt>translations_en-US.po</tt>, <tt>translations_en.po</tt>,
- * <tt>translations.po</tt>, according to the location that the provided {@link org.apache.isis.applib.services.i18n.TranslationsResolver} searches. For example, if using the Wicket implementation, then will search for these files
- * under <tt>/WEB-INF</tt> directory.
- * </p>
- */
- private final String basename = "translations";
-
- private List<String> fallback;
-
- public PoReader(final TranslationServicePo translationServicePo) {
- super(translationServicePo, TranslationService.Mode.READ);
- }
-
- //region > init, shutdown
- void init(final Map<String,String> config) {
- fallback = readUrl(basename + ".po");
- if(fallback == null) {
- LOG.warn("No fallback translations found");
- fallback = Collections.emptyList();
- }
- }
-
- @Override
- void shutdown() {
- }
- //endregion
-
- public String translate(final String context, final String msgId) {
- return translate(context, msgId, ContextAndMsgId.Type.REGULAR);
- }
-
- @Override
- String translate(final String context, final String msgId, final String msgIdPlural, final int num) {
-
- final String msgIdToUse;
- final ContextAndMsgId.Type type;
- if (num == 1) {
- msgIdToUse = msgId;
- type = ContextAndMsgId.Type.REGULAR;
- } else {
- msgIdToUse = msgIdPlural;
- type = ContextAndMsgId.Type.PLURAL_ONLY;
- }
-
- return translate(context, msgIdToUse, type);
- }
-
- private String translate(
- final String context, final String msgId, final ContextAndMsgId.Type type) {
-
- final Locale targetLocale;
- try {
- targetLocale = translationServicePo.getLocaleProvider().getLocale();
- } catch(final RuntimeException ex){
- LOG.warn("Failed to obtain locale, returning the original msgId");
- return msgId;
- }
-
- final Map<ContextAndMsgId, String> translationsByKey = readAndCacheTranslationsIfRequired(targetLocale);
-
- final ContextAndMsgId key = new ContextAndMsgId(context, msgId, type);
- final String translation = lookupTranslation(translationsByKey, key);
- if (!Strings.isNullOrEmpty(translation)) {
- return translation;
- }
-
- final ContextAndMsgId keyNoContext = new ContextAndMsgId("", msgId, type);
- final String translationNoContext = lookupTranslation(translationsByKey, keyNoContext);
- if (!Strings.isNullOrEmpty(translationNoContext)) {
- return translationNoContext;
- }
-
- LOG.warn("No translation found for: " + key);
- return msgId;
- }
-
- private String lookupTranslation(final Map<ContextAndMsgId, String> translationsByKey, final ContextAndMsgId key) {
- final String s = translationsByKey.get(key);
- return s != null? s.trim(): null;
- }
-
- private Map<ContextAndMsgId, String> readAndCacheTranslationsIfRequired(final Locale locale) {
- Map<ContextAndMsgId, String> translationsByKey = translationByKeyByLocale.get(locale);
- if(translationsByKey != null) {
- return translationsByKey;
- }
-
- translationsByKey = Maps.newHashMap();
- read(locale, translationsByKey);
- translationByKeyByLocale.put(locale, translationsByKey);
-
- return translationsByKey;
- }
-
-
- /**
- * @param locale - the .po file to load
- * @param translationsByKey - the translations to be populated
- */
- private void read(final Locale locale, final Map<ContextAndMsgId, String> translationsByKey) {
- final List<String> contents = readPo(locale);
-
- Block block = new Block();
- for (final String line : contents) {
- block = block.parseLine(line, translationsByKey);
- }
- }
-
- protected List<String> readPo(final Locale locale) {
- final List<String> lines = readPoElseNull(locale);
- if(lines != null) {
- return lines;
- }
- LOG.warn("Could not locate translations for locale: " + locale + ", using fallback");
- return fallback;
- }
-
- private List<String> readPoElseNull(final Locale locale) {
- final String country = locale.getCountry().toUpperCase(Locale.ROOT);
- final String language = locale.getLanguage().toLowerCase(Locale.ROOT);
-
- final List<String> candidates = Lists.newArrayList();
- if(!Strings.isNullOrEmpty(language)) {
- if(!Strings.isNullOrEmpty(country)) {
- candidates.add(basename + "_" + language + "-" + country+ ".po");
- }
- candidates.add(basename + "_" + language + ".po");
- }
-
- for (final String candidate : candidates) {
- final List<String> lines = readUrl(candidate);
- if(lines != null) {
- return lines;
- }
- }
- return null;
- }
-
- private List<String> readUrl(final String candidate) {
- final TranslationsResolver translationsResolver = translationServicePo.getTranslationsResolver();
- if(translationsResolver == null) {
- return null;
- }
- return translationsResolver.readLines(candidate);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoWriter.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoWriter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoWriter.java
deleted file mode 100644
index 6ff8117..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/PoWriter.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.SortedMap;
-import java.util.SortedSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.joda.time.LocalDateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.isis.applib.services.i18n.TranslationService;
-
-class PoWriter extends PoAbstract {
-
- public static Logger LOG = LoggerFactory.getLogger(PoWriter.class);
-
- private static class Block {
- private final String msgId;
- private final SortedSet<String> contexts = Sets.newTreeSet();
- private String msgIdPlural;
-
- private Block(final String msgId) {
- this.msgId = msgId;
- }
- }
-
- private final SortedMap<String, Block> blocksByMsgId = Maps.newTreeMap();
-
- public PoWriter(final TranslationServicePo translationServicePo) {
- super(translationServicePo, TranslationService.Mode.WRITE);
- }
-
- //region > shutdown
-
- @Override
- void shutdown() {
- final StringBuilder buf = new StringBuilder();
- buf.append("\n");
- buf.append("\n##############################################################################");
- buf.append("\n#");
- buf.append("\n# .pot file");
- buf.append("\n#");
- buf.append("\n# generated at: ").append(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
- buf.append("\n# generated by: ").append(TranslationServicePo.class.getSimpleName());
- buf.append("\n#");
- buf.append("\n# Translate this file to each required language and place in WEB-INF, eg:");
- buf.append("\n#");
- buf.append("\n# /WEB-INF/translations_en-US.po");
- buf.append("\n# /WEB-INF/translations_en.po");
- buf.append("\n# /WEB-INF/translations_fr-FR.po");
- buf.append("\n# /WEB-INF/translations_fr.po");
- buf.append("\n# /WEB-INF/translations.po");
- buf.append("\n#");
- buf.append("\n# If the app uses TranslatableString (eg for internationalized validation");
- buf.append("\n# messages), or if the app calls the TranslationService directly, then ensure");
- buf.append("\n# that all text to be translated has been captured by running a full");
- buf.append("\n# integration test suite that exercises all relevant behaviour");
- buf.append("\n#");
- buf.append("\n##############################################################################");
- buf.append("\n");
- buf.append("\n");
- buf.append(toPot());
- buf.append("\n");
- buf.append("\n");
- buf.append("\n##############################################################################");
- buf.append("\n# end of .pot file");
- buf.append("\n##############################################################################");
- buf.append("\n");
- LOG.info(buf.toString());
- }
- //endregion
-
-
- public String translate(final String context, final String msgId) {
-
- final Block block = blockFor(msgId);
- block.contexts.add(context);
-
- return msgId;
- }
-
- @Override
- String translate(final String context, final String msgId, final String msgIdPlural, final int num) {
-
- final Block block = blockFor(msgId);
- block.contexts.add(context);
- block.msgIdPlural = msgIdPlural;
-
- return null;
- }
-
- private Block blockFor(final String msgId) {
- Block block = blocksByMsgId.get(msgId);
- if(block == null) {
- block = new Block(msgId);
- blocksByMsgId.put(msgId, block);
- }
- return block;
- }
-
- /**
- * Not API
- */
- String toPot() {
- final StringBuilder buf = new StringBuilder();
- for (final String msgId : blocksByMsgId.keySet()) {
- final Block block = blocksByMsgId.get(msgId);
- for (final String context : block.contexts) {
- buf.append("#: ").append(context).append("\n");
- }
- buf.append("msgid \"").append(msgId).append("\"\n");
- if(block.msgIdPlural == null) {
- buf.append("msgstr \"\"\n");
- } else {
- buf.append("msgid_plural \"").append(block.msgIdPlural).append("\"\n");
- buf.append("msgstr[0] \"\"\n");
- buf.append("msgstr[1] \"\"\n");
- }
- buf.append("\n\n");
- }
- return buf.toString();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePo.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePo.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePo.java
deleted file mode 100644
index 5dda93b..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePo.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.Map;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.i18n.LocaleProvider;
-import org.apache.isis.applib.services.i18n.TranslationService;
-import org.apache.isis.applib.services.i18n.TranslationsResolver;
-
-@DomainService
-public class TranslationServicePo implements TranslationService {
-
- public static Logger LOG = LoggerFactory.getLogger(TranslationServicePo.class);
-
- public static final String KEY_DEPLOYMENT_TYPE = "isis.deploymentType";
- public static final String KEY_PO_MODE = "isis.services.translation.po.mode";
-
- private PoAbstract po;
-
- /**
- * Defaults to writer mode because the service won't have been init'd while the metamodel is bring instantiated,
- * and we want to ensure that we capture all requests for translation.
- */
- public TranslationServicePo() {
- po = new PoWriter(this);
- }
-
- //region > init, shutdown
-
- @Programmatic
- @PostConstruct
- public void init(final Map<String,String> config) {
-
- if(getLocaleProvider() == null || getTranslationsResolver() == null) {
- // remain in write mode
- return;
- }
-
- final String deploymentType = config.get(KEY_DEPLOYMENT_TYPE);
- boolean prototypeOrTest = deploymentType==null ||
- deploymentType.toLowerCase().contains("prototype") ||
- deploymentType.toLowerCase().contains("test") ;
-
- final String translationMode = config.get(KEY_PO_MODE);
- final boolean forceRead =
- translationMode != null &&
- ("read".equalsIgnoreCase(translationMode) ||
- "reader".equalsIgnoreCase(translationMode));
-
- if(prototypeOrTest && !forceRead) {
- // remain in write mode
- return;
- }
-
- // switch to read mode
- final PoReader poReader = new PoReader(this);
- poReader.init(config);
- po = poReader;
- }
-
- @Programmatic
- @PreDestroy
- public void shutdown() {
- po.shutdown();
- }
- //endregion
-
-
- @Override
- @Programmatic
- public String translate(final String context, final String text) {
- return po.translate(context, text);
- }
-
- @Override
- public String translate(final String context, final String singularText, final String pluralText, final int num) {
- return po.translate(context, singularText, pluralText, num);
- }
-
- @Override
- public Mode getMode() {
- return po.getMode();
- }
-
- /**
- * Not API
- */
- @Programmatic
- public String toPo() {
- if (!getMode().isWrite()) {
- throw new IllegalStateException("Not in write mode");
- }
- return ((PoWriter)po).toPot();
- }
-
- // //////////////////////////////////////
-
- @Inject
- private
- TranslationsResolver translationsResolver;
-
- @Programmatic
- TranslationsResolver getTranslationsResolver() {
- return translationsResolver;
- }
-
- @Inject
- private
- LocaleProvider localeProvider;
-
- @Programmatic
- LocaleProvider getLocaleProvider() {
- return localeProvider;
- }
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePoMenu.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePoMenu.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePoMenu.java
deleted file mode 100644
index c39f98a..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/TranslationServicePoMenu.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.List;
-import javax.inject.Inject;
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.IsisApplibModule;
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.DomainServiceLayout;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.annotation.ParameterLayout;
-import org.apache.isis.applib.annotation.RestrictTo;
-import org.apache.isis.applib.annotation.SemanticsOf;
-import org.apache.isis.applib.value.Clob;
-
-@DomainService(
- nature = NatureOfService.VIEW_MENU_ONLY
-)
-@DomainServiceLayout(
- menuBar = DomainServiceLayout.MenuBar.SECONDARY,
- named = "Prototyping"
-)
-public class TranslationServicePoMenu {
-
- public static abstract class ActionDomainEvent extends IsisApplibModule.ActionDomainEvent<TranslationServicePoMenu> {
- public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier) {
- super(source, identifier);
- }
-
- public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final Object... arguments) {
- super(source, identifier, arguments);
- }
-
- public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final List<Object> arguments) {
- super(source, identifier, arguments);
- }
- }
-
- // //////////////////////////////////////
-
- public static class DownloadPotFileDomainEvent extends ActionDomainEvent {
- public DownloadPotFileDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final Object... arguments) {
- super(source, identifier, arguments);
- }
- }
-
- @Action(
- domainEvent = DownloadPotFileDomainEvent.class,
- semantics = SemanticsOf.SAFE,
- restrictTo = RestrictTo.PROTOTYPING
- )
- @ActionLayout(
- cssClassFa = "fa-download"
- )
- public Clob downloadPotFile(
- @ParameterLayout(named = ".pot file name")
- final String potFileName) {
- final String chars = translationService.toPo();
- return new Clob(potFileName, "text/plain", chars);
- }
-
- public String default0DownloadPotFile() {
- return "translations.pot";
- }
- public boolean hideDownloadPotFile() {
- return translationService.getMode().isRead();
- }
-
- // //////////////////////////////////////
-
-
- @Inject
- private TranslationServicePo translationService;
-
-}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/i18n/po/PoReaderTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/i18n/po/PoReaderTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/i18n/po/PoReaderTest.java
deleted file mode 100644
index 1889394..0000000
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/i18n/po/PoReaderTest.java
+++ /dev/null
@@ -1,212 +0,0 @@
-package org.apache.isis.core.metamodel.services.i18n.po;
-
-import java.util.List;
-import java.util.Locale;
-import com.google.common.collect.Lists;
-import org.jmock.Expectations;
-import org.jmock.auto.Mock;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.apache.isis.applib.services.i18n.LocaleProvider;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-public class PoReaderTest {
-
- @Rule
- public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(JUnitRuleMockery2.Mode.INTERFACES_AND_CLASSES);
-
- @Mock
- TranslationServicePo mockTranslationServicePo;
-
- @Mock
- LocaleProvider mockLocaleProvider;
-
- PoReader poReader;
-
- @Before
- public void setUp() throws Exception {
- context.checking(new Expectations() {{
- allowing(mockTranslationServicePo).getLocaleProvider();
- will(returnValue(mockLocaleProvider));
-
- allowing(mockLocaleProvider).getLocale();
- will(returnValue(Locale.UK));
- }});
- }
-
- public static class Translate extends PoReaderTest {
-
- @Test
- public void singleContext() throws Exception {
-
- // given
- final String context =
- "org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object()";
- final String msgId = "Work of art";
- final String msgStr = "Objet d'art";
-
- poReader = new PoReader(mockTranslationServicePo) {
- @Override
- protected List<String> readPo(final Locale locale) {
- final List<String> lines = Lists.newArrayList();
- lines.add(String.format("#: %s", context));
- lines.add(String.format("msgid \"%s\"", msgId));
- lines.add(String.format("msgstr \"%s\"", msgStr));
- return lines;
- }
- };
-
- // when
- final String translated = poReader.translate(context, msgId);
-
- // then
- assertThat(translated, is(equalTo(msgStr)));
- }
-
- @Test
- public void multipleContext() throws Exception {
-
- // given
- final String context1 =
- "fixture.simple.SimpleObjectsFixturesService#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String)";
- final String context2 =
- "org.apache.isis.applib.fixturescripts.FixtureScripts#runFixtureScript(org.apache.isis.applib.fixturescripts.FixtureScript,java.lang.String)";
- final String msgId = "Parameters";
- final String msgStr = "Paramètres";
-
- poReader = new PoReader(mockTranslationServicePo) {
- @Override
- protected List<String> readPo(final Locale locale) {
- final List<String> lines = Lists.newArrayList();
- lines.add(String.format("#: %s", context1));
- lines.add(String.format("#: %s", context2));
- lines.add(String.format("msgid \"%s\"", msgId));
- lines.add(String.format("msgstr \"%s\"", msgStr));
- return lines;
- }
- };
- // when
- final String translated = poReader.translate(context1, msgId);
-
- // then
- assertThat(translated, is(equalTo(msgStr)));
-
- // when
- final String translated2 = poReader.translate(context2, msgId);
-
- // then
- assertThat(translated2, is(equalTo(msgStr)));
- }
-
- @Test
- public void multipleBlocks() throws Exception {
-
- // given
- final String context1 =
- "org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object()";
- final String msgid1 = "Work of art";
- final String msgstr1 = "Objet d'art";
-
- final String context2 =
- "org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#lookup()";
- final String msgid2 = "Lookup";
- final String msgstr2 = "Look up";
-
- poReader = new PoReader(mockTranslationServicePo) {
- @Override
- protected List<String> readPo(final Locale locale) {
- final List<String> lines = Lists.newArrayList();
- lines.add(String.format("#: %s", context1));
- lines.add(String.format("msgid \"%s\"", msgid1));
- lines.add(String.format("msgstr \"%s\"", msgstr1));
-
- lines.add(String.format(""));
- lines.add(String.format("# "));
-
- lines.add(String.format("#: %s", context2));
- lines.add(String.format("msgid \"%s\"", msgid2));
- lines.add(String.format("msgstr \"%s\"", msgstr2));
-
- lines.add(String.format(""));
- return lines;
- }
- };
-
- // when
- final String translated1 = poReader.translate(context1, msgid1);
-
- // then
- assertThat(translated1, is(equalTo(msgstr1)));
-
- // when
- final String translated2 = poReader.translate(context2, msgid2);
-
- // then
- assertThat(translated2, is(equalTo(msgstr2)));
- }
-
- @Test
- public void withPlurals() throws Exception {
-
- // given
- final String context =
- "org.apache.isis.applib.services.bookmark.BookmarkHolderAssociationContributions#object()";
- final String msgid = "Work of art";
- final String msgid_plural = "Works of art";
- final String msgstr$0 = "Œuvre d'art";
- final String msgstr$1 = "Les œuvres d'art";
-
- poReader = new PoReader(mockTranslationServicePo) {
- @Override
- protected List<String> readPo(final Locale locale) {
- final List<String> lines = Lists.newArrayList();
- lines.add(String.format("#: %s", context));
- lines.add(String.format("msgid \"%s\"", msgid));
- lines.add(String.format("msgid_plural \"%s\"", msgid_plural));
- lines.add(String.format("msgstr[0] \"%s\"", msgstr$0));
- lines.add(String.format("msgstr[1] \"%s\"", msgstr$1));
- return lines;
- }
- };
-
- // when
- final String translated1 = poReader.translate(context, msgid);
-
- // then
- assertThat(translated1, is(equalTo(msgstr$0)));
-
- // when
- final String translated2 = poReader.translate(context, msgid_plural);
-
- // then
- assertThat(translated2, is(equalTo(msgstr$1)));
- }
-
-
-
- @Test
- public void noTranslation() throws Exception {
-
- // given
-
- poReader = new PoReader(mockTranslationServicePo) {
- @Override
- protected List<String> readPo(final Locale locale) {
- return Lists.newArrayList();
- }
- };
-
- // when
- final String translated = poReader.translate("someContext", "Something to translate");
-
- // then
- assertThat(translated, is(equalTo("Something to translate")));
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/Block.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/Block.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/Block.java
new file mode 100644
index 0000000..a39ca1a
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/Block.java
@@ -0,0 +1,114 @@
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import com.google.common.collect.Lists;
+
+class Block {
+
+ private enum State {
+ CONTEXT("^#: (?<value>.+)$"),
+ MSGID("^msgid \"(?<value>.+)\"$"),
+ MSGID_PLURAL("^msgid_plural \"(?<value>.+)\"$"),
+ MSGSTR("^msgstr \"(?<value>.+)\"$"),
+ MSGSTR0("^msgstr\\[0\\] \"(?<value>.+)\"$"),
+ MSGSTR1("^msgstr\\[1\\] \"(?<value>.+)\"$");
+
+ private final Pattern pattern;
+
+ private State(final String regex) {
+ pattern = Pattern.compile(regex);
+ }
+ }
+
+ State state = State.CONTEXT;
+
+ List<String> contextList = Lists.newArrayList();
+ String msgid = null;
+ String msgid_plural = null;
+ String msgstr = null; // either from msgstr or msgstr[0] if there is a plural
+ String msgstr_plural = null; // from msgstr[1]
+
+ Block parseLine(final String line, final Map<ContextAndMsgId, String> translationsByKey) {
+ if (state == State.CONTEXT) {
+ final Matcher contextMatcher = state.pattern.matcher(line);
+ if (contextMatcher.matches()) {
+ final String context = contextMatcher.group("value");
+ contextList.add(context);
+ return this;
+ } else {
+ state = State.MSGID;
+ // fallthrough (there may not have been any more context)
+ }
+ }
+
+ if (state == State.MSGID) {
+ final Matcher msgidMatcher = state.pattern.matcher(line);
+ if (msgidMatcher.matches()) {
+ msgid = msgidMatcher.group("value");
+ state = State.MSGID_PLURAL; // found, next time look for plurals
+ } else {
+ return new Block();
+ }
+ return this;
+ }
+
+ if (state == State.MSGID_PLURAL) {
+ final Matcher msgIdPluralMatcher = state.pattern.matcher(line);
+ if (msgIdPluralMatcher.matches()) {
+ msgid_plural = msgIdPluralMatcher.group("value");
+ state = State.MSGSTR0; // next time look for msgstr[0]
+ return this;
+ } else {
+ state = State.MSGSTR; // fall through (there may not have been any plural form)
+ }
+ }
+
+ if (state == State.MSGSTR) {
+ final Matcher msgStrMatcher = state.pattern.matcher(line);
+ if (msgStrMatcher.matches()) {
+ msgstr = msgStrMatcher.group("value");
+ }
+ append(translationsByKey);
+ return new Block();
+ }
+
+ if (state == State.MSGSTR0) {
+ final Matcher msgStr0Matcher = state.pattern.matcher(line);
+ if (msgStr0Matcher.matches()) {
+ msgstr = msgStr0Matcher.group("value");
+ state = State.MSGSTR1; // next time, look for plural
+ } else {
+ append(translationsByKey);
+ return new Block();
+ }
+ return this;
+ }
+
+ if (state == State.MSGSTR1) {
+ final Matcher msgStr1Matcher = state.pattern.matcher(line);
+ if (msgStr1Matcher.matches()) {
+ msgstr_plural = msgStr1Matcher.group("value");
+ }
+ append(translationsByKey);
+ return new Block();
+ }
+ return this;
+ }
+
+ void append(final Map<ContextAndMsgId, String> translationsByKey) {
+ for (String context : contextList) {
+ if(msgid != null && msgstr != null) {
+ final ContextAndMsgId mc = new ContextAndMsgId(context, msgid, ContextAndMsgId.Type.REGULAR);
+ translationsByKey.put(mc, msgstr);
+ }
+ if(msgid_plural != null && msgstr_plural != null) {
+ final ContextAndMsgId mc = new ContextAndMsgId(context, msgid_plural, ContextAndMsgId.Type.PLURAL_ONLY);
+ translationsByKey.put(mc, msgstr_plural);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/ContextAndMsgId.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/ContextAndMsgId.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/ContextAndMsgId.java
new file mode 100644
index 0000000..d00d08a
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/ContextAndMsgId.java
@@ -0,0 +1,90 @@
+package org.apache.isis.core.runtime.services.i18n.po;
+
+/**
+ * The combination of a <tt>msgId</tt> and context (optionally null) that represents a key to a translatable resource.
+ *
+ * <p>
+ * For example, with this <i>.pot</i> file:
+ * </p>
+ * <pre>
+ * #: org.isisaddons.module.sessionlogger.dom.SessionLoggingServiceMenu#activeSessions()
+ msgid: "Active Sessions"
+
+ #: org.isisaddons.module.audit.dom.AuditingServiceMenu
+ #: org.isisaddons.module.command.dom.CommandServiceMenu
+ #: org.isisaddons.module.publishing.dom.PublishingServiceMenu
+ msgid: "Activity"
+
+ * </pre>
+ *
+ * <p>
+ * the combination of <code>{org.isisaddons.module.sessionlogger.dom.SessionLoggingServiceMenu#activeSessions(), "Active Sessions"}</code> represents such a key, as does <code>{org.isisaddons.module.audit.dom.AuditingServiceMenu, "Activity"}</code>
+ * </p>
+ */
+public class ContextAndMsgId implements Comparable<ContextAndMsgId> {
+
+ public enum Type {
+ /**
+ * The text to use when there is no plural form, or the text to use for singular pattern when there is also a plural form.
+ */
+ REGULAR,
+ /**
+ * The text to use for plural form.
+ */
+ PLURAL_ONLY
+ }
+
+ private final String context;
+ private final String msgId;
+ private final Type type;
+
+ public ContextAndMsgId(final String context, final String msgId, final Type type) {
+ this.context = context == null? "": context;
+ this.msgId = msgId;
+ this.type = type;
+ }
+
+ public String getMsgId() {
+ return msgId;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ /**
+ * Not part of equals/hashCode impl.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final ContextAndMsgId that = (ContextAndMsgId) o;
+
+ if (context != null ? !context.equals(that.context) : that.context != null) return false;
+ if (msgId != null ? !msgId.equals(that.msgId) : that.msgId != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = context != null ? context.hashCode() : 0;
+ result = 31 * result + (msgId != null ? msgId.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public int compareTo(final ContextAndMsgId o) {
+ final int i = msgId.compareTo(o.msgId);
+ if(i != 0) {
+ return i;
+ }
+ return context.compareTo(o.context);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoAbstract.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoAbstract.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoAbstract.java
new file mode 100644
index 0000000..15777cc
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoAbstract.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import org.apache.isis.applib.services.i18n.TranslationService;
+
+abstract class PoAbstract {
+
+ protected final TranslationServicePo translationServicePo;
+ private final TranslationService.Mode mode;
+
+ PoAbstract(final TranslationServicePo translationServicePo, final TranslationService.Mode mode) {
+ this.translationServicePo = translationServicePo;
+ this.mode = mode;
+ }
+
+
+ abstract void shutdown();
+
+ abstract String translate(final String context, final String msgId);
+
+ abstract String translate(final String context, final String msgId, final String msgIdPlural, int num);
+
+ public TranslationService.Mode getMode() {
+ return mode;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoReader.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoReader.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoReader.java
new file mode 100644
index 0000000..03cb9ed
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoReader.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.isis.applib.services.i18n.TranslationService;
+import org.apache.isis.applib.services.i18n.TranslationsResolver;
+
+class PoReader extends PoAbstract {
+
+ public static final String LOCATION_BASE_URL = "isis.services.translation.po.locationBaseUrl";
+ public static Logger LOG = LoggerFactory.getLogger(PoReader.class);
+
+ private final Map<Locale, Map<ContextAndMsgId, String>> translationByKeyByLocale = Maps.newHashMap();
+
+ /**
+ * The basename of the translations file, hard-coded to <tt>translations</tt>.
+ *
+ * <p>
+ * This means that the reader will search for <tt>translations_en-US.po</tt>, <tt>translations_en.po</tt>,
+ * <tt>translations.po</tt>, according to the location that the provided {@link org.apache.isis.applib.services.i18n.TranslationsResolver} searches. For example, if using the Wicket implementation, then will search for these files
+ * under <tt>/WEB-INF</tt> directory.
+ * </p>
+ */
+ private final String basename = "translations";
+
+ private List<String> fallback;
+
+ public PoReader(final TranslationServicePo translationServicePo) {
+ super(translationServicePo, TranslationService.Mode.READ);
+ }
+
+ //region > init, shutdown
+ void init(final Map<String,String> config) {
+ fallback = readUrl(basename + ".po");
+ if(fallback == null) {
+ LOG.warn("No fallback translations found");
+ fallback = Collections.emptyList();
+ }
+ }
+
+ @Override
+ void shutdown() {
+ }
+ //endregion
+
+ public String translate(final String context, final String msgId) {
+ return translate(context, msgId, ContextAndMsgId.Type.REGULAR);
+ }
+
+ @Override
+ String translate(final String context, final String msgId, final String msgIdPlural, final int num) {
+
+ final String msgIdToUse;
+ final ContextAndMsgId.Type type;
+ if (num == 1) {
+ msgIdToUse = msgId;
+ type = ContextAndMsgId.Type.REGULAR;
+ } else {
+ msgIdToUse = msgIdPlural;
+ type = ContextAndMsgId.Type.PLURAL_ONLY;
+ }
+
+ return translate(context, msgIdToUse, type);
+ }
+
+ private String translate(
+ final String context, final String msgId, final ContextAndMsgId.Type type) {
+
+ final Locale targetLocale;
+ try {
+ targetLocale = translationServicePo.getLocaleProvider().getLocale();
+ } catch(final RuntimeException ex){
+ LOG.warn("Failed to obtain locale, returning the original msgId");
+ return msgId;
+ }
+
+ final Map<ContextAndMsgId, String> translationsByKey = readAndCacheTranslationsIfRequired(targetLocale);
+
+ final ContextAndMsgId key = new ContextAndMsgId(context, msgId, type);
+ final String translation = lookupTranslation(translationsByKey, key);
+ if (!Strings.isNullOrEmpty(translation)) {
+ return translation;
+ }
+
+ final ContextAndMsgId keyNoContext = new ContextAndMsgId("", msgId, type);
+ final String translationNoContext = lookupTranslation(translationsByKey, keyNoContext);
+ if (!Strings.isNullOrEmpty(translationNoContext)) {
+ return translationNoContext;
+ }
+
+ LOG.warn("No translation found for: " + key);
+ return msgId;
+ }
+
+ private String lookupTranslation(final Map<ContextAndMsgId, String> translationsByKey, final ContextAndMsgId key) {
+ final String s = translationsByKey.get(key);
+ return s != null? s.trim(): null;
+ }
+
+ private Map<ContextAndMsgId, String> readAndCacheTranslationsIfRequired(final Locale locale) {
+ Map<ContextAndMsgId, String> translationsByKey = translationByKeyByLocale.get(locale);
+ if(translationsByKey != null) {
+ return translationsByKey;
+ }
+
+ translationsByKey = Maps.newHashMap();
+ read(locale, translationsByKey);
+ translationByKeyByLocale.put(locale, translationsByKey);
+
+ return translationsByKey;
+ }
+
+
+ /**
+ * @param locale - the .po file to load
+ * @param translationsByKey - the translations to be populated
+ */
+ private void read(final Locale locale, final Map<ContextAndMsgId, String> translationsByKey) {
+ final List<String> contents = readPo(locale);
+
+ Block block = new Block();
+ for (final String line : contents) {
+ block = block.parseLine(line, translationsByKey);
+ }
+ }
+
+ protected List<String> readPo(final Locale locale) {
+ final List<String> lines = readPoElseNull(locale);
+ if(lines != null) {
+ return lines;
+ }
+ LOG.warn("Could not locate translations for locale: " + locale + ", using fallback");
+ return fallback;
+ }
+
+ private List<String> readPoElseNull(final Locale locale) {
+ final String country = locale.getCountry().toUpperCase(Locale.ROOT);
+ final String language = locale.getLanguage().toLowerCase(Locale.ROOT);
+
+ final List<String> candidates = Lists.newArrayList();
+ if(!Strings.isNullOrEmpty(language)) {
+ if(!Strings.isNullOrEmpty(country)) {
+ candidates.add(basename + "_" + language + "-" + country+ ".po");
+ }
+ candidates.add(basename + "_" + language + ".po");
+ }
+
+ for (final String candidate : candidates) {
+ final List<String> lines = readUrl(candidate);
+ if(lines != null) {
+ return lines;
+ }
+ }
+ return null;
+ }
+
+ private List<String> readUrl(final String candidate) {
+ final TranslationsResolver translationsResolver = translationServicePo.getTranslationsResolver();
+ if(translationsResolver == null) {
+ return null;
+ }
+ return translationsResolver.readLines(candidate);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoWriter.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoWriter.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoWriter.java
new file mode 100644
index 0000000..5b850d6
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/PoWriter.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import java.util.SortedMap;
+import java.util.SortedSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.joda.time.LocalDateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.isis.applib.services.i18n.TranslationService;
+
+class PoWriter extends PoAbstract {
+
+ public static Logger LOG = LoggerFactory.getLogger(PoWriter.class);
+
+ private static class Block {
+ private final String msgId;
+ private final SortedSet<String> contexts = Sets.newTreeSet();
+ private String msgIdPlural;
+
+ private Block(final String msgId) {
+ this.msgId = msgId;
+ }
+ }
+
+ private final SortedMap<String, Block> blocksByMsgId = Maps.newTreeMap();
+
+ public PoWriter(final TranslationServicePo translationServicePo) {
+ super(translationServicePo, TranslationService.Mode.WRITE);
+ }
+
+ //region > shutdown
+
+ @Override
+ void shutdown() {
+ final StringBuilder buf = new StringBuilder();
+ buf.append("\n");
+ buf.append("\n##############################################################################");
+ buf.append("\n#");
+ buf.append("\n# .pot file");
+ buf.append("\n#");
+ buf.append("\n# generated at: ").append(LocalDateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
+ buf.append("\n# generated by: ").append(TranslationServicePo.class.getSimpleName());
+ buf.append("\n#");
+ buf.append("\n# Translate this file to each required language and place in WEB-INF, eg:");
+ buf.append("\n#");
+ buf.append("\n# /WEB-INF/translations_en-US.po");
+ buf.append("\n# /WEB-INF/translations_en.po");
+ buf.append("\n# /WEB-INF/translations_fr-FR.po");
+ buf.append("\n# /WEB-INF/translations_fr.po");
+ buf.append("\n# /WEB-INF/translations.po");
+ buf.append("\n#");
+ buf.append("\n# If the app uses TranslatableString (eg for internationalized validation");
+ buf.append("\n# messages), or if the app calls the TranslationService directly, then ensure");
+ buf.append("\n# that all text to be translated has been captured by running a full");
+ buf.append("\n# integration test suite that exercises all relevant behaviour");
+ buf.append("\n#");
+ buf.append("\n##############################################################################");
+ buf.append("\n");
+ buf.append("\n");
+ buf.append(toPot());
+ buf.append("\n");
+ buf.append("\n");
+ buf.append("\n##############################################################################");
+ buf.append("\n# end of .pot file");
+ buf.append("\n##############################################################################");
+ buf.append("\n");
+ LOG.info(buf.toString());
+ }
+ //endregion
+
+
+ public String translate(final String context, final String msgId) {
+
+ final Block block = blockFor(msgId);
+ block.contexts.add(context);
+
+ return msgId;
+ }
+
+ @Override
+ String translate(final String context, final String msgId, final String msgIdPlural, final int num) {
+
+ final Block block = blockFor(msgId);
+ block.contexts.add(context);
+ block.msgIdPlural = msgIdPlural;
+
+ return null;
+ }
+
+ private Block blockFor(final String msgId) {
+ Block block = blocksByMsgId.get(msgId);
+ if(block == null) {
+ block = new Block(msgId);
+ blocksByMsgId.put(msgId, block);
+ }
+ return block;
+ }
+
+ /**
+ * Not API
+ */
+ String toPot() {
+ final StringBuilder buf = new StringBuilder();
+ for (final String msgId : blocksByMsgId.keySet()) {
+ final Block block = blocksByMsgId.get(msgId);
+ for (final String context : block.contexts) {
+ buf.append("#: ").append(context).append("\n");
+ }
+ buf.append("msgid \"").append(msgId).append("\"\n");
+ if(block.msgIdPlural == null) {
+ buf.append("msgstr \"\"\n");
+ } else {
+ buf.append("msgid_plural \"").append(block.msgIdPlural).append("\"\n");
+ buf.append("msgstr[0] \"\"\n");
+ buf.append("msgstr[1] \"\"\n");
+ }
+ buf.append("\n\n");
+ }
+ return buf.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
new file mode 100644
index 0000000..7bba5b2
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import java.util.Map;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.i18n.LocaleProvider;
+import org.apache.isis.applib.services.i18n.TranslationService;
+import org.apache.isis.applib.services.i18n.TranslationsResolver;
+import org.apache.isis.core.runtime.system.DeploymentType;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+
+@DomainService
+public class TranslationServicePo implements TranslationService {
+
+ public static Logger LOG = LoggerFactory.getLogger(TranslationServicePo.class);
+
+ public static final String KEY_DEPLOYMENT_TYPE = "isis.deploymentType";
+ public static final String KEY_PO_MODE = "isis.services.translation.po.mode";
+
+ private PoAbstract po;
+
+ /**
+ * Defaults to writer mode because the service won't have been init'd while the metamodel is bring instantiated,
+ * and we want to ensure that we capture all requests for translation.
+ */
+ public TranslationServicePo() {
+ po = new PoWriter(this);
+ }
+
+ //region > init, shutdown
+
+ @Programmatic
+ @PostConstruct
+ public void init(final Map<String,String> config) {
+
+ if(getLocaleProvider() == null || getTranslationsResolver() == null) {
+ // remain in write mode
+ return;
+ }
+
+ final boolean prototypeOrTest = isPrototypeOrTest();
+
+ final String translationMode = config.get(KEY_PO_MODE);
+ final boolean forceRead =
+ translationMode != null &&
+ ("read".equalsIgnoreCase(translationMode) ||
+ "reader".equalsIgnoreCase(translationMode));
+
+ if(prototypeOrTest && !forceRead) {
+ // remain in write mode
+ return;
+ }
+
+ // switch to read mode
+ final PoReader poReader = new PoReader(this);
+ poReader.init(config);
+ po = poReader;
+ }
+
+ protected boolean isPrototypeOrTest() {
+ final DeploymentType deploymentType = getDeploymentType();
+ return !deploymentType.isProduction();
+ }
+
+ @Programmatic
+ @PreDestroy
+ public void shutdown() {
+ po.shutdown();
+ }
+ //endregion
+
+
+ @Override
+ @Programmatic
+ public String translate(final String context, final String text) {
+ return po.translate(context, text);
+ }
+
+ @Override
+ public String translate(final String context, final String singularText, final String pluralText, final int num) {
+ return po.translate(context, singularText, pluralText, num);
+ }
+
+ @Override
+ public Mode getMode() {
+ return po.getMode();
+ }
+
+ /**
+ * Not API
+ */
+ @Programmatic
+ public String toPo() {
+ if (!getMode().isWrite()) {
+ throw new IllegalStateException("Not in write mode");
+ }
+ return ((PoWriter)po).toPot();
+ }
+
+ // //////////////////////////////////////
+
+ DeploymentType getDeploymentType() {
+ return IsisContext.getDeploymentType();
+ }
+
+
+ @Inject
+ private
+ TranslationsResolver translationsResolver;
+
+ @Programmatic
+ TranslationsResolver getTranslationsResolver() {
+ return translationsResolver;
+ }
+
+ @Inject
+ private
+ LocaleProvider localeProvider;
+
+ @Programmatic
+ LocaleProvider getLocaleProvider() {
+ return localeProvider;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePoMenu.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePoMenu.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePoMenu.java
new file mode 100644
index 0000000..e349668
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePoMenu.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.runtime.services.i18n.po;
+
+import java.util.List;
+import javax.inject.Inject;
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.applib.IsisApplibModule;
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.RestrictTo;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.value.Clob;
+
+@DomainService(
+ nature = NatureOfService.VIEW_MENU_ONLY
+)
+@DomainServiceLayout(
+ menuBar = DomainServiceLayout.MenuBar.SECONDARY,
+ named = "Prototyping"
+)
+public class TranslationServicePoMenu {
+
+ public static abstract class ActionDomainEvent extends IsisApplibModule.ActionDomainEvent<TranslationServicePoMenu> {
+ public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier) {
+ super(source, identifier);
+ }
+
+ public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final Object... arguments) {
+ super(source, identifier, arguments);
+ }
+
+ public ActionDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final List<Object> arguments) {
+ super(source, identifier, arguments);
+ }
+ }
+
+ // //////////////////////////////////////
+
+ public static class DownloadPotFileDomainEvent extends ActionDomainEvent {
+ public DownloadPotFileDomainEvent(final TranslationServicePoMenu source, final Identifier identifier, final Object... arguments) {
+ super(source, identifier, arguments);
+ }
+ }
+
+ @Action(
+ domainEvent = DownloadPotFileDomainEvent.class,
+ semantics = SemanticsOf.SAFE,
+ restrictTo = RestrictTo.PROTOTYPING
+ )
+ @ActionLayout(
+ cssClassFa = "fa-download"
+ )
+ public Clob downloadPotFile(
+ @ParameterLayout(named = ".pot file name")
+ final String potFileName) {
+ final String chars = translationService.toPo();
+ return new Clob(potFileName, "text/plain", chars);
+ }
+
+ public String default0DownloadPotFile() {
+ return "translations.pot";
+ }
+ public boolean hideDownloadPotFile() {
+ return translationService.getMode().isRead();
+ }
+
+ // //////////////////////////////////////
+
+
+ @Inject
+ private TranslationServicePo translationService;
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/362a5bfc/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
index 7fae462..f16068b 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
@@ -87,10 +87,10 @@ public class IsisWebAppBootstrapper implements ServletContextListener {
new ResourceStreamSourceForWebInf(servletContext)) ;
if ( configLocation != null ) {
- LOG.info( "Config override location: " + configLocation );
- compositeSource.addResourceStreamSource(ResourceStreamSourceFileSystem.create(configLocation));
+ LOG.info( "Config override location: " + configLocation );
+ compositeSource.addResourceStreamSource(ResourceStreamSourceFileSystem.create(configLocation));
} else {
- LOG.info( "Config override location: No override location configured" );
+ LOG.info( "Config override location: No override location configured" );
}
// will load either from WEB-INF, from the classpath or from config directory.