diff --git a/scm-core/src/main/java/sonia/scm/i18n/Bundle.java b/scm-core/src/main/java/sonia/scm/i18n/Bundle.java index be349fddb7..11429133d8 100644 --- a/scm-core/src/main/java/sonia/scm/i18n/Bundle.java +++ b/scm-core/src/main/java/sonia/scm/i18n/Bundle.java @@ -30,6 +30,7 @@ */ + package sonia.scm.i18n; //~--- non-JDK imports -------------------------------------------------------- @@ -41,6 +42,7 @@ import sonia.scm.util.Util; import java.text.MessageFormat; import java.util.Locale; +import java.util.MissingResourceException; import java.util.ResourceBundle; /** @@ -134,8 +136,33 @@ public class Bundle return msg; } + /** + * Returns the value of the key, formatted with {@link MessageFormat} or null + * if the key is not present in the bundle. + * + * + * @param key key in the properties file + * @param args format arguments + * + * @return formated message or null + * + * @since 1.37 + */ + public String getStringIfPresent(String key, Object... args) + { + String msg = null; + + try + { + msg = getString(key, args); + } + catch (MissingResourceException ex) {} + + return msg; + } + //~--- fields --------------------------------------------------------------- /** Field description */ - private ResourceBundle bundle; + private final ResourceBundle bundle; } diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18n.java b/scm-core/src/main/java/sonia/scm/i18n/I18n.java new file mode 100644 index 0000000000..63d761fb17 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18n.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.i18n; + +//~--- JDK imports ------------------------------------------------------------ + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The I18n annotation is used by the {@link I18nMessages} class to define the + * resource bundle key. + * + * @author Sebastian Sdorra + * @since 1.37 + */ +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface I18n +{ + /** + * Returns the key for the resource bundle. + * + * @return resource bundle key + */ + String value(); +} diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18nException.java b/scm-core/src/main/java/sonia/scm/i18n/I18nException.java new file mode 100644 index 0000000000..dc7d062de7 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18nException.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.i18n; + +/** + * + * @author Sebastian Sdorra + */ +public class I18nException extends RuntimeException +{ + + /** Field description */ + private static final long serialVersionUID = 1845326427312983227L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public I18nException() {} + + /** + * Constructs ... + * + * + * @param message + */ + public I18nException(String message) + { + super(message); + } + + /** + * Constructs ... + * + * + * @param cause + */ + public I18nException(Throwable cause) + { + super(cause); + } + + /** + * Constructs ... + * + * + * @param message + * @param cause + */ + public I18nException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java b/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java new file mode 100644 index 0000000000..97ba0cb0c2 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.i18n; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Objects; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +//~--- JDK imports ------------------------------------------------------------ + +import java.lang.reflect.Field; + +import java.util.Locale; + +/** + * The I18nMessages class instantiates a class and initializes all {@link String} + * fields with values from a resource bundle. The resource bundle must have the + * same name as the class. Each field which should be initialized from the + * bundle, must match a key in the resource bundle or is annotated with a + * {@link I18n} annotation which holds the key. I18nMessages injects also the + * locale and the bundle if it founds a field with the corresponding type. + * + * @author Sebastian Sdorra + * @since 1.37 + */ +public final class I18nMessages +{ + + /** Field description */ + private static final Cache cache = + CacheBuilder.newBuilder().build(); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + private I18nMessages() {} + + //~--- get methods ---------------------------------------------------------- + + /** + * Same as {@link #get(java.lang.Class, java.util.Locale)}, with locale + * {@link Locale#ENGLISH}. + * + * @param msgClass message class + * @param type of message class + * + * @return instance of message class + */ + public static T get(Class msgClass) + { + return get(msgClass, Locale.ENGLISH); + } + + /** + * Returns a instance of the given message class with all message fields + * initialized. + * + * + * @param msgClass message class + * @param locale locale + * @param type of the message class + * + * @return instance of message class + */ + public synchronized static T get(Class msgClass, Locale locale) + { + CacheKey ck = new CacheKey(locale, msgClass); + T instance = (T) cache.getIfPresent(ck); + + if (instance == null) + { + instance = createInstance(msgClass, locale); + cache.put(ck, instance); + } + + return instance; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param msgClass + * @param locale + * @param + * + * @return + */ + private static T createInstance(Class msgClass, Locale locale) + { + Bundle bundle = Bundle.getBundle(msgClass.getName(), locale); + T instance = null; + + try + { + instance = msgClass.newInstance(); + initializeInstance(bundle, locale, msgClass, instance); + } + catch (Exception ex) + { + throw new I18nException("could not instantiate/initialize class", ex); + } + + return instance; + } + + /** + * Method description + * + * + * @param bundle + * @param locale + * @param msgClass + * @param instance + * + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + private static void initializeInstance(Bundle bundle, Locale locale, + Class msgClass, Object instance) + throws IllegalArgumentException, IllegalAccessException + { + for (Field field : msgClass.getFields()) + { + if (field.getType().isAssignableFrom(String.class)) + { + String key = field.getName(); + I18n i18n = field.getAnnotation(I18n.class); + + if (i18n != null) + { + key = i18n.value(); + } + + String value = bundle.getString(key); + + if (value != null) + { + field.setAccessible(true); + field.set(instance, value); + } + } + else if (field.getType().isAssignableFrom(Bundle.class)) + { + field.setAccessible(true); + field.set(instance, bundle); + } + else if (field.getType().isAssignableFrom(Locale.class)) + { + + field.setAccessible(true); + field.set(instance, locale); + } + } + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 14/03/15 + * @author Enter your name here... + */ + private static class CacheKey + { + + /** + * Constructs ... + * + * + * @param locale + * @param msgClass + */ + public CacheKey(Locale locale, Class msgClass) + { + this.locale = locale; + this.msgClass = msgClass; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final CacheKey other = (CacheKey) obj; + + return Objects.equal(locale, other.locale) + && Objects.equal(msgClass, other.msgClass); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(locale, msgClass); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private final Locale locale; + + /** Field description */ + private final Class msgClass; + } +} diff --git a/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java b/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java new file mode 100644 index 0000000000..0760c4efb6 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.i18n; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.junit.Test; + +import sonia.scm.repository.Changeset; + +import static org.junit.Assert.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; +import java.util.MissingResourceException; + +/** + * + * @author Sebastian Sdorra + */ +public class I18nMessagesTest +{ + + /** + * Method description + * + */ + @Test + public void testI18n() + { + TestMessages msg = I18nMessages.get(TestMessages.class); + + assertEquals("Normal Key", msg.normalKey); + assertEquals("Key with Annotation", msg.keyWithAnnotation); + assertNull(msg.someObject); + assertNotNull(msg.bundle); + assertEquals(Locale.ENGLISH, msg.locale); + } + + /** + * Method description + * + */ + @Test + public void testI18nOtherLanguage() + { + TestMessages msg = I18nMessages.get(TestMessages.class, Locale.GERMANY); + + assertEquals("Normaler Schlüssel", msg.normalKey); + assertEquals("Schlüssel mit Annotation", msg.keyWithAnnotation); + assertNull(msg.someObject); + assertNotNull(msg.bundle); + assertEquals(Locale.GERMANY, msg.locale); + } + + /** + * Method description + * + */ + @Test(expected = MissingResourceException.class) + public void testMissingBundle() + { + Changeset msg = I18nMessages.get(Changeset.class); + } +} diff --git a/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java b/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java new file mode 100644 index 0000000000..e1901eace9 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/i18n/TestMessages.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.i18n; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; + +/** + * + * @author Sebastian Sdorra + */ +public class TestMessages +{ + + /** Field description */ + public Bundle bundle; + + /** Field description */ + @I18n("key_with_annotation") + public String keyWithAnnotation; + + /** Field description */ + public Locale locale; + + /** Field description */ + public String normalKey; + + /** Field description */ + public Integer someObject; +} diff --git a/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties new file mode 100644 index 0000000000..06a80bcae0 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties @@ -0,0 +1,30 @@ +# Copyright (c) 2010, Sebastian Sdorra +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of SCM-Manager; nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# http://bitbucket.org/sdorra/scm-manager +# +key_with_annotation = Key with Annotation +normalKey = Normal Key \ No newline at end of file diff --git a/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties new file mode 100644 index 0000000000..c370bc25d8 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties @@ -0,0 +1,30 @@ +# Copyright (c) 2010, Sebastian Sdorra +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of SCM-Manager; nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# http://bitbucket.org/sdorra/scm-manager +# +key_with_annotation = Schl\u00fcssel mit Annotation +normalKey = Normaler Schl\u00fcssel \ No newline at end of file