Merge branch 'next' into new-database-38 new-database-38
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 12 Jan 2013 16:41:27 +0000 (17:41 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 14 Jan 2013 05:32:50 +0000 (06:32 +0100)
Conflicts:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/web/SearchPage.java

21 files changed:
pom.xml
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/UpdateChecker.java
src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java
src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java
src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/template/ParserFilter.java
src/main/java/net/pterodactylus/sone/text/SoneTextParser.java
src/main/java/net/pterodactylus/sone/web/EditProfilePage.java
src/main/java/net/pterodactylus/sone/web/SearchPage.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java
src/main/java/net/pterodactylus/sone/web/page/FreenetTemplatePage.java
src/main/resources/i18n/sone.fr.properties
src/main/resources/i18n/sone.ja.properties [new file with mode: 0644]
src/main/resources/i18n/sone.no.properties
src/main/resources/i18n/sone.pl.properties
src/main/resources/i18n/sone.ru.properties
src/main/resources/templates/editProfile.html
src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java

diff --git a/pom.xml b/pom.xml
index 9ea02ae..0e2cc60 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -2,12 +2,12 @@
        <modelVersion>4.0.0</modelVersion>
        <groupId>net.pterodactylus</groupId>
        <artifactId>sone</artifactId>
-       <version>0.8.2</version>
+       <version>0.8.4</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.12</version>
+                       <version>0.12.3-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index 0b1379a..1bb638a 100644 (file)
@@ -61,6 +61,7 @@ import net.pterodactylus.util.config.ConfigurationException;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.service.AbstractService;
+import net.pterodactylus.util.thread.NamedThreadFactory;
 import net.pterodactylus.util.thread.Ticker;
 import net.pterodactylus.util.validation.EqualityValidator;
 import net.pterodactylus.util.validation.IntegerRangeValidator;
@@ -79,6 +80,9 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        /** The logger. */
        private static final Logger logger = Logging.getLogger(Core.class);
 
+       /** The start time. */
+       private final long startupTime = System.currentTimeMillis();
+
        /** The options. */
        private final Options options = new Options();
 
@@ -110,7 +114,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        private final ImageInserter imageInserter;
 
        /** Sone downloader thread-pool. */
-       private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10);
+       private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10, new NamedThreadFactory("Sone Downloader %2$d"));
 
        /** The update checker. */
        private final UpdateChecker updateChecker;
@@ -228,6 +232,15 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
        //
 
        /**
+        * Returns the time Sone was started.
+        *
+        * @return The startup time (in milliseconds since Jan 1, 1970 UTC)
+        */
+       public long getStartupTime() {
+               return startupTime;
+       }
+
+       /**
         * Sets the configuration to use. This will automatically save the current
         * configuration to the given configuration.
         *
@@ -1056,8 +1069,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                                                if (!storedPosts.contains(post)) {
                                                        if (post.getTime() < getSoneFollowingTime(sone)) {
                                                                knownPosts.add(post.getId());
+                                                               post.setKnown(true);
                                                        } else if (!knownPosts.contains(post.getId())) {
-                                                               sone.setKnown(false);
                                                                coreListenerManager.fireNewPostFound(post);
                                                        }
                                                }
@@ -1081,8 +1094,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                                                if (!storedReplies.contains(reply)) {
                                                        if (reply.getTime() < getSoneFollowingTime(sone)) {
                                                                knownReplies.add(reply.getId());
+                                                               reply.setKnown(true);
                                                        } else if (!knownReplies.contains(reply.getId())) {
-                                                               reply.setKnown(false);
                                                                coreListenerManager.fireNewReplyFound(reply);
                                                        }
                                                }
@@ -1429,6 +1442,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         * @return The created post
         */
        public Post createPost(Sone sone, Sone recipient, long time, String text) {
+               Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.length(), 0).check();
                if (!sone.isLocal()) {
                        logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone));
                        return null;
@@ -1569,6 +1583,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         * @return The created reply
         */
        public PostReply createReply(Sone sone, Post post, long time, String text) {
+               Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.trim().length(), 0).check();
                if (!sone.isLocal()) {
                        logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone));
                        return null;
@@ -2260,7 +2275,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
         */
        @Override
        public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) {
-               new Thread(new Runnable() {
+               soneDownloaders.execute(new Runnable() {
 
                        @Override
                        @SuppressWarnings("synthetic-access")
@@ -2271,7 +2286,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis
                                soneDownloader.addSone(sone);
                                soneDownloader.fetchSone(sone);
                        }
-               }).start();
+               });
        }
 
        /**
index 003e276..3f4d235 100644 (file)
@@ -49,7 +49,7 @@ public class UpdateChecker {
        private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
 
        /** The current latest known edition. */
-       private static final int LATEST_EDITION = 49;
+       private static final int LATEST_EDITION = 55;
 
        /** The Freenet interface. */
        private final FreenetInterface freenetInterface;
index b3222be..c1ed953 100644 (file)
@@ -27,7 +27,6 @@ import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.freenet.fcp.Command.AccessType;
 import net.pterodactylus.sone.freenet.fcp.Command.ErrorResponse;
 import net.pterodactylus.sone.freenet.fcp.Command.Response;
-import net.pterodactylus.sone.freenet.fcp.FcpException;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.validation.Validation;
 import freenet.pluginmanager.FredPluginFCP;
@@ -172,9 +171,9 @@ public class FcpInterface {
                        try {
                                Response response = command.execute(parameters, data, AccessType.values()[accessType]);
                                sendReply(pluginReplySender, identifier, response);
-                       } catch (FcpException fe1) {
+                       } catch (Exception e1) {
                                logger.log(Level.WARNING, "Could not process FCP command “%s”.", command);
-                               sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + fe1.getMessage()));
+                               sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + e1.getMessage()));
                        }
                } catch (PluginNotFoundException pnfe1) {
                        logger.log(Level.WARNING, "Could not find destination plugin: " + pluginReplySender);
index 3c3c9f9..568a637 100644 (file)
@@ -59,21 +59,28 @@ public class IdentityManager extends AbstractService {
        private final WebOfTrustConnector webOfTrustConnector;
 
        /** The context to filter for. */
-       private volatile String context;
+       private final String context;
 
        /** The currently known own identities. */
        /* synchronize access on syncObject. */
-       private Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+       private final Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+
+       /** The last time all identities were loaded. */
+       private volatile long identitiesLastLoaded;
 
        /**
         * Creates a new identity manager.
         *
         * @param webOfTrustConnector
         *            The Web of Trust connector
+        * @param context
+        *            The context to focus on (may be {@code null} to ignore
+        *            contexts)
         */
-       public IdentityManager(WebOfTrustConnector webOfTrustConnector) {
+       public IdentityManager(WebOfTrustConnector webOfTrustConnector, String context) {
                super("Sone Identity Manager", false);
                this.webOfTrustConnector = webOfTrustConnector;
+               this.context = context;
        }
 
        //
@@ -105,13 +112,13 @@ public class IdentityManager extends AbstractService {
        //
 
        /**
-        * Sets the context to filter own identities and trusted identities for.
+        * Returns the last time all identities were loaded.
         *
-        * @param context
-        *            The context to filter for, or {@code null} to not filter
+        * @return The last time all identities were loaded (in milliseconds since
+        *         Jan 1, 1970 UTC)
         */
-       public void setContext(String context) {
-               this.context = context;
+       public long getIdentitiesLastLoaded() {
+               return identitiesLastLoaded;
        }
 
        /**
@@ -179,19 +186,23 @@ public class IdentityManager extends AbstractService {
 
                                /* load trusted identities. */
                                for (OwnIdentity ownIdentity : ownIdentities) {
+                                       currentOwnIdentities.put(ownIdentity.getId(), ownIdentity);
+                                       Map<String, Identity> identities = new HashMap<String, Identity>();
+                                       currentIdentities.put(ownIdentity, identities);
+
+                                       /* if the context doesn’t match, skip getting trusted identities. */
                                        if ((context != null) && !ownIdentity.hasContext(context)) {
                                                continue;
                                        }
-                                       currentOwnIdentities.put(ownIdentity.getId(), ownIdentity);
 
+                                       /* load trusted identities. */
                                        Set<Identity> trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context);
-                                       Map<String, Identity> identities = new HashMap<String, Identity>();
-                                       currentIdentities.put(ownIdentity, identities);
                                        for (Identity identity : trustedIdentities) {
                                                identities.put(identity.getId(), identity);
                                        }
                                }
                                identitiesLoaded = true;
+                               identitiesLastLoaded = System.currentTimeMillis();
                        } catch (WebOfTrustException wote1) {
                                logger.log(Level.WARNING, "WoT has disappeared!", wote1);
                        }
index 53a45b1..c4e8d89 100644 (file)
@@ -144,8 +144,8 @@ public class WebOfTrustConnector {
                        identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields));
                        identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields));
                        Integer trust = Numbers.safeParseInteger(fields.get("Trust" + identityCounter), null);
-                       int score = Numbers.safeParseInteger(fields.get("Score" + identityCounter));
-                       int rank = Numbers.safeParseInteger(fields.get("Rank" + identityCounter));
+                       int score = Numbers.safeParseInteger(fields.get("Score" + identityCounter), 0);
+                       int rank = Numbers.safeParseInteger(fields.get("Rank" + identityCounter), 0);
                        identity.setTrust(ownIdentity, new Trust(trust, score, rank));
                        identities.add(identity);
                }
index 42e2fe1..1bc8b5a 100644 (file)
@@ -86,7 +86,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 8, 2);
+       public static final Version VERSION = new Version(0, 8, 4);
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(SonePlugin.class);
@@ -188,8 +188,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                        /* create web of trust connector. */
                        PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
                        webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
-                       identityManager = new IdentityManager(webOfTrustConnector);
-                       identityManager.setContext("Sone");
+                       identityManager = new IdentityManager(webOfTrustConnector, "Sone");
 
                        /* create Sone database. */
                        Database soneDatabase = new MemoryDatabase();
index 1936656..b8af831 100644 (file)
@@ -20,7 +20,9 @@ package net.pterodactylus.sone.template;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -213,7 +215,12 @@ public class ParserFilter implements Filter {
         *            The part to render
         */
        private void render(Writer writer, LinkPart linkPart) {
-               renderLink(writer, "/?_CHECKED_HTTP_=" + linkPart.getLink(), linkPart.getText(), linkPart.getTitle(), "internet");
+               try {
+                       renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), linkPart.getTitle(), "internet");
+               } catch (UnsupportedEncodingException uee1) {
+                       /* not possible for UTF-8. */
+                       throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1);
+               }
        }
 
        /**
@@ -249,7 +256,13 @@ public class ParserFilter implements Filter {
                        for (Part part : parts) {
                                excerpt.append(part.getText());
                                if (excerpt.length() > 20) {
-                                       excerpt.setLength(20);
+                                       int lastSpace = excerpt.lastIndexOf(" ", 20);
+                                       if (lastSpace > -1) {
+                                               excerpt.setLength(lastSpace);
+                                       } else {
+                                               excerpt.setLength(20);
+                                       }
+                                       excerpt.append("…");
                                        break;
                                }
                        }
index 19db65b..2c209d5 100644 (file)
@@ -56,28 +56,50 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
        private enum LinkType {
 
                /** Link is a KSK. */
-               KSK,
+               KSK("KSK@"),
 
                /** Link is a CHK. */
-               CHK,
+               CHK("CHK@"),
 
                /** Link is an SSK. */
-               SSK,
+               SSK("SSK@"),
 
                /** Link is a USK. */
-               USK,
+               USK("USK@"),
 
                /** Link is HTTP. */
-               HTTP,
+               HTTP("http://"),
 
                /** Link is HTTPS. */
-               HTTPS,
+               HTTPS("https://"),
 
                /** Link is a Sone. */
-               SONE,
+               SONE("sone://"),
 
                /** Link is a post. */
-               POST,
+               POST("post://");
+
+               /** The scheme identifying this link type. */
+               private final String scheme;
+
+               /**
+                * Creates a new link type identified by the given scheme.
+                *
+                * @param scheme
+                *            The scheme of the link type
+                */
+               private LinkType(String scheme) {
+                       this.scheme = scheme;
+               }
+
+               /**
+                * Returns the scheme of this link type.
+                *
+                * @return The scheme of this link type
+                */
+               public String getScheme() {
+                       return scheme;
+               }
 
        }
 
@@ -201,6 +223,20 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                        }
                                        lineComplete = false;
 
+                                       Matcher matcher = whitespacePattern.matcher(line);
+                                       int nextSpace = matcher.find(0) ? matcher.start() : line.length();
+                                       String link = line.substring(0, nextSpace);
+                                       String name = link;
+                                       logger.log(Level.FINER, String.format("Found link: %s", link));
+                                       logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk));
+
+                                       /* if there is no text after the scheme, it’s not a link! */
+                                       if (link.equals(linkType.getScheme())) {
+                                               parts.add(new PlainTextPart(linkType.getScheme()));
+                                               line = line.substring(linkType.getScheme().length());
+                                               continue;
+                                       }
+
                                        if (linkType == LinkType.SONE) {
                                                if (line.length() >= (7 + 43)) {
                                                        String soneId = line.substring(7, 50);
@@ -236,12 +272,6 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                                }
                                                continue;
                                        }
-                                       Matcher matcher = whitespacePattern.matcher(line);
-                                       int nextSpace = matcher.find(0) ? matcher.start() : line.length();
-                                       String link = line.substring(0, nextSpace);
-                                       String name = link;
-                                       logger.log(Level.FINER, String.format("Found link: %s", link));
-                                       logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk));
 
                                        if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
                                                FreenetURI uri;
index a1b759b..51a72b8 100644 (file)
@@ -77,7 +77,7 @@ public class EditProfilePage extends SoneTemplatePage {
                                birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim());
                                birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim());
                                birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim());
-                               avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatar-id", 36);
+                               avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatarId", 36);
                                profile.setFirstName(firstName.length() > 0 ? firstName : null);
                                profile.setMiddleName(middleName.length() > 0 ? middleName : null);
                                profile.setLastName(lastName.length() > 0 ? lastName : null);
@@ -139,7 +139,7 @@ public class EditProfilePage extends SoneTemplatePage {
                templateContext.set("birthDay", birthDay);
                templateContext.set("birthMonth", birthMonth);
                templateContext.set("birthYear", birthYear);
-               templateContext.set("avatar-id", avatarId);
+               templateContext.set("avatarId", avatarId);
                templateContext.set("fields", fields);
        }
 
index 691453d..524e501 100644 (file)
@@ -111,6 +111,26 @@ public class SearchPage extends SoneTemplatePage {
                        throw new RedirectException("index.html");
                }
 
+               /* check for a couple of shortcuts. */
+               if (phrases.size() == 1) {
+                       String phrase = phrases.get(0).getPhrase();
+
+                       /* is it a Sone ID? */
+                       redirectIfNotNull(getSoneId(phrase), "viewSone.html?sone=");
+
+                       /* is it a post ID? */
+                       redirectIfNotNull(getPostId(phrase), "viewPost.html?post=");
+
+                       /* is it a reply ID? show the post. */
+                       redirectIfNotNull(getReplyPostId(phrase), "viewPost.html?post=");
+
+                       /* is it an album ID? */
+                       redirectIfNotNull(getAlbumId(phrase), "imageBrowser.html?album=");
+
+                       /* is it an image ID? */
+                       redirectIfNotNull(getImageId(phrase), "imageBrowser.html?image=");
+               }
+
                Collection<Sone> sones = webInterface.getCore().getSones();
                Set<Hit<Sone>> soneHits = getHits(sones, phrases, SoneStringGenerator.COMPLETE_GENERATOR);
 
@@ -271,6 +291,91 @@ public class SearchPage extends SoneTemplatePage {
        }
 
        /**
+        * Throws a
+        * {@link net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException}
+        * if the given object is not {@code null}, appending the object to the
+        * given target URL.
+        *
+        * @param object
+        *            The object on which to redirect
+        * @param target
+        *            The target of the redirect
+        * @throws RedirectException
+        *             if {@code object} is not {@code null}
+        */
+       private static void redirectIfNotNull(String object, String target) throws RedirectException {
+               if (object != null) {
+                       throw new RedirectException(target + object);
+               }
+       }
+
+       /**
+        * If the given phrase contains a Sone ID (optionally prefixed by
+        * “sone://”), returns said Sone ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a Sone ID
+        * @return The Sone ID, or {@code null}
+        */
+       private String getSoneId(String phrase) {
+               String soneId = phrase.startsWith("sone://") ? phrase.substring(7) : phrase;
+               return (webInterface.getCore().getSone(soneId, false) != null) ? soneId : null;
+       }
+
+       /**
+        * If the given phrase contains a post ID (optionally prefixed by
+        * “post://”), returns said post ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a post ID
+        * @return The post ID, or {@code null}
+        */
+       private String getPostId(String phrase) {
+               String postId = phrase.startsWith("post://") ? phrase.substring(7) : phrase;
+               return (webInterface.getCore().getPost(postId, false) != null) ? postId : null;
+       }
+
+       /**
+        * If the given phrase contains a reply ID (optionally prefixed by
+        * “reply://”), returns the ID of the post the reply belongs to, otherwise
+        * return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is a reply ID
+        * @return The reply’s post ID, or {@code null}
+        */
+       private String getReplyPostId(String phrase) {
+               String replyId = phrase.startsWith("reply://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getReply(replyId, false) != null) ? webInterface.getCore().getReply(replyId, false).getPost().getId() : null;
+       }
+
+       /**
+        * If the given phrase contains an album ID (optionally prefixed by
+        * “album://”), returns said album ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is an album ID
+        * @return The album ID, or {@code null}
+        */
+       private String getAlbumId(String phrase) {
+               String albumId = phrase.startsWith("album://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getAlbum(albumId, false) != null) ? albumId : null;
+       }
+
+       /**
+        * If the given phrase contains an image ID (optionally prefixed by
+        * “image://”), returns said image ID, otherwise return {@code null}.
+        *
+        * @param phrase
+        *            The phrase that maybe is an image ID
+        * @return The image ID, or {@code null}
+        */
+       private String getImageId(String phrase) {
+               String imageId = phrase.startsWith("image://") ? phrase.substring(8) : phrase;
+               return (webInterface.getCore().getImage(imageId, false) != null) ? imageId : null;
+       }
+
+       /**
         * Converts a given object into a {@link String}.
         *
         * @param <T>
index b5f6dfb..75b5f50 100644 (file)
@@ -103,18 +103,13 @@ import net.pterodactylus.sone.web.ajax.UntrustAjaxPage;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.sone.web.page.PageToadlet;
 import net.pterodactylus.sone.web.page.PageToadletFactory;
-import net.pterodactylus.util.cache.Cache;
-import net.pterodactylus.util.cache.CacheException;
-import net.pterodactylus.util.cache.CacheItem;
-import net.pterodactylus.util.cache.DefaultCacheItem;
-import net.pterodactylus.util.cache.MemoryCache;
-import net.pterodactylus.util.cache.ValueRetriever;
 import net.pterodactylus.util.collection.SetBuilder;
 import net.pterodactylus.util.collection.filter.Filters;
 import net.pterodactylus.util.logging.Logging;
 import net.pterodactylus.util.notify.Notification;
 import net.pterodactylus.util.notify.NotificationManager;
 import net.pterodactylus.util.notify.TemplateNotification;
+import net.pterodactylus.util.template.ClassPathTemplateProvider;
 import net.pterodactylus.util.template.CollectionSortFilter;
 import net.pterodactylus.util.template.ContainsFilter;
 import net.pterodactylus.util.template.DateFilter;
@@ -123,15 +118,13 @@ import net.pterodactylus.util.template.HtmlFilter;
 import net.pterodactylus.util.template.MatchFilter;
 import net.pterodactylus.util.template.ModFilter;
 import net.pterodactylus.util.template.PaginationFilter;
-import net.pterodactylus.util.template.Provider;
 import net.pterodactylus.util.template.ReflectionAccessor;
 import net.pterodactylus.util.template.ReplaceFilter;
 import net.pterodactylus.util.template.StoreFilter;
 import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
 import net.pterodactylus.util.template.TemplateContextFactory;
-import net.pterodactylus.util.template.TemplateException;
 import net.pterodactylus.util.template.TemplateParser;
+import net.pterodactylus.util.template.TemplateProvider;
 import net.pterodactylus.util.template.XmlFilter;
 import net.pterodactylus.util.thread.Ticker;
 import net.pterodactylus.util.version.Version;
@@ -222,7 +215,6 @@ public class WebInterface implements CoreListener {
         * @param sonePlugin
         *            The Sone plugin
         */
-       @SuppressWarnings("synthetic-access")
        public WebInterface(SonePlugin sonePlugin) {
                this.sonePlugin = sonePlugin;
                formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
@@ -261,8 +253,8 @@ public class WebInterface implements CoreListener {
                templateContextFactory.addFilter("unique", new UniqueElementFilter());
                templateContextFactory.addFilter("mod", new ModFilter());
                templateContextFactory.addFilter("paginate", new PaginationFilter());
-               templateContextFactory.addProvider(Provider.TEMPLATE_CONTEXT_PROVIDER);
-               templateContextFactory.addProvider(new ClassPathTemplateProvider());
+               templateContextFactory.addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER);
+               templateContextFactory.addProvider(new ClassPathTemplateProvider(WebInterface.class, "/templates/"));
                templateContextFactory.addTemplateObject("webInterface", this);
                templateContextFactory.addTemplateObject("formPassword", formPassword);
 
@@ -1011,66 +1003,4 @@ public class WebInterface implements CoreListener {
                notificationManager.addNotification(imageInsertFailedNotification);
        }
 
-       /**
-        * Template provider implementation that uses
-        * {@link WebInterface#createReader(String)} to load templates for
-        * inclusion.
-        *
-        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
-        */
-       private class ClassPathTemplateProvider implements Provider {
-
-               /** Cache for templates. */
-               private final Cache<String, Template> templateCache = new MemoryCache<String, Template>(new ValueRetriever<String, Template>() {
-
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public CacheItem<Template> retrieve(String key) throws CacheException {
-                               Template template = findTemplate(key);
-                               if (template != null) {
-                                       return new DefaultCacheItem<Template>(template);
-                               }
-                               return null;
-                       }
-               });
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               @SuppressWarnings("synthetic-access")
-               public Template getTemplate(TemplateContext templateContext, String templateName) {
-                       try {
-                               return templateCache.get(templateName);
-                       } catch (CacheException ce1) {
-                               logger.log(Level.WARNING, String.format("Could not get template for %s!", templateName), ce1);
-                               return null;
-                       }
-               }
-
-               /**
-                * Locates a template in the class path.
-                *
-                * @param templateName
-                *            The name of the template to load
-                * @return The loaded template, or {@code null} if no template could be
-                *         found
-                */
-               @SuppressWarnings("synthetic-access")
-               private Template findTemplate(String templateName) {
-                       Reader templateReader = createReader("/templates/" + templateName);
-                       if (templateReader == null) {
-                               return null;
-                       }
-                       Template template = null;
-                       try {
-                               template = TemplateParser.parse(templateReader);
-                       } catch (TemplateException te1) {
-                               logger.log(Level.WARNING, String.format("Could not parse template “%s” for inclusion!", templateName), te1);
-                       }
-                       return template;
-               }
-
-       }
-
 }
index 22eeec8..910abd5 100644 (file)
 
 package net.pterodactylus.sone.web.ajax;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 import java.net.URI;
 
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.web.WebInterface;
 import net.pterodactylus.sone.web.page.FreenetPage;
 import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.json.JsonObject;
 import net.pterodactylus.util.json.JsonUtils;
 import net.pterodactylus.util.web.Page;
@@ -218,8 +222,12 @@ public abstract class JsonPage implements FreenetPage {
                                return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required")));
                        }
                }
-               JsonObject jsonObject = createJsonObject(request);
-               return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject));
+               try {
+                       JsonObject jsonObject = createJsonObject(request);
+                       return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject));
+               } catch (Exception e1) {
+                       return response.setStatusCode(500).setStatusText(e1.getMessage()).setContentType("text/plain").write(dumpStackTrace(e1));
+               }
        }
 
        /**
@@ -230,4 +238,36 @@ public abstract class JsonPage implements FreenetPage {
                return false;
        }
 
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Returns a byte array containing the stack trace of the given throwable.
+        *
+        * @param t
+        *            The throwable whose stack trace to dump into an array
+        * @return The array with the stack trace, or an empty array if the stack
+        *         trace could not be dumped
+        */
+       private static byte[] dumpStackTrace(Throwable t) {
+               ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+               OutputStreamWriter writer = null;
+               PrintWriter printWriter = null;
+               try {
+                       writer = new OutputStreamWriter(byteArrayOutputStream, "uTF-8");
+                       printWriter = new PrintWriter(writer);
+                       t.printStackTrace(printWriter);
+                       byteArrayOutputStream.flush();
+                       return byteArrayOutputStream.toByteArray();
+               } catch (IOException ioe1) {
+                       /* quite not possible. */
+                       return new byte[0];
+               } finally {
+                       Closer.close(printWriter);
+                       Closer.close(writer);
+                       Closer.close(byteArrayOutputStream);
+               }
+       }
+
 }
index 97ee53b..67884bd 100644 (file)
@@ -156,7 +156,7 @@ public class FreenetTemplatePage implements FreenetPage, LinkEnabledCallback {
                        long start = System.nanoTime();
                        processTemplate(request, templateContext);
                        long finish = System.nanoTime();
-                       logger.log(Level.FINEST, "Template was rendered in %dms.", ((finish - start) / 1000) / 1000.0);
+                       logger.log(Level.FINEST, String.format("Template was rendered in %.2fms.", ((finish - start) / 1000) / 1000.0));
                } catch (RedirectException re1) {
                        return new RedirectResponse(re1.getTarget());
                }
index 7da1634..12a44d4 100644 (file)
@@ -16,21 +16,21 @@ Navigation.Menu.Sone.Item.EditProfile.Name=Editer le profil
 Navigation.Menu.Sone.Item.EditProfile.Tooltip=Editer le profil de votre Sone
 Navigation.Menu.Sone.Item.ImageBrowser.Name=Images
 Navigation.Menu.Sone.Item.ImageBrowser.Tooltip=Gérer vos images
-Navigation.Menu.Sone.Item.DeleteSone.Name=Efface Sone
-Navigation.Menu.Sone.Item.DeleteSone.Tooltip=Efface les Sones actuels
+Navigation.Menu.Sone.Item.DeleteSone.Name=Effacer ce Sone
+Navigation.Menu.Sone.Item.DeleteSone.Tooltip=Effacer les Sones actuels
 Navigation.Menu.Sone.Item.Logout.Name=Déconnexion
 Navigation.Menu.Sone.Item.Logout.Tooltip=Vous déconnecte de votre Sone actuel
 Navigation.Menu.Sone.Item.Options.Name=Options
 Navigation.Menu.Sone.Item.Options.Tooltip=Options concernant le plugin Sone
-Navigation.Menu.Sone.Item.Rescue.Name=Réparation
-Navigation.Menu.Sone.Item.Rescue.Tooltip=Réparer Sone
+Navigation.Menu.Sone.Item.Rescue.Name=Récupération
+Navigation.Menu.Sone.Item.Rescue.Tooltip=Récupération de votre Sone
 Navigation.Menu.Sone.Item.About.Name=A propos
 Navigation.Menu.Sone.Item.About.Tooltip=Informations à propos de Sone
 
 Page.About.Title=A propos de - Sone
 Page.About.Page.Title=A propos
-Page.About.Flattr.Description=Si vous aimez Sone et aue vous voulez me récompenser, vous pouvez utiliser le bouton Flattr au bas de la page de recherche. Flattr est un système de micro-payement non anonyme comparable au principe du pouboire via internet où le montant que chaque utilisateur dépense est limité (le minimum est de 2€ par mois). Plus d'informations sur {link}flattr.com{/link}.
-Page.About.Homepage.Title=Paage d'accueil
+Page.About.Flattr.Description=Si vous aimez Sone et que vous voulez me récompenser, vous pouvez utiliser le bouton Flattr au bas de la page de recherche. Flattr est un système de micro-payement non anonyme comparable au principe du pourboire via internet où le montant que chaque utilisateur dépense est limité (le minimum est de 2€ par mois). Plus d'informations sur {link}flattr.com{/link}.
+Page.About.Homepage.Title=Page d'accueil
 Page.About.Homepage.Description=Vous pouvez trouver plus d'informations ainsi que le code source de Sone sur la {link}page d'accueil{/link}.
 Page.About.License.Title=Licence
 
@@ -39,9 +39,9 @@ Page.Options.Page.Title=Options
 Page.Options.Page.Description=Ces options influencent le comportement de l'exécution du plugin Sone
 Page.Options.Section.SoneSpecificOptions.Title=Options spécifiques à Sone
 Page.Options.Section.SoneSpecificOptions.NotLoggedIn=Ces options sont uniquement disponibles si vous êtes {link}connecté{/link}.
-Page.Options.Section.SoneSpecificOptions.LoggedIn=Ces options sont uniquement disponibles pour le Sone entant que tel vous êtes enregistré.
+Page.Options.Section.SoneSpecificOptions.LoggedIn=Ces options sont uniquement disponibles pour le Sone actif.
 Page.Options.Option.AutoFollow.Description=si un nouveau Sone est découvert, automatiquement le suivre. Veuillez noter que cela suivra uniquement les Sones qui seront découverts après que cette option soit activée!
-Page.Options.Option.EnableSoneInsertNotifications.Description=Si activé, cela affichera les notifications chaque fois que votre Sone insère un message ou qu'il a fini d'insérer.
+Page.Options.Option.EnableSoneInsertNotifications.Description=Si cette option est activée, Sone affichera les notifications chaque fois que vous insèrerez un message ou qu'il sera correctement inséré.
 Page.Options.Option.ShowNotificationNewSones.Description=Affiche les notifications des nouveaux Sones.
 Page.Options.Option.ShowNotificationNewPosts.Description=Affiche une notification lors d'un nouveau message.
 Page.Options.Option.ShowNotificationNewReplies.Description=Affiche une notification lors d'une nouvelle réponse.
@@ -54,24 +54,24 @@ Page.Options.Option.ShowAvatars.Trusted.Description=Ne montrer que les avatars d
 Page.Options.Option.ShowAvatars.Always.Description=Montre tout le temps les avatars personnalisés.Attention: certains avatars peuvent être offensants !
 Page.Options.Section.RuntimeOptions.Title=Comportement runtime
 Page.Options.Option.InsertionDelay.Description=Le nombre de secondes que l'inserteur de Sone attends après une modification d'un Sone avant qu'elle soit insérée.
-Page.Options.Option.PostsPerPage.Description=Le nombre de message à afficher par page avant que les boutons de contrôle de page sont affichés.
-Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
+Page.Options.Option.PostsPerPage.Description=Le nombre de message à afficher par page avant que les boutons de pagination soit affichés.
+Page.Options.Option.ImagesPerPage.Description=Le nombre de message à afficher par page avant que les boutons de pagination soit affichés.
 Page.Options.Option.CharactersPerPost.Description=Le nombre de caractères à afficher par message avant que le lien proposant de voir l'intégralité ne soit proposé (-1 pour désactiver). La taille du composant est determinée par l'option ci-desssous.
-Page.Options.Option.PostCutOffLength.Description=Le nombre de charactère à afficher avant que le message ne soit considéré comme trops long. (voir option du dessus)
-Page.Options.Option.RequireFullAccess.Description=Que ce soit pour refuser l'accès à Sone à tout hôte à qui un accès complet n'a pas été accordé.
+Page.Options.Option.PostCutOffLength.Description=Le nombre de charactère à afficher avant que le message ne soit considéré comme trop long. (voir option du dessus)
+Page.Options.Option.RequireFullAccess.Description=Pour refuser l'accès à Sone à tout hôte à qui un accès complet n'a pas été accordé.
 Page.Options.Section.TrustOptions.Title=Réglages de confiance
 Page.Options.Option.PositiveTrust.Description=La quantité de note de confiance positive que vous voulez assigner à d'autres Sones en cochant la case en dessous d'un message ou d'une réponse.
 Page.Options.Option.NegativeTrust.Description=La quantité de note de confiance que vous voulez assigner à d'autres Sones en cliquand le X rouge en dessous d'un message ou d'une réponse. Cette valeur devrait être négative.
 Page.Options.Option.TrustComment.Description=Le commentaire qui sera mis dans le web of trust pour chaque note de confiance que vous assignez de Sone.
 Page.Options.Section.FcpOptions.Title=Réglages de l'Interface FCP
-Page.Options.Option.FcpInterfaceActive.Description=Activer l'interface FCP aafin de permettre à d'autres plugins et clients à distance d'accéder votre plugin Sone.
+Page.Options.Option.FcpInterfaceActive.Description=Activer l'interface FCP afin de permettre à d'autres plugins et clients à distance d'accéder votre plugin Sone.
 Page.Options.Option.FcpFullAccessRequired.Description=Requière une connexion FCP d'hôtes autorisés (Veuillez voir votre {link}configuration du noeud, section “FCP”{/link})
 Page.Options.Option.FcpFullAccessRequired.Value.No=Non
 Page.Options.Option.FcpFullAccessRequired.Value.Writing=Pour accès à l'écriture
 Page.Options.Option.FcpFullAccessRequired.Value.Always=toujours
 Page.Options.Section.Cleaning.Title=Nettoyer
-Page.Options.Option.ClearOnNextRestart.Description=Réinitialiser la configuration du plugin Sone au prochain redémarrage. Attention! {strong}Cela détruira tous vos Sones{/strong}. Soyez sûr d'avoir sauvegardé tout ce dont vous avez besoin! Vous devez également choisir "vrai" à l'option suivante pour procéder à la réinitialisation.
-Page.Options.Option.ReallyClearOnNextRestart.Description=Choisir "oui" pour cette option si vous voulez vraiment{strong}vraiment{/strong} effacer la configuration au prochain redémarrage.
+Page.Options.Option.ClearOnNextRestart.Description=Réinitialiser la configuration du plugin Sone au prochain redémarrage. Attention! {strong}Cela détruira tous vos Sones{/strong}. Soyez sûr d'avoir sauvegardé tout ce dont vous avez besoin! Vous devez également choisir "Oui" à l'option suivante pour procéder à la réinitialisation.
+Page.Options.Option.ReallyClearOnNextRestart.Description=Choisir "Oui" pour cette option si vous voulez vraiment{strong}vraiment{/strong} effacer la configuration au prochain redémarrage.
 Page.Options.Warnings.ValueNotChanged=Cette option n'a pas été modifiée car la valeur que vous avez spécifiée n'est pas valide.
 Page.Options.Button.Save=Enregistrer
 
@@ -86,7 +86,7 @@ Page.CreateSone.Title=Créer un Sone - Sone
 
 Page.DeleteSone.Title=Effacer Sone - Sone
 Page.DeleteSone.Page.Title=Effacer Sone “{sone}”?
-Page.DeleteSone.Page.Description=Ceci n'effacera pas le Sone de Freenet(car ceci est impossible), celaa déconnectera seulement votre identité Web of Trust de Sone.
+Page.DeleteSone.Page.Description=Ceci n'effacera pas le Sone de Freenet(car ceci est impossible), cela déconnectera seulement votre identité Web of Trust de Sone.
 Page.DeleteSone.Button.Yes=Oui, effacer.
 Page.DeleteSone.Button.No=Non, ne pas effacer.
 
@@ -96,7 +96,7 @@ Page.Index.Label.Sender=Expéditeur:
 Page.Index.Button.Post=Envoyer!
 Page.Index.PostList.Title=Envoyer du contenu
 Page.Index.PostList.Text.NoPostYet=Personne n'a encore écrit de message. vous devriez probablement commencer maintenant!
-Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting!
+Page.Index.PostList.Text.FollowSomeSones=Peut-être ne suivez vous pas encore de Sones? Jetez un oeil à la liste des {link}Sones connus{/link} et suivez ceux qui pourrait vous intéresser!
 
 Page.New.Title=Nouveaux messages et réponses - Sone
 Page.New.Page.Title=Nouveaux messages et réponses
@@ -124,7 +124,7 @@ Page.KnownSones.Button.UnfollowAllSones=Ne plus suivre tous les Sones de cette p
 Page.EditProfile.Title=Editer le profil - Sone
 Page.EditProfile.Page.Title=Éditer le profil
 Page.EditProfile.Page.Description=Sur cette page, vous pouvez entrer les données de votre profil.
-Page.EditProfile.Page.Hint.Optionality=Veuillez noter que chaque champ de ce profil est optionnel! Vous n'êtes pas obligés de remplir quoi que ce soit ici! Tout ce que vous remplissez ici serq probqblement stocké dans Freenet pour très longtemps!
+Page.EditProfile.Page.Hint.Optionality=Veuillez noter que chaque champ de ce profil est optionnel! Vous n'êtes pas obligés de remplir quoi que ce soit ici! Tout ce que vous remplissez ici sera probablement stocké dans Freenet pour très longtemps!
 Page.EditProfile.Label.FirstName=Prénom:
 Page.EditProfile.Label.MiddleName=Prénom(s):
 Page.EditProfile.Label.LastName=Nom de famille:
@@ -136,7 +136,7 @@ Page.EditProfile.Avatar.Title=Avatar
 Page.EditProfile.Avatar.Description=Vous pouvez selectionner une de vos images comme avatar. Elle ne doit pas dépasser la taille de 64x64 pixels car c'est la taille la plus grande vue par les autre. (La taille 80x80 est réservée pour l'entête).
 Page.EditProfile.Avatar.Delete=Pas d'avatar
 Page.EditProfile.Fields.Title=Champs personnalisés
-Page.EditProfile.Fields.Description=Vous pouvez ajouter ici des champs personalisés à votre profil. Ces champs peuvent contenir tout ce que vous voulez et être aussi laconique ou aussi long que vous le souhaitez. Veuillez juste vous souvenir que quand on parle d'anonymat, parfois le moins, le mieux.
+Page.EditProfile.Fields.Description=Vous pouvez ajouter ici des champs personalisés à votre profil. Ces champs peuvent contenir tout ce que vous voulez et être aussi laconique ou aussi long que vous le souhaitez. Gardez à l'esprit qu'en terme d'anonymat, moins vous en dite, mieux c'est!
 Page.EditProfile.Fields.Button.Edit=Éditer
 Page.EditProfile.Fields.Button.MoveUp=Déplacer vers le haut
 Page.EditProfile.Fields.Button.MoveDown=Déplacer vers le bas
@@ -182,11 +182,11 @@ Page.ViewSone.UnknownSone.LinkToWebOfTrust=Ce Sone n'est pas connu, son profile
 Page.ViewSone.WriteAMessage=Vous pouvez écrire un message à ce Sone ici. Veuillez noter que tout le monde sera capable de lire ce message!
 Page.ViewSone.PostList.Title=Messages par {sone}
 Page.ViewSone.PostList.Text.NoPostYet=Ce Sone n'as encore rien envoyé.
-Page.ViewSone.Profile.Title=Profil
+Page.ViewSone.Profile.Title=Profile
 Page.ViewSone.Profile.Label.Name=Nom
 Page.ViewSone.Profile.Label.Albums=Albums
-Page.ViewSone.Profile.Albums.Text.All=All Albums
-Page.ViewSone.Profile.Name.WoTLink=Profil Web of trust
+Page.ViewSone.Profile.Albums.Text.All=Tous les albums
+Page.ViewSone.Profile.Name.WoTLink=Profile Web of trust
 Page.ViewSone.Replies.Title=Messages {sone} a répondu à
 
 Page.ViewPost.Title=Voir message - Sone
@@ -217,51 +217,51 @@ Page.FollowSone.Title=Suivre Sone - Sone
 
 Page.UnfollowSone.Title=Ne plus suivre Sone - Sone
 
-Page.ImageBrowser.Title=Image Browser - Sone
+Page.ImageBrowser.Title=Explorateur d'images - Sone
 Page.ImageBrowser.Album.Title=Album “{album}”
-Page.ImageBrowser.Album.Error.NotFound.Text=l'Album recherché n'est pas disponible. Il est possible qu'il ne soit pas encore mis à jour, ou qu'il est été dupprimé.
+Page.ImageBrowser.Album.Error.NotFound.Text=l'Album recherché n'est pas disponible. Il est possible qu'il ne soit pas encore mis à jour, ou qu'il est été supprimé.
 Page.ImageBrowser.Sone.Title=Albums de {sone}
 Page.ImageBrowser.Sone.Error.NotFound.Text=Le Sone recherché ne peut pas être trouvé. Il est possible qu'il ne soit pas encore téléchargé.
 Page.ImageBrowser.Header.Albums=Albums
 Page.ImageBrowser.Header.Images=Images
 Page.ImageBrowser.Link.All=Tous les Sones
-Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Creer un Album
-Page.ImageBrowser.Album.Edit.Title=Editer l'Album
-Page.ImageBrowser.Album.Delete.Title=Supprimer l'Album
-Page.ImageBrowser.Album.Label.AlbumImage=Album d'Images:
-Page.ImageBrowser.Album.Label.Title=Title:
+Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Creer un album
+Page.ImageBrowser.Album.Edit.Title=Editer l'album
+Page.ImageBrowser.Album.Delete.Title=Supprimer l'album
+Page.ImageBrowser.Album.Label.AlbumImage=Album d'images:
+Page.ImageBrowser.Album.Label.Title=Titre:
 Page.ImageBrowser.Album.Label.Description=Description:
-Page.ImageBrowser.Album.AlbumImage.Choose=Vhoisissez un Album d'images…
-Page.ImageBrowser.Album.Button.Save=Sauver l'Album
-Page.ImageBrowser.Album.Button.Delete=Supprimer l'Album
+Page.ImageBrowser.Album.AlbumImage.Choose=Choisissez un album d'images…
+Page.ImageBrowser.Album.Button.Save=Sauver l'album
+Page.ImageBrowser.Album.Button.Delete=Supprimer l'album
 Page.ImageBrowser.Image.Edit.Title=Editer l'image
-Page.ImageBrowser.Image.Title.Label=Title:
+Page.ImageBrowser.Image.Title.Label=Titre:
 Page.ImageBrowser.Image.Description.Label=Description:
 Page.ImageBrowser.Image.Button.MoveLeft=◀
-Page.ImageBrowser.Image.Button.Save=Save Image
+Page.ImageBrowser.Image.Button.Save=Sauvegarder l'image
 Page.ImageBrowser.Image.Button.MoveRight=►
-Page.ImageBrowser.Image.Delete.Title=Supprimer l'Image
-Page.ImageBrowser.Image.Button.Delete=Supprimer l'Image
+Page.ImageBrowser.Image.Delete.Title=Supprimer l'image
+Page.ImageBrowser.Image.Button.Delete=Supprimer l'image
 
-Page.CreateAlbum.Title=Créer un Album - Sone
-Page.CreateAlbum.Page.Title=Créer un Album
+Page.CreateAlbum.Title=Créer un album - Sone
+Page.CreateAlbum.Page.Title=Créer un album
 Page.CreateAlbum.Error.NameMissing=Vous avez oublié de donner un nom à votre Album.
 
 Page.UploadImage.Title=Insérer une Image - Sone
-Page.UploadImage.Error.InvalidImage=L'Image qu vous éssayez d'insérer n'est pas reconnue. Merci de n'insérer que des JPEG (*.jpg or *.jpeg), ou des PNG (*.png).
+Page.UploadImage.Error.InvalidImage=L'image qu vous essayez d'insérer n'est pas reconnue. Merci de n'insérer que des JPEG (*.jpg or *.jpeg), ou des PNG (*.png).
 
-Page.EditImage.Title=Editer l'Image - Sone
+Page.EditImage.Title=Editer l'image - Sone
 
-Page.DeleteImage.Title=Supprimer l'Image - Sone
-Page.DeleteImage.Page.Title=Supprimer l'Image
-Page.DeleteImage.Text.ImageWillBeGone=Ceci supprimera l'image “{image}” de l'Album “{album}”. Si elle a déjà été insérée sur Freenet elle ne pourra pas être supprimée. Voulez-vous supprimer cette Image ?
+Page.DeleteImage.Title=Supprimer l'image - Sone
+Page.DeleteImage.Page.Title=Supprimer l'image
+Page.DeleteImage.Text.ImageWillBeGone=Ceci supprimera l'image “{image}” de l'Album “{album}”. Si elle a déjà été insérée sur Freenet elle ne pourra pas être supprimée. Voulez-vous supprimer cette image ?
 Page.DeleteImage.Button.Yes=Oui, supprimer cette image.
 Page.DeleteImage.Button.No=Non, ne pas supprimer cette image.
 
-Page.EditAlbum.Title=Editer l'Album - Sone
+Page.EditAlbum.Title=Editer l'album - Sone
 
-Page.DeleteAlbum.Title=Supprimer l'Album - Sone
-Page.DeleteAlbum.Page.Title=Supprimer l'Album
+Page.DeleteAlbum.Title=Supprimer l'album - Sone
+Page.DeleteAlbum.Page.Title=Supprimer l'album
 Page.DeleteAlbum.Text.AlbumWillBeGone=Vous allez supprimer l'Album “{title}”. Etes vous certain de vouloir faire cela ?
 Page.DeleteAlbum.Button.Yes=Oui, supprimer cet album.
 Page.DeleteAlbum.Button.No=Non, ne pas supprimer cet album.
@@ -287,14 +287,14 @@ Page.Search.Text.SoneHits=Les Sones suivants correspondent aux termes de votre r
 Page.Search.Text.PostHits=Les messages suivants correspondent aux termes de votre recherche.
 Page.Search.Text.NoHits=Aucun Sone ou message ne correspond aux termes de votre recherche.
 
-Page.Rescue.Title=Secours Sone - Sone
-Page.Rescue.Page.Title=Secours Sone “{0}”
-Page.Rescue.Text.Description=Le Mode Secours vous permet de restaurer une version précédente de Sone. Cela peut être nécessaire si votre configuration est perdue
-Page.Rescue.Text.Procedure=Le Mode secours fonctionne en récupérant vos dernières insertions. Si une insertion est récupérée elle sera chargé dans votre Sone, vous permettant de reprendre le controle de vos messages, profile, et autres configurations (vous pouvez faire cela dans une seconde fenetre ou tabulation Sone). Si vos messages ne peuvent être récupérés, demandez au Mode Secours de récupérer les anciennes versions ci-dessous.
-Page.Rescue.Text.Fetching=Le Secouriste de Sone est en train de récupérer la version {0} de votre Sone.
-Page.Rescue.Text.Fetched=Le Secouriste de Sone a récupéré la version {0} de votre Sone. Merci de vérifire vos messages, réponses et profile. Si les informations vous conviennent, débloquez la version.
-Page.Rescue.Text.FetchedLast=Le secouriste de Sone a récupéré la dernière version disponible. Si vous ne souhaitiez pas récupérer une ancienne version de Sone. Consiédérez que vous n'avez pas de chance.
-Page.Rescue.Text.NotFetched=Le Secouriste de Sone ne peut pas récupérer la version {0} de votre Sone. Merci de réessayer, ou essayez avec une version plus ancienne.
+Page.Rescue.Title=Récupération de Sone - Sone
+Page.Rescue.Page.Title=Récupération du Sone “{0}”
+Page.Rescue.Text.Description=Le Mode Récupération vous permet de restaurer une version précédente de Sone. Cela peut être nécessaire si votre configuration est perdue
+Page.Rescue.Text.Procedure=Le Mode Récupération fonctionne en récupérant vos dernières insertions. Si une insertion est récupérée elle sera chargé dans votre Sone, vous permettant de reprendre le controle de vos messages, profile, et autres configurations (vous pouvez faire cela dans une seconde fenêtre ou tabulation Sone). Si vos messages ne peuvent être récupérés, demandez au Mode Récupération de restaurer les anciennes versions ci-dessous.
+Page.Rescue.Text.Fetching=Le récupérateur de Sone est en train de restaurer la version {0} de votre Sone.
+Page.Rescue.Text.Fetched=Le récupérateur de Sone a restauré la version {0} de votre Sone. Merci de vérifier vos messages, réponses et profile. Si les informations vous conviennent, débloquez la version.
+Page.Rescue.Text.FetchedLast=Le récupérateur de Sone a restauré la dernière version disponible. Si vous ne souhaitiez pas récupérer une ancienne version de Sone. Considérez que vous n'avez pas de chance.
+Page.Rescue.Text.NotFetched=Le récupérateur de Sone ne peut pas restaurer la version {0} de votre Sone. Merci de réessayer, ou essayez avec une version plus ancienne.
 Page.Rescue.Label.NextEdition=Prochaine version:
 Page.Rescue.Button.Fetch=Récupérer la version.
 
@@ -305,7 +305,7 @@ Page.NoPermission.Text.NoPermission=Vous avez tenté une action pour laquelle vo
 Page.DismissNotification.Title=Effacer la notification - Sone
 
 Page.WotPluginMissing.Text.LoadPlugin=Veuillez charger le plugin Web of Trust dans le {link}plugin manager{/link}.
-Page.WotPluginMissing.Text.WotRequired=Parce que le Web of Trust est une partie intégrante de Sone, le plugin Web of trust doit être chargé afin de pouvoir faire fontionner Sone.
+Page.WotPluginMissing.Text.WotRequired=Parce que Web of Trust est une partie intégrante de Sone, le plugin Web of trust doit être chargé afin de pouvoir faire fontionner Sone.
 
 Page.Logout.Title=Déconnexion - Sone
 
@@ -339,11 +339,11 @@ View.Sone.Status.Idle=Ce Sone est inactif, c'est à dire qu'il n'est pas en trai
 View.Sone.Status.Downloading=Ce Sone est en train d'être téléchargé.
 View.Sone.Status.Inserting=Ce Sone est en train d'être inséré.
 
-View.SoneMenu.Link.AllAlbums=tous les Albums
+View.SoneMenu.Link.AllAlbums=Tous les Albums
 
 View.Post.UnknownAuthor=(inconnu)
-View.Post.WebOfTrustLink=profile web of trust
-View.Post.Permalink=lier le message
+View.Post.WebOfTrustLink=Profile web of trust
+View.Post.Permalink=Lier le message
 View.Post.PermalinkAuthor=Lier l'auteur
 View.Post.Bookmarks.PostIsBookmarked=Ce message a été ajouté aux marque-pages, cliquer pour retirer des marque-pages
 View.Post.Bookmarks.PostIsNotBookmarked=Ce message n'est pas dans les marque-pages, cliquer pour marquer cette page
@@ -354,8 +354,8 @@ View.Post.LikeLink=Aime
 View.Post.UnlikeLink=N'aime pas
 View.Post.ShowSource=Activer/Désactiver le parser
 View.Post.NotDownloaded=Ce message n'a pas encore été téléchargé, ou a été effacé.
-View.Post.ShowMore=voir plus
-View.Post.ShowLess=vois moins
+View.Post.ShowMore=Voir plus
+View.Post.ShowLess=Vois moins
 
 View.UpdateStatus.Text.ChooseSenderIdentity=Choisir l'identité de l'expéditeur
 
@@ -376,18 +376,18 @@ View.Time.InTheFuture=dans le futur
 View.Time.AFewSecondsAgo=au cours des dernières secondes passées
 View.Time.HalfAMinuteAgo=au cours des 30 dernières secondes
 View.Time.AMinuteAgo=au cours de la dernière minute
-View.Time.XMinutesAgo=${min} il y a quelques minutes
+View.Time.XMinutesAgo=il y a environs {min} minutes
 View.Time.HalfAnHourAgo=au cours de la dernière demi heure
 View.Time.AnHourAgo=il y a environ une heure
-View.Time.XHoursAgo=${heure} au cours des dernières heures
+View.Time.XHoursAgo=Il y a environ ${hour} heures
 View.Time.ADayAgo=il y a environ un jour
-View.Time.XDaysAgo=${jour} il y a quelques jours
+View.Time.XDaysAgo=il y a plus ou moins ${day} jours
 View.Time.AWeekAgo=il y a environ une semaine
-View.Time.XWeeksAgo=${semaine} au cours des dernières semaines
+View.Time.XWeeksAgo=au cours des dernières ${week}semaines
 View.Time.AMonthAgo=au cours du dernier mois
-View.Time.XMonthsAgo=${mois} au cours des derniers mois
+View.Time.XMonthsAgo=au cours des derniers ${month} mois
 View.Time.AYearAgo=au cours de la dernière année
-View.Time.XYearsAgo=${année} au cours des dernières années
+View.Time.XYearsAgo=au cours des dernières ${year} années
 
 WebInterface.DefaultText.StatusUpdate=Exprimez-vous
 WebInterface.DefaultText.Message=Écrire un message...
@@ -398,21 +398,21 @@ WebInterface.DefaultText.LastName=Nom de famille
 WebInterface.DefaultText.BirthDay=Jour
 WebInterface.DefaultText.BirthMonth=Mois
 WebInterface.DefaultText.BirthYear=Année
-WebInterface.DefaultText.FieldName=nom du champ
-WebInterface.DefaultText.Option.InsertionDelay=Temps d'attente après qu'un Sone aie été modifié avant l'insertion (en secondes)
+WebInterface.DefaultText.FieldName=Nom du champ
+WebInterface.DefaultText.Option.InsertionDelay=Temps d'attente après qu'un Sone ai été modifié avant l'insertion (en secondes)
 WebInterface.DefaultText.Search=Que recherchez vous ?
-WebInterface.DefaultText.CreateAlbum.Name=Album title
-WebInterface.DefaultText.CreateAlbum.Description=Album description
-WebInterface.DefaultText.EditAlbum.Title=Album title
-WebInterface.DefaultText.EditAlbum.Description=Album description
-WebInterface.DefaultText.UploadImage.Title=Image title
-WebInterface.DefaultText.UploadImage.Description=Image description
-WebInterface.DefaultText.EditImage.Title=Image title
-WebInterface.DefaultText.EditImage.Description=Image description
+WebInterface.DefaultText.CreateAlbum.Name=Titre de l'album
+WebInterface.DefaultText.CreateAlbum.Description=Description de l'album
+WebInterface.DefaultText.EditAlbum.Title=Title de l'album
+WebInterface.DefaultText.EditAlbum.Description=Description de l'album
+WebInterface.DefaultText.UploadImage.Title=Titre de l'image
+WebInterface.DefaultText.UploadImage.Description=Description de l'image
+WebInterface.DefaultText.EditImage.Title=Titre de l'image
+WebInterface.DefaultText.EditImage.Description=Description de l'image
 WebInterface.DefaultText.Option.PostsPerPage=Nombre de messages à afficher par page
-WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
-WebInterface.DefaultText.Option.CharactersPerPost=Number of characters a post must have to be shortened
-WebInterface.DefaultText.Option.PostCutOffLength=Number of characters for the snippet of the shortened post
+WebInterface.DefaultText.Option.ImagesPerPage=Nombre d'images à afficher par pages
+WebInterface.DefaultText.Option.CharactersPerPost=Nombre de charactère qu'une publication doit avoir pour être racourcie
+WebInterface.DefaultText.Option.PostCutOffLength=Nombre de charactère du descriptif de la publication racourcie
 WebInterface.DefaultText.Option.PositiveTrust=La note de confiance positive à assigner
 WebInterface.DefaultText.Option.NegativeTrust=Une note de confiance négative à assigner
 WebInterface.DefaultText.Option.TrustComment=Le commentaire à mettre dans le "Web of Trust"
@@ -425,12 +425,12 @@ WebInterface.SelectBox.No=Non
 WebInterface.ClickToShow.Replies=Cliquer ici pour afficher les réponses cachées.
 WebInterface.VersionInformation.CurrentVersion=Version actuelle:
 WebInterface.VersionInformation.LatestVersion=Dernière version:
-WebInterface.VersionInformation.Homepage=Homepage
+WebInterface.VersionInformation.Homepage=Accueil
 
 Notification.ClickHereToRead=Cliquer ici pour lire le texte entier de la notification.
 Notification.FirstStart.Text=Il semble que ce soit la première fois que vous démarrez Sone. Pour démarrer, créez un nouveau Sone depuis une identité Web of Trust et commencez à suivre d'autres Sones.
 Notification.Startup.Text=Sone est cours de démarrage. Cela peut prendre un certain temps afin de récupérer toutes les identités et Sones du Web of Trust. Si il vous manque certains éléments, veuillez être patient, ils réapparaîtrons probablement assez tôt.
-Notification.ConfigNotRead.Text=Le fichier de configuration “sone.properties” n'a pas pu être lu probablement car il n'a pas été sauvegardé correctement. Ceci peut arriver avec les versions antérieures à Sone 0.3.3 et il n'y a rien que vous puissiez faire à ce propos.
+Notification.ConfigNotRead.Text=Le fichier de configuration “sone.properties” n'a pas pu être lu probablement parce qu'il n'a pas été sauvegardé correctement. Ceci peut arriver avec les versions antérieures à Sone 0.3.3 et il n'y a rien que vous puissiez faire à ce propos.
 Notification.Button.Dismiss=Rejeter
 Notification.NewSone.ShortText=De nouveaux Sones ont été découverts:
 Notification.NewSone.Text=De nouveaux Sones ont été découverts:
@@ -443,11 +443,10 @@ Notification.SoneIsBeingRescued.Text=Les Sones suivants sont actuellement en cou
 Notification.SoneRescued.Text=Les Sones suivants ont été sauvés:
 Notification.SoneRescued.Text.RememberToUnlock=Veuillez vous souvenir de contrôler les messages et réponses que vous avez donnés et n'oubliez ps de déverrouiller vos Sones!
 Notification.LockedSones.Text=Les Sones suivants ont été verrouillés pour plus de 5 minutes. Veuillez vérifier si vous voulez vraiment garder ces Sones bloqués:
-Notification.NewVersion.Text=La version {version} du pluggin Sone a été trouvée. Téléchargez la depuis USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}​!
+Notification.NewVersion.Text=La version {version} du plugin Sone a été trouvée. Téléchargez la depuis USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}​!
 Notification.InsertingImages.Text=Les images suivantes sont en cours d'insertion:
 Notification.InsertedImages.Text=Les images suivantes ont été insérées:
 Notification.ImageInsertFailed.Text=Les images suivantes ne peuvent être insérées:
 Notification.Mention.ShortText=Vous avez été mentionné.
 Notification.Mention.Text=Vous avez été mentionné dans les messages suivants:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
-#to update: 58, 99, 232 (?), 239, 241, 404-411, 413-415, 428, 452 (?)
\ No newline at end of file
diff --git a/src/main/resources/i18n/sone.ja.properties b/src/main/resources/i18n/sone.ja.properties
new file mode 100644 (file)
index 0000000..b31ff61
--- /dev/null
@@ -0,0 +1,452 @@
+Navigation.Menu.Sone.Name=Sone
+Navigation.Menu.Sone.Tooltip=Freenetソーシャルネットワークプラグイン
+Navigation.Menu.Sone.Item.Login.Name=ログイン
+Navigation.Menu.Sone.Item.Login.Tooltip=Soneにログインする
+Navigation.Menu.Sone.Item.Index.Name=自分のSone
+Navigation.Menu.Sone.Item.Index.Tooltip=自分のSoneを表示する
+Navigation.Menu.Sone.Item.New.Name=新しい投稿や返信
+Navigation.Menu.Sone.Item.New.Tooltip=新しい投稿や返信を表示する
+Navigation.Menu.Sone.Item.CreateSone.Name=Soneを作成
+Navigation.Menu.Sone.Item.CreateSone.Tooltip=新しいSoneを作成する
+Navigation.Menu.Sone.Item.KnownSones.Name=既知のSone
+Navigation.Menu.Sone.Item.KnownSones.Tooltip=既知のSoneをすべて表示する
+Navigation.Menu.Sone.Item.Bookmarks.Name=ブックマーク
+Navigation.Menu.Sone.Item.Bookmarks.Tooltip=ブックマークした投稿を表示する
+Navigation.Menu.Sone.Item.EditProfile.Name=プロフィールを編集
+Navigation.Menu.Sone.Item.EditProfile.Tooltip=自分のSoneのプロフィールを編集する
+Navigation.Menu.Sone.Item.ImageBrowser.Name=画像
+Navigation.Menu.Sone.Item.ImageBrowser.Tooltip=画像を管理する
+Navigation.Menu.Sone.Item.DeleteSone.Name=Soneを削除する
+Navigation.Menu.Sone.Item.DeleteSone.Tooltip=現在のSoneを削除する
+Navigation.Menu.Sone.Item.Logout.Name=ログアウト
+Navigation.Menu.Sone.Item.Logout.Tooltip=現在のSoneよりログアウトする
+Navigation.Menu.Sone.Item.Options.Name=設定
+Navigation.Menu.Sone.Item.Options.Tooltip=Soneプラグインの設定
+Navigation.Menu.Sone.Item.Rescue.Name=復帰
+Navigation.Menu.Sone.Item.Rescue.Tooltip=Soneを復帰する
+Navigation.Menu.Sone.Item.About.Name=情報
+Navigation.Menu.Sone.Item.About.Tooltip=Soneについて
+
+Page.About.Title=情報 - Sone
+Page.About.Page.Title=情報
+Page.About.Flattr.Description=Soneを便利だと感じられましたら、ぜひ寄付をいただけませんでしょうか? このページの一番下にあるFlatterはインターネット上にあるチップジャーのように、月2ユーロから匿名ではない寄付をしていただけます。詳しくは{link}flattr.com{/link}をご覧ください。
+Page.About.Homepage.Title=ホームページ
+Page.About.Homepage.Description=Soneの情報やソースコードは{link}ホームページ{/link}をご覧ください。
+Page.About.License.Title=ライセンス
+
+Page.Options.Title=設定 - Sone
+Page.Options.Page.Title=設定
+Page.Options.Page.Description=下記の設定はSoneプラグインの挙動を変更します。
+Page.Options.Section.SoneSpecificOptions.Title=Sone独自の設定
+Page.Options.Section.SoneSpecificOptions.NotLoggedIn=これらの設定は{link}ログイン{/link}後に有効になります。
+Page.Options.Section.SoneSpecificOptions.LoggedIn=これらの設定はログインしたSoneに対して行われるため、これらの設定をするにはログインをする必要があります。
+Page.Options.Option.AutoFollow.Description=新しいSoneが見つかったら自動的にフォローする。この設定は有効にした後に見つかったものに対して有効になります。。
+Page.Options.Option.EnableSoneInsertNotifications.Description=有効な場合、Soneのインサート中、及び完了した場合に通知が表示されます。
+Page.Options.Option.ShowNotificationNewSones.Description=新しいSoneを通知する。
+Page.Options.Option.ShowNotificationNewPosts.Description=新しい投稿を通知する。
+Page.Options.Option.ShowNotificationNewReplies.Description=新しい返信を通知する。
+Page.Options.Section.AvatarOptions.Title=アイコン設定
+Page.Options.Option.ShowAvatars.Description=条件によってカスタムアイコンはここで無効にできます。Soneのカスタムアイコンが無効になっている場合は代わりに自動的に生成されるアイコンが表示されます。
+Page.Options.Option.ShowAvatars.Never.Description=カスタムアイコンを表示しない。
+Page.Options.Option.ShowAvatars.Followed.Description=フォローしているSoneのみ、カスタムアイコンを表示する。
+Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=信用値を手動で0以上に設定しているSoneのみ、カスタムアイコンを表示する。
+Page.Options.Option.ShowAvatars.Trusted.Description=信用値が0以上のSoneのみ、カスタムアイコンを表示する。
+Page.Options.Option.ShowAvatars.Always.Description=常にカスタムアイコンを表示する。注意:不快な画像が表示される可能性があります。
+Page.Options.Section.RuntimeOptions.Title=実行の挙動
+Page.Options.Option.InsertionDelay.Description=Soneを変更した後にインサートが開始されるまでの遅延時間(秒)。
+Page.Options.Option.PostsPerPage.Description=ページ送りのボタンが表示されるまでに表示する投稿の数。
+Page.Options.Option.ImagesPerPage.Description=ページ送りのボタンが表示されるまでに表示する画像の数。
+Page.Options.Option.CharactersPerPost.Description=投稿を切って全文を見るリンクが表示されるまでの文字数。(-1で無効になります。)文字数は以下の設定により判定されます。
+Page.Options.Option.PostCutOffLength.Description=投稿が長い場合に表示される文字数。(上記の設定も参照してください。)
+Page.Options.Option.RequireFullAccess.Description=完全なアクセスが設定されていないホストに対してSoneへのアクセスを拒否するか否かの設定。
+Page.Options.Section.TrustOptions.Title=信用設定
+Page.Options.Option.PositiveTrust.Description=返信のリンクの下に表示されるチェックメークのリンクをクリックした際にSoneに設定されるポジティブな信用値。
+Page.Options.Option.NegativeTrust.Description=返信のリンクの下に表示される赤い☓のリンクをクリックした際にSoneに設定されるネガティブな信用値。
+Page.Options.Option.TrustComment.Description=Soneで信用値を設定した場合に設定されるWoTコメント。
+Page.Options.Section.FcpOptions.Title=FCPインターフェースの設定
+Page.Options.Option.FcpInterfaceActive.Description=FCPインターフェースを有効にし、Soneプラグインに他のプラグインやリモートクライアントからアクセスできるようにする。
+Page.Options.Option.FcpFullAccessRequired.Description=許可されたホストのみFCP接続を許可する。({link}ノード設定の「FCP」内の設定{/link}も確認してください。)
+Page.Options.Option.FcpFullAccessRequired.Value.No=いいえ
+Page.Options.Option.FcpFullAccessRequired.Value.Writing=書き込みのアクセス
+Page.Options.Option.FcpFullAccessRequired.Value.Always=常に
+Page.Options.Section.Cleaning.Title=クリーンアップ
+Page.Options.Option.ClearOnNextRestart.Description=次回のプラグインの再起動時に設定を初期化する。注意:{strong}これを行うとあなたの全てのSoneが破棄されます{/strong}。実行の前に必要なものは全てバックアップされていることを確認してください。また、次項の設定も同時に有効にする必要があります。
+Page.Options.Option.ReallyClearOnNextRestart.Description={strong}本当{/strong}にプラグインの設定を初期化する場合はこの項目も有効にしてください。
+Page.Options.Warnings.ValueNotChanged=指定された値が無効なため設定されませんでした。
+Page.Options.Button.Save=保存
+
+Page.Login.Title=ログイン - Sone
+Page.Login.Page.Title=ログイン
+Page.Login.Label.SelectSone=Sone選択
+Page.Login.Option.NoSone=選択してください。
+
+Page.Login.CreateSone.Title=Sone作成
+
+Page.CreateSone.Title=Sone作成 - Sone
+
+Page.DeleteSone.Title=Sone削除 - Sone
+Page.DeleteSone.Page.Title=“{sone}”を削除しますか?
+Page.DeleteSone.Page.Description=これにより、Sone自体は機構上不可能なため、Freenetから削除されませんがSoneに対するWeb of Trustを切り離します。
+Page.DeleteSone.Button.Yes=はい、消去します。
+Page.DeleteSone.Button.No=いいえ、消去しません。
+
+Page.Index.Title=自分のSone - Sone
+Page.Index.Label.Text=テキストを投稿:
+Page.Index.Label.Sender=送信者:
+Page.Index.Button.Post=投稿!
+Page.Index.PostList.Title=ポストフィード
+Page.Index.PostList.Text.NoPostYet=誰も投稿していません。ぜひ投稿をスタートしましょう!
+Page.Index.PostList.Text.FollowSomeSones=もしくは誰もフォローしてないですか? {link}既知のSone{/link}を表示し、面白そうなSoneをフォローしましょう。
+
+Page.New.Title=新しい投稿と返信 - Sone
+Page.New.Page.Title=新しい投稿と返信
+Page.New.NothingNew=現在新しいものはありません。
+
+Page.KnownSones.Title=既知のSone - Sone
+Page.KnownSones.Page.Title=既知のSone
+Page.KnownSones.Text.NoKnownSones=現在、絞り込みに合致するSoneはありません。
+Page.KnownSones.Label.Sort=ソート:
+Page.KnownSones.Label.FilterSones=Sone絞り込み:
+Page.KnownSones.Sort.Field.Name=名前
+Page.KnownSones.Sort.Field.LastActivity=最終のアクティビティ
+Page.KnownSones.Sort.Field.Posts=投稿数
+Page.KnownSones.Sort.Field.Images=画像数
+Page.KnownSones.Sort.Order.Ascending=昇順
+Page.KnownSones.Sort.Order.Descending=降順
+Page.KnownSones.Filter.Followed=フォローしているSoneのみ表示
+Page.KnownSones.Filter.NotFollowed=フォローしているSoneを隠す
+Page.KnownSones.Filter.New=新しいSoneのみ表示
+Page.KnownSones.Filter.NotNew=新しいSoneを隠す
+Page.KnownSones.Button.Apply=実行
+Page.KnownSones.Button.FollowAllSones=このページ内の全てのSoneをフォロー
+Page.KnownSones.Button.UnfollowAllSones=このページ内の全てのフォローを解除
+
+Page.EditProfile.Title=プロフィール編集 - Sone
+Page.EditProfile.Page.Title=プロフィール編集
+Page.EditProfile.Page.Description=このページではプロフィールの情報を設定できます。
+Page.EditProfile.Page.Hint.Optionality=プロフィールの全ての項目は強制でないことにご注意ください。全てが空欄でも構いません。また、ここに入力した項目はFreenet内に長きに渡って保持される可能性があります。
+Page.EditProfile.Label.FirstName=名:
+Page.EditProfile.Label.MiddleName=ミドルネーム:
+Page.EditProfile.Label.LastName=姓:
+Page.EditProfile.Birthday.Title=誕生日
+Page.EditProfile.Birthday.Label.Day=日:
+Page.EditProfile.Birthday.Label.Month=月:
+Page.EditProfile.Birthday.Label.Year=年:
+Page.EditProfile.Avatar.Title=アイコン
+Page.EditProfile.Avatar.Description=カスタムアイコンとして使用する画像を選択できます。他の人から見ると最大64x64ピクセルの大きさまでしか表示しないため、サイズはそれ以下にするべきです。(ページヘッダーでは80x80の大きさで表示されます。)
+Page.EditProfile.Avatar.Delete=カスタムアイコンなし
+Page.EditProfile.Fields.Title=カスタム項目
+Page.EditProfile.Fields.Description=ここではカスタム項目を設定できます。この項目では任意の項目を設定し、自由に表記することができます。ここに表記するものは匿名性に影響する可能性がありますのでご注意ください。
+Page.EditProfile.Fields.Button.Edit=編集
+Page.EditProfile.Fields.Button.MoveUp=上に上げる
+Page.EditProfile.Fields.Button.MoveDown=下に下げる
+Page.EditProfile.Fields.Button.Delete=消去
+Page.EditProfile.Fields.Button.ReallyDelete=本当に消去
+Page.EditProfile.Fields.AddField.Title=項目を追加
+Page.EditProfile.Fields.AddField.Label.Name=項目名:
+Page.EditProfile.Fields.AddField.Button.AddField=項目を追加
+Page.EditProfile.Button.Save=プロフィールを保存
+Page.EditProfile.Error.DuplicateFieldName=項目名“{fieldName}”は重複しています。
+
+Page.EditProfileField.Title=プロフィール項目編集 - Sone
+Page.EditProfileField.Page.Title=プロフィール項目を編集
+Page.EditProfileField.Text=このプロフィール項目の新しい名前を入力してください。
+Page.EditProfileField.Error.DuplicateFieldName=入力した項目名はすでに存在しています。
+Page.EditProfileField.Button.Save=変更
+Page.EditProfileField.Button.Reset=前の名称に戻す
+Page.EditProfileField.Button.Cancel=項目を変更しない
+
+Page.DeleteProfileField.Title=プロフィール項目を削除 - Sone
+Page.DeleteProfileField.Page.Title=プロフィール項目を削除
+Page.DeleteProfileField.Text=このプロフィール項目を本当に削除しますか?
+Page.DeleteProfileField.Button.Yes=はい、消去します
+Page.DeleteProfileField.Button.No=いいえ、消去しません
+
+Page.CreatePost.Title=投稿文を作成 - Sone
+Page.CreatePost.Page.Title=投稿文を作成
+Page.CreatePost.Label.Text=投稿文のテキスト:
+Page.CreatePost.Button.Post=投稿する!
+Page.CreatePost.Error.EmptyText=投稿文が空っぽです。何か書いてください!
+
+Page.CreateReply.Title=返信を作成 - Sone
+Page.CreateReply.Page.Title=返信を作成
+Page.CreateReply.Error.EmptyText=返信が空っぽです。何か書いてください!
+Page.CreateReply.Label.Text=返信テキスト:
+Page.CreateReply.Button.Post=返信する!
+
+Page.ViewSone.Title=Soneを表示 - Sone
+Page.ViewSone.Page.TitleWithoutSone=未知のSoneを表示する
+Page.ViewSone.NoSone.Description={sone}のIDに対応する既知のSoneは存在しません。特定のSoneを探している場合はWeb of Trust上で可視であることを確認してください:
+Page.ViewSone.UnknownSone.Description=このSoneはまだ読み込まれていません。しばらくしてから試してみてください。
+Page.ViewSone.UnknownSone.LinkToWebOfTrust=このSoneは未知ですが、Web of Trustのプロフィールは存在しているかもしれません:
+Page.ViewSone.WriteAMessage=このSoneにメッセージを書き込めます。投稿は誰でも読めることに注意してください。
+Page.ViewSone.PostList.Title={sone}による投稿
+Page.ViewSone.PostList.Text.NoPostYet=このSoneにはまだ何も投稿されていません。
+Page.ViewSone.Profile.Title=プロフィール
+Page.ViewSone.Profile.Label.Name=名前
+Page.ViewSone.Profile.Label.Albums=アルバム
+Page.ViewSone.Profile.Albums.Text.All=全てのアルバム
+Page.ViewSone.Profile.Name.WoTLink=Web of Trustプロフィール
+Page.ViewSone.Replies.Title={sone}が返信した投稿
+
+Page.ViewPost.Title=投稿を表示 - Sone
+Page.ViewPost.Page.Title={sone}による投稿を表示
+Page.ViewPost.Page.TitleUnknownSone=未知の投稿を表示
+Page.ViewPost.Text.UnknownPost=この投稿はまだダウンロードされていません。
+
+Page.Like.Title=投稿に「いいね」をつける - Sone
+Page.Unlike.Title=「いいね」を解除する - Sone
+
+Page.DeletePost.Title=Soneを削除 - Sone
+Page.DeletePost.Page.Title=Soneを削除
+Page.DeletePost.Text.PostWillBeGone=投稿を削除するとあなたのSoneから削除されます。Freenetの機構上、Freenetから削除することはできません。より古いリビジョンのSoneは削除された投稿を含みます。
+Page.DeletePost.Button.Yes=はい、削除します。
+Page.DeletePost.Button.No=いいえ、削除しません。
+
+Page.DeleteReply.Title=返信を削除 - Sone
+Page.DeleteReply.Page.Title=返信を削除
+Page.DeleteReply.Text.PostWillBeGone=返信を削除するとあなたのSoneから削除されます。Freenetの機構上、Freenetから削除することはできません。より古いリビジョンのSoneは削除された投稿を含みます。
+Page.DeleteReply.Button.Yes=はい、削除します。
+Page.DeleteReply.Button.No=いいえ、削除しません。
+
+Page.LockSone.Title=Soneをロック - Sone
+
+Page.UnlockSone.Title=Soneのロックを解除 - Sone
+
+Page.FollowSone.Title=Soneをフォロー - Sone
+
+Page.UnfollowSone.Title=Soneのフォローを解除 - Sone
+
+Page.ImageBrowser.Title=画像ブラウザ - Sone
+Page.ImageBrowser.Album.Title=“{album}”アルバム
+Page.ImageBrowser.Album.Error.NotFound.Text=要求されたアルバムが見つかりませんでした。ダウンロードされていないか、削除された可能性があります。
+Page.ImageBrowser.Sone.Title={sone}のアルバム
+Page.ImageBrowser.Sone.Error.NotFound.Text=要求されたSoneが見つかりませんでした。まだダウンロードされていない可能性があります。
+Page.ImageBrowser.Header.Albums=アルバム
+Page.ImageBrowser.Header.Images=画像
+Page.ImageBrowser.Link.All=全てのSone
+Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=アルバムを作成
+Page.ImageBrowser.Album.Edit.Title=アルバムを編集
+Page.ImageBrowser.Album.Delete.Title=アルバムを削除
+Page.ImageBrowser.Album.Label.AlbumImage=アルバム画像:
+Page.ImageBrowser.Album.Label.Title=タイトル:
+Page.ImageBrowser.Album.Label.Description=説明:
+Page.ImageBrowser.Album.AlbumImage.Choose=アルバム画像を選択…
+Page.ImageBrowser.Album.Button.Save=アルバムを保存
+Page.ImageBrowser.Album.Button.Delete=アルバムを削除
+Page.ImageBrowser.Image.Edit.Title=アルバムを編集
+Page.ImageBrowser.Image.Title.Label=タイトル:
+Page.ImageBrowser.Image.Description.Label=説明:
+Page.ImageBrowser.Image.Button.MoveLeft=◀
+Page.ImageBrowser.Image.Button.Save=画像を保存
+Page.ImageBrowser.Image.Button.MoveRight=►
+Page.ImageBrowser.Image.Delete.Title=画像を削除
+Page.ImageBrowser.Image.Button.Delete=画像を削除
+
+Page.CreateAlbum.Title=アルバムを作成 - Sone
+Page.CreateAlbum.Page.Title=アルバムを作成
+Page.CreateAlbum.Error.NameMissing=新しいアルバムの名称を入力するのを忘れていませんか?
+
+Page.UploadImage.Title=画像をアップロード - Sone
+Page.UploadImage.Error.InvalidImage=アップロードされた画像を認識できません。対応している形式はJPEG (*.jpgもしくは*.jpeg)もしくはPNG(*.png)です。
+
+Page.EditImage.Title=画像を編集 - Sone
+
+Page.DeleteImage.Title=画像を削除 - Sone
+Page.DeleteImage.Page.Title=画像を削除
+Page.DeleteImage.Text.ImageWillBeGone=これにより“{image}”が“{album}”のアルバムより削除されます。画像がすでにFreenetにインサートされている場合は強制的に削除することはできません。画像の削除を行いますか?
+Page.DeleteImage.Button.Yes=はい、画像を削除します。
+Page.DeleteImage.Button.No=いいえ、画像を削除しません。
+
+Page.EditAlbum.Title=アルバムを編集 - Sone
+
+Page.DeleteAlbum.Title=アルバムを削除 - Sone
+Page.DeleteAlbum.Page.Title=アルバムを削除
+Page.DeleteAlbum.Text.AlbumWillBeGone=これにより“{title}”のアルバムを削除します。本当に削除しますか?
+Page.DeleteAlbum.Button.Yes=はい、アルバムを削除します。
+Page.DeleteAlbum.Button.No=いいえ、アルバムを削除しません。
+
+Page.Trust.Title=Soneを信用する - Sone
+
+Page.Distrust.Title=Soneを信用しない - Sone
+
+Page.Untrust.Title=Soneの信用を解除する - Sone
+
+Page.MarkAsKnown.Title=既知のマークを付ける - Sone
+
+Page.Bookmark.Title=ブックマーク - Sone
+Page.Unbookmark.Title=ブックマークを解除 - Sone
+Page.Bookmarks.Title=ブックマーク - Sone
+Page.Bookmarks.Page.Title=ブックマーク
+Page.Bookmarks.Text.NoBookmarks=何もブックマークされていません。投稿の下の星をクリックすることによりブックマークを設定できます。
+Page.Bookmarks.Text.PostsNotLoaded=ブックマークされた投稿の一部が読み出せなかったため表示していません。Soneが最近再起動されたか、元のSoneより削除された可能性があります。もし投稿がすでに削除されているとおもわれる場合には{link}ブックマークを解除{/link}できます。
+
+Page.Search.Title=検索 - Sone
+Page.Search.Page.Title=検索結果
+Page.Search.Text.SoneHits=検索に合致するSoneは以下の通りです。
+Page.Search.Text.PostHits=検索に合致する投稿は以下の通りです。
+Page.Search.Text.NoHits=検索に合致するSoneまたは投稿はありませんでした。
+
+Page.Rescue.Title=Sone復帰モード - Sone
+Page.Rescue.Page.Title=Sone復帰モード “{0}”
+Page.Rescue.Text.Description=復帰モードでは古いバージョンのSoneを復帰することができます。このプロセスは設定が失われた場合に必要となる場合があります。
+Page.Rescue.Text.Procedure=復帰モードは古い版のSoneを取り出すことにより行います。版が正常に取り出せた場合はそれがSoneに読み込まれ、投稿、プロフィール、その他の設定を使用することができます。これは異なったブラウザウィンドウやタブで行うこともできます。読み出された版が望むものではない場合は、更に古い版を読み込むことができます。
+Page.Rescue.Text.Fetching=Sone復帰モードでは現在{0}の版を読み込んでいます。
+Page.Rescue.Text.Fetched=Sone復帰モードは{0}の版を読み込みました。投稿、返信、プロフィールなどを確認の上、問題がなければロックを解除してください。
+Page.Rescue.Text.FetchedLast=Sone復帰モードは存在する全ての版を読み込みました。この時点で読み込めていない場合は恐らく復帰は不可能でしょう。
+Page.Rescue.Text.NotFetched=Sone復帰モードは{0}の版を読み込むことはできませんでした。{0}の版を試してみるか、さらに次の版を試してみてください。
+Page.Rescue.Label.NextEdition=次の版:
+Page.Rescue.Button.Fetch=版を取り出す
+
+Page.NoPermission.Title=不正なアクセス - Sone
+Page.NoPermission.Page.Title=不正なアクセス
+Page.NoPermission.Text.NoPermission=許可されていないアクセスが検知されました。これ以上続行すると防衛機構が作動するかも知れません!
+
+Page.DismissNotification.Title=通知を消す - Sone
+
+Page.WotPluginMissing.Text.WotRequired=Web of TrustはSoneで必要な機構であるため、Soneの読み込みにはまずはWeb of Trustが読み出されている必要があります。
+Page.WotPluginMissing.Text.LoadPlugin={link}プラグインマネージャー{/link}よりWeb of Trustプラグインを読み込んでください。
+
+Page.Logout.Title=ログアウト - Sone
+
+Page.Invalid.Title=不正な操作が行われました - Sone
+Page.Invalid.Page.Title=不正な操作が行われました
+Page.Invalid.Text=不正な操作が行われたか、操作が正常でもパラメーターが不正である可能性があります。{link}インデックスページ{/link}に戻って試してみてください。同じ問題が何度も発生するようであればバグを見つけた可能性があります。
+
+View.Search.Button.Search=検索
+
+View.CreateSone.Text.WotIdentityRequired=Soneを作成するのには{link}Web of Trustプラグイン{/link}のプロフィールが必要です。
+View.CreateSone.Select.Default=プロフィールを選択
+View.CreateSone.Text.NoIdentities=Web of Trustのプロフィールが存在しません。{link}Web of Trustプラグイン{/link}でプロフィールを作成してください。
+View.CreateSone.Text.NoNonSoneIdentities=Soneに紐付けられていないプロフィールがWeb of Trustのプロフィールに存在しません。残りのWeb of Trustを使用して新しいSoneを作成するか、{link}Web of Trustプラグイン{/link}より新しいプロフィールを作成してください。
+View.CreateSone.Button.Create=Soneを作成
+View.CreateSone.Text.Error.NoIdentity=プロフィールが選択されていません。
+
+View.Sone.Label.LastUpdate=最終更新:
+View.Sone.Text.UnknownDate=不明
+View.Sone.Stats.Posts={0,number} {0,choice,0#個|1#個|1<個}
+View.Sone.Stats.Replies={0,number} {0,choice,0#通|1#通|1<通}
+View.Sone.Stats.Images={0,number} {0,choice,0#枚|1#枚|1<枚}
+View.Sone.Button.UnlockSone=ロック解除
+View.Sone.Button.UnlockSone.Tooltip=このSoneのインサートを有効にする
+View.Sone.Button.LockSone=ロック
+View.Sone.Button.LockSone.Tooltip=このSoneのインサートを無効にする
+View.Sone.Button.UnfollowSone=フォロー解除
+View.Sone.Button.FollowSone=フォロー
+View.Sone.Status.Modified=このSoneには変更があり、インサート待ちです。
+View.Sone.Status.Unknown=このSoneはまだ読み込まれていません。
+View.Sone.Status.Idle=このSoneはアイドル状態です。(インサート・ダウンロードは実行されていません。)
+View.Sone.Status.Downloading=このSoneは現在ダウンロード中です。
+View.Sone.Status.Inserting=このSoneは現在インサート中です。
+
+View.SoneMenu.Link.AllAlbums=全てのアルバム
+
+View.Post.UnknownAuthor=(不明)
+View.Post.WebOfTrustLink=Web of Trustプロフィール
+View.Post.Permalink=投稿へのリンク
+View.Post.PermalinkAuthor=著者へのリンク
+View.Post.Bookmarks.PostIsBookmarked=投稿はブックマークされています。クリックするとブックマークを解除します
+View.Post.Bookmarks.PostIsNotBookmarked=投稿はブックマークされていません。クリックするとブックマークを設定します
+View.Post.DeleteLink=削除
+View.Post.SendReply=返信する
+View.Post.Reply.DeleteLink=削除
+View.Post.LikeLink=いいね
+View.Post.UnlikeLink=「いいね」を解除
+View.Post.ShowSource=パーサーを切り替え
+View.Post.NotDownloaded=この投稿はまだダウンロードされていないか削除されています。
+View.Post.ShowMore=もっと見る
+View.Post.ShowLess=一部を見る
+
+View.UpdateStatus.Text.ChooseSenderIdentity=送信者のプロフィールを選択してください
+
+View.Trust.Tooltip.Trust=この人を信用する
+View.Trust.Tooltip.Distrust=この人にネガティブな信用を付与する
+View.Trust.Tooltip.Untrust=この人につけた信用を解除する
+
+View.CreateAlbum.Title=アルバムを作成
+View.CreateAlbum.Label.Name=名称:
+View.CreateAlbum.Label.Description=説明:
+
+View.UploadImage.Title=画像をアップロード
+View.UploadImage.Label.Title=タイトル:
+View.UploadImage.Label.Description=説明:
+View.UploadImage.Button.UploadImage=画像をアップロードする
+
+View.Time.InTheFuture=今より未来
+View.Time.AFewSecondsAgo=およそ数秒前
+View.Time.HalfAMinuteAgo=およそ30秒前
+View.Time.AMinuteAgo=およそ1分前
+View.Time.XMinutesAgo=${min}分前o
+View.Time.HalfAnHourAgo=30分前
+View.Time.AnHourAgo=およそ1時間前
+View.Time.XHoursAgo=${hour}時間前
+View.Time.ADayAgo=およそ1日前
+View.Time.XDaysAgo=${day}日前
+View.Time.AWeekAgo=およそ1週前
+View.Time.XWeeksAgo=${week}週前
+View.Time.AMonthAgo=およそ1月前
+View.Time.XMonthsAgo=${month}月前
+View.Time.AYearAgo=およそ1年前
+View.Time.XYearsAgo=${year}年前
+
+WebInterface.DefaultText.StatusUpdate=何か投稿しますか?
+WebInterface.DefaultText.Message=メッセージを書いてください…
+WebInterface.DefaultText.Reply=返信を書いてください…
+WebInterface.DefaultText.FirstName=名
+WebInterface.DefaultText.MiddleName=ミドルネーム
+WebInterface.DefaultText.LastName=姓
+WebInterface.DefaultText.BirthDay=日
+WebInterface.DefaultText.BirthMonth=月
+WebInterface.DefaultText.BirthYear=年
+WebInterface.DefaultText.FieldName=項目名
+WebInterface.DefaultText.Option.InsertionDelay=Soneに変更が加わってからインサートするまでの遅延時間(秒)
+WebInterface.DefaultText.Search=何をお探しですか?
+WebInterface.DefaultText.CreateAlbum.Name=アルバムのタイトル
+WebInterface.DefaultText.CreateAlbum.Description=アルバムの説明
+WebInterface.DefaultText.EditAlbum.Title=アルバムのタイトル
+WebInterface.DefaultText.EditAlbum.Description=アルバムの説明
+WebInterface.DefaultText.UploadImage.Title=画像のタイトル
+WebInterface.DefaultText.UploadImage.Description=画像の説明
+WebInterface.DefaultText.EditImage.Title=画像のタイトル
+WebInterface.DefaultText.EditImage.Description=画像の説明
+WebInterface.DefaultText.Option.PostsPerPage=ページに表示する投稿の数
+WebInterface.DefaultText.Option.ImagesPerPage=ページに表示する画像の数
+WebInterface.DefaultText.Option.CharactersPerPost=投稿を短く表示する最低の文字数
+WebInterface.DefaultText.Option.PostCutOffLength=投稿を短く表示する場合の文字数
+WebInterface.DefaultText.Option.PositiveTrust=割り当てるポジティブな信用値
+WebInterface.DefaultText.Option.NegativeTrust=割り当てるネガティブな信用値
+WebInterface.DefaultText.Option.TrustComment=Web of Trustに設定するコメント
+WebInterface.Button.Comment=コメント
+WebInterface.Confirmation.DeletePostButton=本当に消去!
+WebInterface.Confirmation.DeleteReplyButton=本当に消去!
+WebInterface.SelectBox.Choose=選択…
+WebInterface.SelectBox.Yes=はい
+WebInterface.SelectBox.No=いいえ
+WebInterface.ClickToShow.Replies=隠れた返信を表示する。
+WebInterface.VersionInformation.CurrentVersion=現バージョン:
+WebInterface.VersionInformation.LatestVersion=最新バージョン:
+WebInterface.VersionInformation.Homepage=ホームページ
+
+Notification.ClickHereToRead=クリックして通知の全文を表示します。
+Notification.FirstStart.Text=Soneの起動は初めてですか? 初めるのにはWeb of Trustプロフィールより新しいSoneを作成して、他のSoneをフォローして下さい。
+Notification.Startup.Text=Soneは現在起動中です。Soneや全てのWeb of Trustプロフィールを読み込むのにしばらくかかります。一部の要素がない場合はしばらくお待ち下さい。
+Notification.ConfigNotRead.Text=設定ファイル、sone.propertiesが読み込めませんでした。恐らく正常に保存されなかったのが原因です。これは0.3.3以下で発生する可能性がありますがその場合は対応手段はありません。
+Notification.Button.Dismiss=消す
+Notification.NewSone.ShortText=新しいSoneが見つかりました:
+Notification.NewSone.Text=新しいSoneが見つかりました:
+Notification.NewPost.ShortText=新しい投稿が見つかりました。
+Notification.NewPost.Text=次のSoneに新しい投稿が見つかりました:
+Notification.NewPost.Button.MarkRead=既読にする
+Notification.NewReply.ShortText=新しい返信が見つかりました。
+Notification.NewReply.Text=次のSoneに対する新しい返信が見つかりました:
+Notification.SoneIsBeingRescued.Text=次のSoneは現在復帰中です:
+Notification.SoneRescued.Text=次のSoneが復帰しました:
+Notification.SoneRescued.Text.RememberToUnlock=投稿や返信を確認の上、ロックを解除するのを忘れないようにしてください。
+Notification.LockedSones.Text=次のSoneは5分以上ロック状態になっています。故意にロック中であるかを確認してください:
+Notification.NewVersion.Text=Soneの新しいバージョン{version}があります。USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/{edition}​よりダウンロードしてください。
+Notification.InsertingImages.Text=次の画像がインサート中です:
+Notification.InsertedImages.Text=次の画像のインサートされました:
+Notification.ImageInsertFailed.Text=次の画像のインサートに失敗しました:
+Notification.Mention.ShortText=誰かにメンションされました。
+Notification.Mention.Text=次の投稿でメンションされています:
+Notification.SoneInsert.Duration={0,number} {0,choice,0#秒|1#秒|1<秒}
index b27ce41..3a7175d 100644 (file)
@@ -55,7 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Alltid vis egendefinerte avat
 Page.Options.Section.RuntimeOptions.Title=Oppførsel ved kjøretid
 Page.Options.Option.InsertionDelay.Description=Antall sekunder Sone-innsetteren skal vente etter en endring av en Sone før den blir innsatt.
 Page.Options.Option.PostsPerPage.Description=Antallet innlegg å vise pr side før side-kontroller blir vist.
-Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
+Page.Options.Option.ImagesPerPage.Description=Antall bilder å vise på en side før side-kontroller blir vist.
 Page.Options.Option.CharactersPerPost.Description=Antall tegn å vise fra et innlegg før resten blir skjult og en link blir vist for å utvide til hele innlegget (-1 for å deaktivere). Lengden på den viste teksten kan endres under.
 Page.Options.Option.PostCutOffLength.Description=Antallet tegn som blir vist hvis et innlegg er for langt (Se innstilling over).
 Page.Options.Option.RequireFullAccess.Description=For å avslå tilgang til Sone fra enhver host som ikke har blitt gitt full tilgang.
@@ -96,7 +96,7 @@ Page.Index.Label.Sender=Avsender:
 Page.Index.Button.Post=Publiser!
 Page.Index.PostList.Title=Innleggsstrøm
 Page.Index.PostList.Text.NoPostYet=Ingen har skrevet noen innlegg enda. Du burde starte med en gang!
-Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting!
+Page.Index.PostList.Text.FollowSomeSones=Eller kanskje du ikke følger noen Soner? Naviger til {link}Kjente Soner{/link} og følg de som ser interessante ut!
 
 Page.New.Title=Nye innlegg og svar - Sone
 Page.New.Page.Title=Nye innlegg og svar
@@ -410,7 +410,7 @@ WebInterface.DefaultText.UploadImage.Title=Bildetittel
 WebInterface.DefaultText.EditImage.Description=Bildebeskrivelse
 WebInterface.DefaultText.EditImage.Title=Bildetittel
 WebInterface.DefaultText.Option.PostsPerPage=Antall innlegg å vise på en side
-WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
+WebInterface.DefaultText.Option.ImagesPerPage=Antall bilder å vise per side
 WebInterface.DefaultText.Option.CharactersPerPost=Antall tegn et innlegg må ha for å bli skjult.
 WebInterface.DefaultText.Option.PostCutOffLength=Antall tegn som vises når et innlegg blir skjult
 WebInterface.DefaultText.Option.PositiveTrust=Positiv tillit å gi
@@ -425,7 +425,7 @@ WebInterface.SelectBox.No=Nei
 WebInterface.ClickToShow.Replies=Klikk her for å vise skjulte svar.
 WebInterface.VersionInformation.CurrentVersion=Gjeldende utgave:
 WebInterface.VersionInformation.LatestVersion=Siste utgave:
-WebInterface.VersionInformation.Homepage=Homepage
+WebInterface.VersionInformation.Homepage=Hjemmeside
 
 Notification.ClickHereToRead=Klikk her for å lese hele varslingen.
 Notification.FirstStart.Text=Dette ser ut til å være første gang du starter Sone. For å starte, lag en ny Sone fra et 'Web Of Trust'-pseudonym og start å følge andre Soner.
@@ -450,4 +450,3 @@ Notification.ImageInsertFailed.Text=De følgende bildene kunne ikke bli innsatt:
 Notification.Mention.ShortText=Du har blitt nevnt:
 Notification.Mention.Text=Du har blitt nevnt i følgende innlegg:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#sekund|1#sekund|1<sekunder}
-# to update: 58, 99, 413, 428
\ No newline at end of file
index 1392796..a19ea96 100644 (file)
@@ -55,7 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Zawsze pokazuj niestandardowe
 Page.Options.Section.RuntimeOptions.Title=Tryb pracy
 Page.Options.Option.InsertionDelay.Description=Czas oczekiwania użytkownika Sone na modifikację profilu Sone przed jego załadowaniem.
 Page.Options.Option.PostsPerPage.Description=Ilość postów wyświetlanych na stronie przed pojawieniem się znaków paginacji.
-Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
+Page.Options.Option.ImagesPerPage.Description=Ilość obrazków wyświetlanych na stronie przed pojawieniem się znaków paginacji.
 Page.Options.Option.CharactersPerPost.Description=Ilość znaków pokazywanych w poście zanim zostanie on obcięty i pojawi się link do jego rozszerzenia (-1 powoduje wyłączenie). Długość fragmentu zależy od poniższej opcji.
 Page.Options.Option.PostCutOffLength.Description=Ilość znaków wyświetlanych w przypadku za długiego postu (zobacz opcję powyżej).
 Page.Options.Option.RequireFullAccess.Description=Opcja odmowy dostępu do Sone hostom bez przyznanego pełnego dostępu.
@@ -96,7 +96,7 @@ Page.Index.Label.Sender=Autor:
 Page.Index.Button.Post=Napisz!
 Page.Index.PostList.Title=Wątek
 Page.Index.PostList.Text.NoPostYet=Nie ma jeszcze żądnych postów. Można rozpocząć pisanie wątku!
-Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting!
+Page.Index.PostList.Text.FollowSomeSones=Być może nie śledzisz żadnych Sone? Spójrz na tę listę {link}znane Sone{/link} i zacznij kogoś śledzić.
 
 Page.New.Title=Nowe Posty i Odpowiedzi - Sone
 Page.New.Page.Title=Nowe Posty i Odpowiedzi
@@ -410,7 +410,7 @@ WebInterface.DefaultText.UploadImage.Description=Opis obrazka
 WebInterface.DefaultText.EditImage.Title=Tytuł Obrazka
 WebInterface.DefaultText.EditImage.Description=Opis Obrazka
 WebInterface.DefaultText.Option.PostsPerPage=Ilość postów wyświetlanych na jednej stronie
-WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
+WebInterface.DefaultText.Option.ImagesPerPage=Ilość obrazków na stronie
 WebInterface.DefaultText.Option.CharactersPerPost=Ilość znaków, które ma zawierać post, aby zostać skrócony
 WebInterface.DefaultText.Option.PostCutOffLength=Ilość znaków w skróconym poście
 WebInterface.DefaultText.Option.PositiveTrust=Pozytywny poziom zaufania
@@ -425,7 +425,7 @@ WebInterface.SelectBox.No=Nie
 WebInterface.ClickToShow.Replies=Kliknij, żeby pokazać ukryte odpowiedzi.
 WebInterface.VersionInformation.CurrentVersion=Aktualna wersja:
 WebInterface.VersionInformation.LatestVersion=Najnowsza wersja:
-WebInterface.VersionInformation.Homepage=Homepage
+WebInterface.VersionInformation.Homepage=Strona główna
 
 Notification.ClickHereToRead=Kliknij tutaj, żeby przeczytać cały tekst powiadomienia.
 Notification.FirstStart.Text=Po raz pierwszy korzystasz z Sone. Zacznij od wybrania nowego Sone z tożsamości z Sieci Zaufania i zacznij śledzić inne Sone.
@@ -450,4 +450,3 @@ Notification.ImageInsertFailed.Text=Nie można załadowac następujących obraz
 Notification.Mention.ShortText=Zostałeś oznaczony.
 Notification.Mention.Text=Zostałeś oznaczony w następujących postach:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#seconds|1#second|1<seconds}
-# to update: 58, 99, 413, 428
index aa8cb16..8263074 100644 (file)
@@ -55,7 +55,7 @@ Page.Options.Option.ShowAvatars.Always.Description=Всегда показыва
 Page.Options.Section.RuntimeOptions.Title=Поведение во время работы.
 Page.Options.Option.InsertionDelay.Description=Количество секунд, в течение которых выгрузчик Sone ожидает после изменения Sone до того, как он будет выгружен.
 Page.Options.Option.PostsPerPage.Description=Количество сообщений, которое должно быть показно на странице до того, как будут показаны кнопки переключения страниц.
-Page.Options.Option.ImagesPerPage.Description=The number of images to display on a page before pagination controls are being shown.
+Page.Options.Option.ImagesPerPage.Description=Количество изображений, которое должно быть показно на странице до того, как будут показаны кнопки переключения страниц.
 Page.Options.Option.CharactersPerPost.Description=Количество символов сообщения, которые должны быть показаны до того, как оно будет обрезано и будет показана ссылка для его раскрытия (-1 для отключения). Фактическая длина обрезанного сообщения задается нижеследующей настройкой.
 Page.Options.Option.PostCutOffLength.Description=Количество символов, которые показываются, если сообщение посчитано слишком длинным (см. настройку выше).
 Page.Options.Option.RequireFullAccess.Description=Запрещать доступ к Sone любому хосту, которому не был дан полный доступ.
@@ -96,7 +96,7 @@ Page.Index.Label.Sender=Отправитель:
 Page.Index.Button.Post=Отправить!
 Page.Index.PostList.Title=Лента сообщений
 Page.Index.PostList.Text.NoPostYet=Никто еще не написал ни одного сообщения. Вам, наверное, следует начать это прямо сейчас!
-Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting!
+Page.Index.PostList.Text.FollowSomeSones=Или возможно вы не подписаны ни на один Sone? Посмотрите на список {link}Известныx Sone{/link} и попдишитесь на всех, кто покажется вам интересным!
 
 Page.New.Title=Новые сообщения и ответы - Sone
 Page.New.Page.Title=Новые сообщения и ответы
@@ -410,13 +410,13 @@ WebInterface.DefaultText.UploadImage.Description=Описание изображ
 WebInterface.DefaultText.EditImage.Title=Название изображения
 WebInterface.DefaultText.EditImage.Description=Описание изображения
 WebInterface.DefaultText.Option.PostsPerPage=Количество сообщений, показываемых на странице
-WebInterface.DefaultText.Option.ImagesPerPage=Number of images to show on a page
+WebInterface.DefaultText.Option.ImagesPerPage=Количество изображений, показываемых на странице
 WebInterface.DefaultText.Option.CharactersPerPost=Количество символов, которое должно быть у сообщения, чтобы оно было сокращено
 WebInterface.DefaultText.Option.PostCutOffLength=Количество символов в сокращенном варианте сообщения
 WebInterface.DefaultText.Option.PositiveTrust=Положительное доверие для назначения
 WebInterface.DefaultText.Option.NegativeTrust=Отрицательное доверие для назначения
 WebInterface.DefaultText.Option.TrustComment=Комментарий для установки в web of trust
-WebInterface.Button.Comment=Comment
+WebInterface.Button.Comment=Комментировать
 WebInterface.Confirmation.DeletePostButton=Да, удалить!
 WebInterface.Confirmation.DeleteReplyButton=Да, удалить!
 WebInterface.SelectBox.Choose=Выбрать…
@@ -425,7 +425,7 @@ WebInterface.SelectBox.No=Нет
 WebInterface.ClickToShow.Replies=Нажмите, чтобы показать скрытые ответы.
 WebInterface.VersionInformation.CurrentVersion=Текущая версия:
 WebInterface.VersionInformation.LatestVersion=Последняя версия:
-WebInterface.VersionInformation.Homepage=Homepage
+WebInterface.VersionInformation.Homepage=Домашняя страница
 
 Notification.ClickHereToRead=Нажмите здесь, чтобы прочитать полный текст уведомления.
 Notification.FirstStart.Text=Видимо, вы запустили Sone в первый раз. Чтобы начать, создайте новый Sone из личность web of trust и начните подписываться на другие Sone.
@@ -450,4 +450,3 @@ Notification.ImageInsertFailed.Text=Следующие изображения н
 Notification.Mention.ShortText=Вас упомянули.
 Notification.Mention.Text=Вас упомянули в следующих сообщениях:
 Notification.SoneInsert.Duration={0,number} {0,choice,0#секунд|1#секунда|2#секунды|4<секунд}
-# to update: 58, 99, 413, 428
\ No newline at end of file
index f0b8b56..ddc08a9 100644 (file)
 
                <ul id="avatar-selection">
                        <li id="no-avatar">
-                               <input type="radio" name="avatar-id" value="none"<%ifnull avatar-id> checked="checked"<%/if>/>
+                               <input type="radio" name="avatarId" value="none"<%ifnull avatarId> checked="checked"<%/if>/>
                                <%= Page.EditProfile.Avatar.Delete|l10n|html>
                        </li>
                        <%foreach currentSone.allImages image>
                                <li>
-                                       <input type="radio" name="avatar-id" value="<%image.id|html>"<%if avatar-id|match key=image.id> checked="checked"<%/if>/>
+                                       <input type="radio" name="avatarId" value="<%image.id|html>"<%if avatarId|match value=image.id> checked="checked"<%/if>/>
                                        <div class="post-avatar"><% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title></div>
                                </li>
                        <%/foreach>
index 4f9f002..3dc25e8 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.text;
 
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.Arrays;
 
 import junit.framework.TestCase;
 import net.pterodactylus.sone.core.SoneProvider;
@@ -42,6 +43,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
+       @SuppressWarnings("static-method")
        public void testPlainText() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(null, null);
                Iterable<Part> parts;
@@ -68,6 +70,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
+       @SuppressWarnings("static-method")
        public void testKSKLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(null, null);
                Iterable<Part> parts;
@@ -94,7 +97,7 @@ public class SoneTextParserTest extends TestCase {
         * @throws IOException
         *             if an I/O error occurs
         */
-       @SuppressWarnings("synthetic-access")
+       @SuppressWarnings({ "synthetic-access", "static-method" })
        public void testEmptyLinesAndSoneLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
                Iterable<Part> parts;
@@ -105,6 +108,24 @@ public class SoneTextParserTest extends TestCase {
                assertEquals("Part Text", "Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff.", convertText(parts, PlainTextPart.class, SonePart.class));
        }
 
+       /**
+        * Test for a bug discovered in Sone 0.8.4 where a plain “http://” would be
+        * parsed into a link.
+        *
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       @SuppressWarnings({ "synthetic-access", "static-method" })
+       public void testEmpyHttpLinks() throws IOException {
+               SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
+               Iterable<Part> parts;
+
+               /* check empty http links. */
+               parts = soneTextParser.parse(null, new StringReader("Some text. Empty link: http:// – nice!"));
+               assertNotNull("Parts", parts);
+               assertEquals("Part Text", "Some text. Empty link: http:// – nice!", convertText(parts, PlainTextPart.class));
+       }
+
        //
        // PRIVATE METHODS
        //
@@ -120,7 +141,7 @@ public class SoneTextParserTest extends TestCase {
         *            valid
         * @return The converted text
         */
-       private String convertText(Iterable<Part> parts, Class<?>... validClasses) {
+       private static String convertText(Iterable<Part> parts, Class<?>... validClasses) {
                StringBuilder text = new StringBuilder();
                for (Part part : parts) {
                        assertNotNull("Part", part);
@@ -132,7 +153,7 @@ public class SoneTextParserTest extends TestCase {
                                }
                        }
                        if (!classValid) {
-                               assertEquals("Part’s Class", null, part.getClass());
+                               fail("Part’s Class (" + part.getClass() + ") is not one of " + Arrays.toString(validClasses));
                        }
                        if (part instanceof PlainTextPart) {
                                text.append(((PlainTextPart) part).getText());