Commits

Sebastian Sdorra  committed 10a4623

added small i18n framework

  • Participants
  • Parent commits 993c2eb
  • Branches 1.x

Comments (0)

Files changed (8)

File scm-core/src/main/java/sonia/scm/i18n/Bundle.java

  */
 
 
+
 package sonia.scm.i18n;
 
 //~--- non-JDK imports --------------------------------------------------------
 import java.text.MessageFormat;
 
 import java.util.Locale;
+import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
 /**
     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;
 }

File scm-core/src/main/java/sonia/scm/i18n/I18n.java

+/**
+ * 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();
+}

File scm-core/src/main/java/sonia/scm/i18n/I18nException.java

+/**
+ * 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);
+  }
+}

File scm-core/src/main/java/sonia/scm/i18n/I18nMessages.java

+/**
+ * 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<CacheKey, Object> 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 <T> type of message class
+   *
+   * @return instance of message class
+   */
+  public static <T> T get(Class<T> 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 <T> type of the message class
+   *
+   * @return instance of message class
+   */
+  public synchronized static <T> T get(Class<T> 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 <T>
+   *
+   * @return
+   */
+  private static <T> T createInstance(Class<T> 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;
+  }
+}

File scm-core/src/test/java/sonia/scm/i18n/I18nMessagesTest.java

+/**
+ * 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);
+  }
+}

File scm-core/src/test/java/sonia/scm/i18n/TestMessages.java

+/**
+ * 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;
+}

File scm-core/src/test/resources/sonia/scm/i18n/TestMessages.properties

+# 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

File scm-core/src/test/resources/sonia/scm/i18n/TestMessages_de_DE.properties

+# 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