ISIS-903: adding PoReader impl along with new XxxTranslated facets Also implementations of LocaleProvider and new UrlResolver service from Wicket.
However, not there yet; the .po file read/write needs to handle plural form, and the interface for TranslationService needs to be extended similarly Project: http://git-wip-us.apache.org/repos/asf/isis/repo Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/b0470ba5 Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/b0470ba5 Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/b0470ba5 Branch: refs/heads/master Commit: b0470ba5632c9b0800e7e011be114295b07a1348 Parents: 1ee748e Author: Dan Haywood <[email protected]> Authored: Tue Feb 17 11:11:29 2015 +0000 Committer: Dan Haywood <[email protected]> Committed: Wed Feb 18 14:07:38 2015 +0000 ---------------------------------------------------------------------- .../viewer/services/LocaleProviderWicket.java | 47 +++++++++++++ .../viewer/services/UrlResolverWicket.java | 65 ++++++++++++++++++ .../applib/services/i18n/LocaleProvider.java | 29 ++++++++ .../isis/applib/services/i18n/UrlResolver.java | 29 ++++++++ .../applib/services/locale/LocaleProvider.java | 29 -------- .../facets/SingleStringValueFacetAbstract.java | 6 +- .../all/i18n/DescribedAsFacetTranslated.java | 52 ++++++++++++++ .../facets/all/i18n/I18nFacetFactory.java | 19 ++---- .../facets/all/i18n/NamedFacetTranslated.java | 56 +++++++++++++++ .../facets/all/i18n/PluralFacetTranslated.java | 50 ++++++++++++++ .../object/plural/PluralFacetAbstract.java | 7 +- .../plural/inferred/PluralFacetInferred.java | 2 +- .../services/i18n/LocaleProviderDefault.java | 2 +- .../core/metamodel/services/i18n/po/Block.java | 65 ++++++++++++++++++ .../metamodel/services/i18n/po/PoReader.java | 72 ++++++++++++++------ .../services/i18n/po/TranslationServicePo.java | 17 ++++- .../dflt/ObjectSpecificationDefault.java | 29 ++++++-- .../services/i18n/po/PoReaderTest.java | 68 ++++++++++++++---- 18 files changed, 559 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/LocaleProviderWicket.java ---------------------------------------------------------------------- diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/LocaleProviderWicket.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/LocaleProviderWicket.java new file mode 100644 index 0000000..966f8fb --- /dev/null +++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/LocaleProviderWicket.java @@ -0,0 +1,47 @@ +/* + * 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.viewer.wicket.viewer.services; + +import java.util.Locale; +import org.apache.wicket.Session; +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; + + +/** + * An implementation that provides the locale of the current session. + */ +@DomainService +public class LocaleProviderWicket implements LocaleProvider { + + public static Logger LOG = LoggerFactory.getLogger(LocaleProviderWicket.class); + + @Programmatic + @Override + public Locale getLocale() { + return getSession().getLocale(); + } + + protected Session getSession() { + return Session.get(); + } +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/UrlResolverWicket.java ---------------------------------------------------------------------- diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/UrlResolverWicket.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/UrlResolverWicket.java new file mode 100644 index 0000000..d98093f --- /dev/null +++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/services/UrlResolverWicket.java @@ -0,0 +1,65 @@ +/* + * 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.viewer.wicket.viewer.services; + +import java.io.IOException; +import java.net.URL; +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.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.UrlResolver; +import org.apache.isis.viewer.wicket.viewer.IsisWicketApplication; + + +/** + * An implementation that reads from /WEB-INF/... + */ +@DomainService +public class UrlResolverWicket implements UrlResolver { + + public static Logger LOG = LoggerFactory.getLogger(UrlResolverWicket.class); + + @Override + @Programmatic + public List<String> readLines(final String file) { + try { + final ServletContext servletContext = getIsisWicketApplication().getServletContext(); + final URL url = servletContext.getResource("/WEB-INF/" + file); + return readLines(url); + } catch (final IOException ignored) { + return null; + } + } + + private static List<String> readLines(final URL url) throws IOException { + final CharSource charSource = Resources.asCharSource(url, Charsets.UTF_8); + return charSource.readLines(); + } + + protected IsisWicketApplication getIsisWicketApplication() { + return IsisWicketApplication.get(); + } + +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/applib/src/main/java/org/apache/isis/applib/services/i18n/LocaleProvider.java ---------------------------------------------------------------------- diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/i18n/LocaleProvider.java b/core/applib/src/main/java/org/apache/isis/applib/services/i18n/LocaleProvider.java new file mode 100644 index 0000000..9fe73d3 --- /dev/null +++ b/core/applib/src/main/java/org/apache/isis/applib/services/i18n/LocaleProvider.java @@ -0,0 +1,29 @@ +/* + * 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.applib.services.i18n; + +import java.util.Locale; +import org.apache.isis.applib.annotation.Programmatic; + +public interface LocaleProvider { + + @Programmatic + Locale getLocale(); + +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/applib/src/main/java/org/apache/isis/applib/services/i18n/UrlResolver.java ---------------------------------------------------------------------- diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/i18n/UrlResolver.java b/core/applib/src/main/java/org/apache/isis/applib/services/i18n/UrlResolver.java new file mode 100644 index 0000000..5dcb614 --- /dev/null +++ b/core/applib/src/main/java/org/apache/isis/applib/services/i18n/UrlResolver.java @@ -0,0 +1,29 @@ +/* + * 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.applib.services.i18n; + +import java.util.List; +import org.apache.isis.applib.annotation.Programmatic; + +public interface UrlResolver { + + @Programmatic + public List<String> readLines(final String file); + +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/applib/src/main/java/org/apache/isis/applib/services/locale/LocaleProvider.java ---------------------------------------------------------------------- diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/locale/LocaleProvider.java b/core/applib/src/main/java/org/apache/isis/applib/services/locale/LocaleProvider.java deleted file mode 100644 index 5a34285..0000000 --- a/core/applib/src/main/java/org/apache/isis/applib/services/locale/LocaleProvider.java +++ /dev/null @@ -1,29 +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.applib.services.locale; - -import java.util.Locale; -import org.apache.isis.applib.annotation.Programmatic; - -public interface LocaleProvider { - - @Programmatic - Locale getLocale(); - -} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleStringValueFacetAbstract.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleStringValueFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleStringValueFacetAbstract.java index 1fed8c9..ad86d5f 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleStringValueFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleStringValueFacetAbstract.java @@ -27,7 +27,11 @@ public abstract class SingleStringValueFacetAbstract extends FacetAbstract imple private final String value; public SingleStringValueFacetAbstract(final Class<? extends Facet> facetType, final FacetHolder holder, final String value) { - super(facetType, holder, Derivation.NOT_DERIVED); + this(facetType, holder, value, Derivation.NOT_DERIVED); + } + + public SingleStringValueFacetAbstract(final Class<? extends Facet> type, final FacetHolder holder, final String value, final Derivation derivation) { + super(type, holder, derivation); this.value = value; } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/DescribedAsFacetTranslated.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/DescribedAsFacetTranslated.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/DescribedAsFacetTranslated.java new file mode 100644 index 0000000..4e67f26 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/DescribedAsFacetTranslated.java @@ -0,0 +1,52 @@ +/* + * 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.facets.all.i18n; + +import java.util.Locale; +import org.apache.isis.applib.services.i18n.LocaleProvider; +import org.apache.isis.applib.services.i18n.TranslationService; +import org.apache.isis.core.metamodel.facetapi.FacetAbstract; +import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder; +import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet; + +public class DescribedAsFacetTranslated extends FacetAbstract implements DescribedAsFacet{ + + private final String context; + private final String originalText; + private final TranslationService translationService; + private final LocaleProvider localeProvider; + + public DescribedAsFacetTranslated( + final String context, final String originalText, + final TranslationService translationService, final LocaleProvider localeProvider, + final IdentifiedHolder holder) { + super(DescribedAsFacet.class, holder, Derivation.NOT_DERIVED); + this.context = context; + this.originalText = originalText; + this.translationService = translationService; + this.localeProvider = localeProvider; + } + + @Override + public String value() { + final Locale locale = localeProvider.getLocale(); + return translationService.translate(context, originalText, locale); + } +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/I18nFacetFactory.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/I18nFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/I18nFacetFactory.java index f866f94..0b6e689 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/I18nFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/I18nFacetFactory.java @@ -19,17 +19,13 @@ package org.apache.isis.core.metamodel.facets.all.i18n; -import java.util.Locale; -import java.util.Objects; import com.google.common.base.Strings; +import org.apache.isis.applib.services.i18n.LocaleProvider; import org.apache.isis.applib.services.i18n.TranslationService; -import org.apache.isis.applib.services.locale.LocaleProvider; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facetapi.FacetUtil; import org.apache.isis.core.metamodel.facetapi.FeatureType; import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder; -import org.apache.isis.core.metamodel.facetdecorator.i18n.internal.DescribedAsFacetWrapI18n; -import org.apache.isis.core.metamodel.facetdecorator.i18n.internal.NamedFacetWrapI18n; import org.apache.isis.core.metamodel.facets.ContributeeMemberFacetFactory; import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract; import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet; @@ -103,10 +99,8 @@ public class I18nFacetFactory extends FacetFactoryAbstract implements Contribute final TranslationService translationService = lookupTranslationService(); final LocaleProvider localeProvider = lookupLocaleProvider(); - final Locale locale = localeProvider.getLocale(); - final String translatedText = translationService.translate(context, originalText, locale); - if(!Objects.equals(originalText, translatedText)) { - FacetUtil.addFacet(new NamedFacetWrapI18n(translatedText, facetHolder)); + if(translationService != null && localeProvider != null) { + FacetUtil.addFacet(new NamedFacetTranslated(context, originalText, translationService, localeProvider, facetHolder)); } } @@ -125,11 +119,10 @@ public class I18nFacetFactory extends FacetFactoryAbstract implements Contribute final TranslationService translationService = lookupTranslationService(); final LocaleProvider localeProvider = lookupLocaleProvider(); - final Locale locale = localeProvider.getLocale(); - final String translatedText = translationService.translate(context, originalText, locale); - if(!Objects.equals(originalText, translatedText)) { - FacetUtil.addFacet(new DescribedAsFacetWrapI18n(translatedText, holder)); + if(translationService != null && localeProvider != null) { + FacetUtil.addFacet(new DescribedAsFacetTranslated(context, originalText, translationService, localeProvider, holder)); } + } private boolean isNullOrEmptyWhenTrimmed(final String originalText) { http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/NamedFacetTranslated.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/NamedFacetTranslated.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/NamedFacetTranslated.java new file mode 100644 index 0000000..6e7fb31 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/NamedFacetTranslated.java @@ -0,0 +1,56 @@ +/* + * 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.facets.all.i18n; + +import org.apache.isis.applib.services.i18n.LocaleProvider; +import org.apache.isis.applib.services.i18n.TranslationService; +import org.apache.isis.core.metamodel.facetapi.FacetAbstract; +import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder; +import org.apache.isis.core.metamodel.facets.all.named.NamedFacet; + +public class NamedFacetTranslated extends FacetAbstract implements NamedFacet { + + final TranslationService translationService; + final LocaleProvider localeProvider; + String context; + String originalText; + + public NamedFacetTranslated( + final String context, final String originalText, + final TranslationService translationService, final LocaleProvider localeProvider, + final IdentifiedHolder facetHolder) { + super(NamedFacet.class, facetHolder, Derivation.NOT_DERIVED); + this.context = context; + this.originalText = originalText; + this.translationService = translationService; + this.localeProvider = localeProvider; + } + + @Override + public String value() { + return translationService.translate(context, originalText, localeProvider.getLocale()); + } + + @Override + public boolean escaped() { + final NamedFacet underlyingFacet = (NamedFacet) getUnderlyingFacet(); + return underlyingFacet != null && underlyingFacet.escaped(); + } +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/PluralFacetTranslated.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/PluralFacetTranslated.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/PluralFacetTranslated.java new file mode 100644 index 0000000..89ece86 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i18n/PluralFacetTranslated.java @@ -0,0 +1,50 @@ +/* + * 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.facets.all.i18n; + +import org.apache.isis.applib.services.i18n.LocaleProvider; +import org.apache.isis.applib.services.i18n.TranslationService; +import org.apache.isis.core.commons.lang.StringExtensions; +import org.apache.isis.core.metamodel.facetapi.FacetAbstract; +import org.apache.isis.core.metamodel.facetapi.FacetHolder; +import org.apache.isis.core.metamodel.facets.object.plural.PluralFacet; + +public class PluralFacetTranslated extends FacetAbstract implements PluralFacet { + + private final TranslationService translationService; + private final LocaleProvider localeProvider; + private String context; + private String originalText; + + public PluralFacetTranslated(final NamedFacetTranslated facet, final FacetHolder facetHolder) { + super(PluralFacet.class, facetHolder, Derivation.DERIVED); + this.translationService = facet.translationService; + this.localeProvider = facet.localeProvider; + this.context = facet.context; + this.originalText = facet.originalText; + } + + @Override + public String value() { + final String singularName = translationService.translate(context, originalText, localeProvider.getLocale()); + return StringExtensions.asPluralName(singularName); + } + +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/PluralFacetAbstract.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/PluralFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/PluralFacetAbstract.java index 24f4075..64c8fd0 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/PluralFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/PluralFacetAbstract.java @@ -30,7 +30,12 @@ public abstract class PluralFacetAbstract extends SingleStringValueFacetAbstract } public PluralFacetAbstract(final String value, final FacetHolder holder) { - super(type(), holder, value); + this(value, holder, Derivation.NOT_DERIVED); } + public PluralFacetAbstract(final String value, final FacetHolder holder, final Derivation derivation) { + super(type(), holder, value, derivation); + } + + } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/inferred/PluralFacetInferred.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/inferred/PluralFacetInferred.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/inferred/PluralFacetInferred.java index 82baefb0..572c3d3 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/inferred/PluralFacetInferred.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/plural/inferred/PluralFacetInferred.java @@ -25,7 +25,7 @@ import org.apache.isis.core.metamodel.facets.object.plural.PluralFacetAbstract; public class PluralFacetInferred extends PluralFacetAbstract { public PluralFacetInferred(final String value, final FacetHolder holder) { - super(value, holder); + super(value, holder, Derivation.DERIVED); } } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/LocaleProviderDefault.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/LocaleProviderDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/LocaleProviderDefault.java index 4b7ec49..6df4111 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/LocaleProviderDefault.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/LocaleProviderDefault.java @@ -21,7 +21,7 @@ package org.apache.isis.core.metamodel.services.i18n; import java.util.Locale; import org.apache.isis.applib.annotation.Programmatic; -import org.apache.isis.applib.services.locale.LocaleProvider; +import org.apache.isis.applib.services.i18n.LocaleProvider; /** * Not annotated with @DomainService, but is registered as a fallback by <tt>ServicesInstallerFallback</tt>. http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/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 new file mode 100644 index 0000000..d4543ad --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/i18n/po/Block.java @@ -0,0 +1,65 @@ +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, + MSGID, + MSGSTR + } + + private static final Pattern contextPattern = Pattern.compile("^#: (?<context>.+)$"); + private static final Pattern msgidPattern = Pattern.compile("^msgid \"(?<msgid>.+)\"$"); + private static final Pattern msgstrPattern = Pattern.compile("^msgstr \"(?<msgstr>.+)\"$"); + + State state = State.CONTEXT; + + List<String> contextList = Lists.newArrayList(); + String msgid = null; + String msgstr = null; + + Block parseLine(final String line, final Map<MsgIdAndContext, String> translationsByKey) { + if (state == State.CONTEXT) { + final Matcher matcher = contextPattern.matcher(line); + if (matcher.matches()) { + final String context = matcher.group("context"); + contextList.add(context); + } else { + state = State.MSGID; + } + } + if (state == State.MSGID) { + final Matcher matcher = msgidPattern.matcher(line); + if (matcher.matches()) { + msgid = matcher.group("msgid"); + } else { + state = State.MSGSTR; + } + } + if (state == State.MSGSTR) { + final Matcher matcher = msgstrPattern.matcher(line); + if (matcher.matches()) { + msgstr = matcher.group("msgstr"); + } + append(translationsByKey); + return new Block(); + } + return this; + } + + private void append(final Map<MsgIdAndContext, String> translationsByKey) { + if(msgid != null && msgstr != null) { + for (String context : contextList) { + final MsgIdAndContext mc = new MsgIdAndContext(msgid, context); + translationsByKey.put(mc, msgstr); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/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 index 514c81c..83806e6 100644 --- 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 @@ -18,37 +18,53 @@ */ package org.apache.isis.core.metamodel.services.i18n.po; -import java.io.File; -import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; -import com.google.common.base.Charsets; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.io.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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<MsgIdAndContext, 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.UrlResolver} 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); } //region > init, shutdown void init(final Map<String,String> config) { + fallback = readPoElseNull(Locale.ROOT); + if(fallback == null) { + LOG.warn("No fallback translations found"); + fallback = Collections.emptyList(); + } } void shutdown() { } //endregion - public String translate(final String context, final String msgId, final Locale targetLocale) { final Map<MsgIdAndContext, String> translationsByKey = readAndCacheTranslationsIfRequired(targetLocale); @@ -82,34 +98,52 @@ class PoReader extends PoAbstract { return translationsByKey; } + /** * @param locale - the .po file to load * @param translationsByKey - the translations to be populated */ private void read(final Locale locale, final Map<MsgIdAndContext, String> translationsByKey) { - final List<String> fileContents = readFile(locale); + final List<String> contents = readPo(locale); - // TODO: parse fileContents into translationsByKey + Block block = new Block(); + for (final String line : contents) { + block = block.parseLine(line, translationsByKey); + } } - List<String> readFile(final Locale locale) { - final File file = locateFile(locale); - try { - return Files.readLines(file, Charsets.UTF_8); - } catch (final IOException ex) { - LOG.warn("Could not locate file for locale: " + locale, ex); - return Collections.emptyList(); + 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; } - File locateFile(final Locale locale) { + 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 String translate(final Locale locale, final MsgIdAndContext key) { - // TODO - return key.getMsgId(); + private List<String> readUrl(final String candidate) { + return translationServicePo.urlResolver.readLines(candidate); } - } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/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 index a8cf426..5bebc99 100644 --- 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 @@ -22,10 +22,12 @@ import java.util.Locale; 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.Programmatic; import org.apache.isis.applib.services.i18n.TranslationService; +import org.apache.isis.applib.services.i18n.UrlResolver; /** * Not annotated with @DomainService, but is registered as a fallback by <tt>ServicesInstallerFallback</tt>. @@ -39,7 +41,7 @@ public class TranslationServicePo implements TranslationService { private PoAbstract po; public TranslationServicePo() { - po = new PoWriter(this); + po = new PoReader(this); } //region > init, shutdown @@ -47,13 +49,19 @@ public class TranslationServicePo implements TranslationService { @Programmatic @PostConstruct public void init(final Map<String,String> config) { + final String deploymentType = config.get("isis.deploymentType"); prototype = deploymentType==null || deploymentType.toLowerCase().contains("prototype") || deploymentType.toLowerCase().contains("test") ; - if (!prototype) { - po = new PoReader(this); + String translationMode = config.get("isis.services.translation.po.mode"); + final boolean forceRead = + "read".equalsIgnoreCase(translationMode) || + "reader".equalsIgnoreCase(translationMode); + + if (prototype && !forceRead) { + po = new PoWriter(this); } po.init(config); } @@ -86,4 +94,7 @@ public class TranslationServicePo implements TranslationService { return ((PoWriter)po).toPo(); } + @Inject + UrlResolver urlResolver; + } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java ---------------------------------------------------------------------- diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java index 0a1c7c2..09355a0 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java @@ -41,6 +41,8 @@ import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facets.FacetedMethod; import org.apache.isis.core.metamodel.facets.ImperativeFacet; +import org.apache.isis.core.metamodel.facets.all.i18n.NamedFacetTranslated; +import org.apache.isis.core.metamodel.facets.all.i18n.PluralFacetTranslated; import org.apache.isis.core.metamodel.facets.all.named.NamedFacet; import org.apache.isis.core.metamodel.facets.all.named.NamedFacetInferred; import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet; @@ -53,10 +55,24 @@ import org.apache.isis.core.metamodel.facets.object.value.ValueFacet; import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; import org.apache.isis.core.metamodel.facets.object.wizard.WizardFacet; import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector; -import org.apache.isis.core.metamodel.spec.*; -import org.apache.isis.core.metamodel.spec.feature.*; +import org.apache.isis.core.metamodel.spec.ActionType; +import org.apache.isis.core.metamodel.spec.ObjectInstantiationException; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.spec.ObjectSpecificationException; +import org.apache.isis.core.metamodel.spec.SpecificationContext; +import org.apache.isis.core.metamodel.spec.feature.Contributed; +import org.apache.isis.core.metamodel.spec.feature.ObjectAction; +import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.core.metamodel.spec.feature.ObjectMember; +import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext; import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor; -import org.apache.isis.core.metamodel.specloader.specimpl.*; +import org.apache.isis.core.metamodel.specloader.specimpl.CreateObjectContext; +import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder; +import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilderContext; +import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionImpl; +import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract; +import org.apache.isis.core.metamodel.specloader.specimpl.OneToManyAssociationImpl; +import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationImpl; public class ObjectSpecificationDefault extends ObjectSpecificationAbstract implements DebuggableWithTitle, FacetHolder { @@ -186,7 +202,12 @@ public class ObjectSpecificationDefault extends ObjectSpecificationAbstract impl PluralFacet pluralFacet = getFacet(PluralFacet.class); if (pluralFacet == null) { - pluralFacet = new PluralFacetInferred(StringExtensions.asPluralName(namedFacet.value()), this); + if(namedFacet instanceof NamedFacetTranslated) { + final NamedFacetTranslated facet = (NamedFacetTranslated) namedFacet; + pluralFacet = new PluralFacetTranslated(facet, this); + } else { + pluralFacet = new PluralFacetInferred(StringExtensions.asPluralName(namedFacet.value()), this); + } addFacet(pluralFacet); } } http://git-wip-us.apache.org/repos/asf/isis/blob/b0470ba5/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 index bbab018..6b516f2 100644 --- 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 @@ -3,7 +3,6 @@ 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.junit.Ignore; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; @@ -14,9 +13,8 @@ public class PoReaderTest { PoReader poReader; - public static class ReadFile extends PoReaderTest { + public static class Translate extends PoReaderTest { - @Ignore // TODO @Test public void singleContext() throws Exception { @@ -28,11 +26,11 @@ public class PoReaderTest { poReader = new PoReader(null) { @Override - List<String> readFile(final Locale locale) { + 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)); + lines.add(String.format("msgid \"%s\"", msgId)); + lines.add(String.format("msgstr \"%s\"", msgStr)); return lines; } }; @@ -44,7 +42,6 @@ public class PoReaderTest { assertThat(translated, is(equalTo(msgStr))); } - @Ignore // TODO @Test public void multipleContext() throws Exception { @@ -58,12 +55,12 @@ public class PoReaderTest { poReader = new PoReader(null) { @Override - List<String> readFile(final Locale locale) { + 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)); + lines.add(String.format("msgid \"%s\"", msgId)); + lines.add(String.format("msgstr \"%s\"", msgStr)); return lines; } }; @@ -80,7 +77,53 @@ public class PoReaderTest { assertThat(translated2, is(equalTo(msgStr))); } - @Ignore // TODO + @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(null) { + @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, Locale.FRENCH); + + // then + assertThat(translated1, is(equalTo(msgstr1))); + + // when + final String translated2 = poReader.translate(context2, msgid2, Locale.FRENCH); + + // then + assertThat(translated2, is(equalTo(msgstr2))); + } + @Test public void noTranslation() throws Exception { @@ -88,7 +131,7 @@ public class PoReaderTest { poReader = new PoReader(null) { @Override - List<String> readFile(final Locale locale) { + protected List<String> readPo(final Locale locale) { return Lists.newArrayList(); } }; @@ -99,7 +142,6 @@ public class PoReaderTest { // then assertThat(translated, is(equalTo("Something to translate"))); } - } } \ No newline at end of file
