]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Shaarli's translation
authorArthurHoaro <arthur@hoa.ro>
Tue, 9 May 2017 16:12:15 +0000 (18:12 +0200)
committerArthurHoaro <arthur@hoa.ro>
Sun, 22 Oct 2017 10:55:03 +0000 (12:55 +0200)
 * translation system and unit tests
 * Translations everywhere

Dont use translation merge

It is not available with PHP builtin gettext, so it would have lead to inconsistency.

51 files changed:
Makefile
application/ApplicationUtils.php
application/Cache.php
application/FeedBuilder.php
application/History.php
application/Languages.php
application/LinkDB.php
application/LinkFilter.php
application/NetscapeBookmarkUtils.php
application/PageBuilder.php
application/PluginManager.php
application/Updater.php
application/Utils.php
application/config/ConfigJson.php
application/config/ConfigManager.php
application/config/ConfigPhp.php
application/config/exception/MissingFieldConfigException.php
application/config/exception/PluginConfigOrderException.php
application/config/exception/UnauthorizedConfigException.php
application/exceptions/IOException.php
composer.json
composer.lock
inc/languages/fr/LC_MESSAGES/shaarli.mo [new file with mode: 0644]
inc/languages/fr/LC_MESSAGES/shaarli.po [new file with mode: 0644]
index.php
plugins/TODO.md [deleted file]
plugins/addlink_toolbar/addlink_toolbar.php
plugins/archiveorg/archiveorg.html
plugins/archiveorg/archiveorg.php
plugins/demo_plugin/demo_plugin.php
plugins/isso/isso.php
plugins/markdown/help.html
plugins/markdown/markdown.php
plugins/piwik/piwik.php
plugins/playvideos/playvideos.php
plugins/pubsubhubbub/pubsubhubbub.php
plugins/qrcode/qrcode.meta
plugins/qrcode/qrcode.php
plugins/wallabag/wallabag.html
plugins/wallabag/wallabag.php
tests/LanguagesTest.php
tests/UtilsTest.php
tests/bootstrap.php [new file with mode: 0644]
tests/languages/bootstrap.php
tests/languages/fr/LanguagesFrTest.php [new file with mode: 0644]
tests/utils/languages/fr/LC_MESSAGES/test.mo [new file with mode: 0644]
tests/utils/languages/fr/LC_MESSAGES/test.po [new file with mode: 0644]
tpl/default/import.html
tpl/default/linklist.html
tpl/default/page.footer.html
tpl/default/pluginsadmin.html

index 656c27b03ab3664589123369ba770c31f44604de..300f1d7f50744aa520eaf001539ed445c4d9cd88 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ test:
        @echo "PHPUNIT"
        @echo "-------"
        @mkdir -p sandbox coverage
-       @$(BIN)/phpunit --coverage-php coverage/main.cov --testsuite unit-tests
+       @$(BIN)/phpunit --coverage-php coverage/main.cov --bootstrap tests/bootstrap.php --testsuite unit-tests
 
 locale_test_%:
        @UT_LOCALE=$*.utf8 \
index 5643f4a09706f2bb5b867153ee09c3c4974d77c3..911873a071ca7813f7ef9e3fa75d8ce18b2f8ec8 100644 (file)
@@ -149,12 +149,13 @@ class ApplicationUtils
     public static function checkPHPVersion($minVersion, $curVersion)
     {
         if (version_compare($curVersion, $minVersion) < 0) {
-            throw new Exception(
+            $msg = t(
                 'Your PHP version is obsolete!'
-                .' Shaarli requires at least PHP '.$minVersion.', and thus cannot run.'
-                .' Your PHP version has known security vulnerabilities and should be'
-                .' updated as soon as possible.'
+                 . ' Shaarli requires at least PHP %s, and thus cannot run.'
+                 . ' Your PHP version has known security vulnerabilities and should be'
+                 . ' updated as soon as possible.'
             );
+            throw new Exception(sprintf($msg, $minVersion));
         }
     }
 
@@ -179,7 +180,7 @@ class ApplicationUtils
             $rainTplDir.'/'.$conf->get('resource.theme'),
         ) as $path) {
             if (! is_readable(realpath($path))) {
-                $errors[] = '"'.$path.'" directory is not readable';
+                $errors[] = '"'.$path.'" '. t('directory is not readable');
             }
         }
 
@@ -191,10 +192,10 @@ class ApplicationUtils
             $conf->get('resource.raintpl_tmp'),
         ) as $path) {
             if (! is_readable(realpath($path))) {
-                $errors[] = '"'.$path.'" directory is not readable';
+                $errors[] = '"'.$path.'" '. t('directory is not readable');
             }
             if (! is_writable(realpath($path))) {
-                $errors[] = '"'.$path.'" directory is not writable';
+                $errors[] = '"'.$path.'" '. t('directory is not writable');
             }
         }
 
@@ -212,10 +213,10 @@ class ApplicationUtils
             }
 
             if (! is_readable(realpath($path))) {
-                $errors[] = '"'.$path.'" file is not readable';
+                $errors[] = '"'.$path.'" '. t('file is not readable');
             }
             if (! is_writable(realpath($path))) {
-                $errors[] = '"'.$path.'" file is not writable';
+                $errors[] = '"'.$path.'" '. t('file is not writable');
             }
         }
 
index 5d050165dbf7e8472094f7df8d984808cccb9413..e5d43e611656823eaef4b952219f9a7755198026 100644 (file)
@@ -13,7 +13,7 @@
 function purgeCachedPages($pageCacheDir)
 {
     if (! is_dir($pageCacheDir)) {
-        $error = 'Cannot purge '.$pageCacheDir.': no directory';
+        $error = sprintf(t('Cannot purge %s: no directory'), $pageCacheDir);
         error_log($error);
         return $error;
     }
index 7377bcec09c3fa21b92434953661133b386e0829..3cfaafb488037ee3e3167f8e1fb5d59cedcabb14 100644 (file)
@@ -148,9 +148,9 @@ class FeedBuilder
             $link['url'] = $pageaddr . $link['url'];
         }
         if ($this->usePermalinks === true) {
-            $permalink = '<a href="'. $link['url'] .'" title="Direct link">Direct link</a>';
+            $permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>';
         } else {
-            $permalink = '<a href="'. $link['guid'] .'" title="Permalink">Permalink</a>';
+            $permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>';
         }
         $link['description']  = format_description($link['description'], '', $pageaddr);
         $link['description'] .= PHP_EOL .'<br>&#8212; '. $permalink;
index 5e3b1b72d475ce97fd05692bec751e661b13e182..35ec016a9be27c374dec0e96cbcd0d27e20a119a 100644 (file)
@@ -171,7 +171,7 @@ class History
         }
 
         if (! is_writable($this->historyFilePath)) {
-            throw new Exception('History file isn\'t readable or writable');
+            throw new Exception(t('History file isn\'t readable or writable'));
         }
     }
 
@@ -182,7 +182,7 @@ class History
     {
         $this->history = FileUtils::readFlatDB($this->historyFilePath, []);
         if ($this->history === false) {
-            throw new Exception('Could not parse history file');
+            throw new Exception(t('Could not parse history file'));
         }
     }
 
index c8b0a25a355c2a60ffbaf33a16cb6d3d06ee1b5a..4ba32f29384d55490af8db47d5416de615216f80 100644 (file)
 <?php
 
+namespace Shaarli;
+
+use Gettext\GettextTranslator;
+use Gettext\Merge;
+use Gettext\Translations;
+use Gettext\Translator;
+use Gettext\TranslatorInterface;
+use Shaarli\Config\ConfigManager;
+
 /**
- * Wrapper function for translation which match the API
- * of gettext()/_() and ngettext().
+ * Class Languages
+ *
+ * Load Shaarli translations using 'gettext/gettext'.
+ * This class allows to either use PHP gettext extension, or a PHP implementation of gettext,
+ * with a fixed language, or dynamically using autoLocale().
  *
- * Not doing translation for now.
+ * Translation files PO/MO files follow gettext standard and must be placed under:
+ *   <translation path>/<language>/LC_MESSAGES/<domain>.[po|mo]
  *
- * @param string $text  Text to translate.
- * @param string $nText The plural message ID.
- * @param int    $nb    The number of items for plural forms.
+ * Pros/cons:
+ *   - gettext extension is faster
+ *   - gettext is very system dependent (PHP extension, the locale must be installed, and web server reloaded)
  *
- * @return String Text translated.
+ * Settings:
+ *   - translation.mode:
+ *     - auto: use default setting (PHP implementation)
+ *     - php: use PHP implementation
+ *     - gettext: use gettext wrapper
+ *   - translation.language:
+ *     - auto: use autoLocale() and the language change according to user HTTP headers
+ *     - fixed language: e.g. 'fr'
+ *   - translation.extensions:
+ *     - domain => translation_path: allow plugins and themes to extend the defaut extension
+ *       The domain must be unique, and translation path must be relative, and contains the tree mentioned above.
+ *
+ * @package Shaarli
  */
-function t($text, $nText = '', $nb = 0) {
-    if (empty($nText)) {
-        return $text;
+class Languages
+{
+    /**
+     * Core translations domain
+     */
+    const DEFAULT_DOMAIN = 'shaarli';
+
+    /**
+     * @var TranslatorInterface
+     */
+    protected $translator;
+
+    /**
+     * @var string
+     */
+    protected $language;
+
+    /**
+     * @var ConfigManager
+     */
+    protected $conf;
+
+    /**
+     * Languages constructor.
+     *
+     * @param string        $language lang determined by autoLocale(), can be override.
+     * @param ConfigManager $conf     instance.
+     */
+    public function __construct($language, $conf)
+    {
+        $this->conf = $conf;
+        $confLanguage = $this->conf->get('translation.language', 'auto');
+        if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) {
+            $this->language = substr($language, 0, 5);
+        } else {
+            $this->language = $confLanguage;
+        }
+
+        if (! extension_loaded('gettext')
+            || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php'])
+        ) {
+            $this->initPhpTranslator();
+        } else {
+            $this->initGettextTranslator();
+        }
+
+        // Register default functions (e.g. '__()') to use our Translator
+        $this->translator->register();
+    }
+
+    /**
+     * Initialize the translator using php gettext extension (gettext dependency act as a wrapper).
+     */
+    protected function initGettextTranslator ()
+    {
+        $this->translator = new GettextTranslator();
+        $this->translator->setLanguage($this->language);
+        $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages');
+
+        foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
+            if ($domain !== self::DEFAULT_DOMAIN) {
+                $this->translator->loadDomain($domain, $translationPath, false);
+            }
+        }
+    }
+
+    /**
+     * Initialize the translator using a PHP implementation of gettext.
+     *
+     * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language).
+     */
+    protected function initPhpTranslator()
+    {
+        $this->translator = new Translator();
+        $translations = new Translations();
+        // Core translations
+        try {
+            /** @var Translations $translations */
+            $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po');
+            $translations->setDomain('shaarli');
+            $this->translator->loadTranslations($translations);
+        } catch (\InvalidArgumentException $e) {}
+
+
+        // Extension translations (plugins, themes, etc.).
+        foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
+            if ($domain === self::DEFAULT_DOMAIN) {
+                continue;
+            }
+
+            try {
+                /** @var Translations $extension */
+                $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po');
+                $extension->setDomain($domain);
+                $this->translator->loadTranslations($extension);
+            } catch (\InvalidArgumentException $e) {}
+        }
+    }
+
+    /**
+     * Checks if a language string is valid.
+     *
+     * @param string $language e.g. 'fr' or 'en_US'
+     *
+     * @return bool true if valid, false otherwise
+     */
+    protected function isValidLanguage($language)
+    {
+        return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1;
     }
-    $actualForm = $nb > 1 ? $nText : $text;
-    return sprintf($actualForm, $nb);
 }
index 22c1f0ab5321b3ccb274931637ba642357ddecb6..f026a0418b98335ae861100968f3104f6f76a7b9 100644 (file)
@@ -133,16 +133,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess
     {
         // TODO: use exceptions instead of "die"
         if (!$this->loggedIn) {
-            die('You are not authorized to add a link.');
+            die(t('You are not authorized to add a link.'));
         }
         if (!isset($value['id']) || empty($value['url'])) {
-            die('Internal Error: A link should always have an id and URL.');
+            die(t('Internal Error: A link should always have an id and URL.'));
         }
         if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) {
-            die('You must specify an integer as a key.');
+            die(t('You must specify an integer as a key.'));
         }
         if ($offset !== null && $offset !== $value['id']) {
-            die('Array offset and link ID must be equal.');
+            die(t('Array offset and link ID must be equal.'));
         }
 
         // If the link exists, we reuse the real offset, otherwise new entry
@@ -248,13 +248,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess
         $this->links = array();
         $link = array(
             'id' => 1,
-            'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone',
+            'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'),
             'url'=>'https://shaarli.readthedocs.io',
-            'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login.
+            'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login.
 
-To learn how to use Shaarli, consult the link "Help/documentation" at the bottom of this page.
+To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page.
 
-You use the community supported version of the original Shaarli project, by Sebastien Sauvage.',
+You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'),
             'private'=>0,
             'created'=> new DateTime(),
             'tags'=>'opensource software'
@@ -264,9 +264,9 @@ You use the community supported version of the original Shaarli project, by Seba
 
         $link = array(
             'id' => 0,
-            'title'=>'My secret stuff... - Pastebin.com',
+            'title'=> t('My secret stuff... - Pastebin.com'),
             'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
-            'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.',
+            'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'),
             'private'=>1,
             'created'=> new DateTime('1 minute ago'),
             'tags'=>'secretstuff',
index 99ecd1e238640bbba9fc32c60cdaeaabedba16d8..12376e27dc3aefe540960afbb0c194ec6ad55c16 100644 (file)
@@ -444,5 +444,11 @@ class LinkFilter
 
 class LinkNotFoundException extends Exception
 {
-    protected $message = 'The link you are trying to reach does not exist or has been deleted.';
+    /**
+     * LinkNotFoundException constructor.
+     */
+    public function __construct()
+    {
+        $this->message =  t('The link you are trying to reach does not exist or has been deleted.');
+    }
 }
index 3179636799622178bf7132fc2ede3ed6fe6521fd..31a14537d101054149c97a6b2b72053ea21020e1 100644 (file)
@@ -32,11 +32,11 @@ class NetscapeBookmarkUtils
     {
         // see tpl/export.html for possible values
         if (! in_array($selection, array('all', 'public', 'private'))) {
-            throw new Exception('Invalid export selection: "'.$selection.'"');
+            throw new Exception(t('Invalid export selection:') .' "'.$selection.'"');
         }
 
         $bookmarkLinks = array();
-
+7
         foreach ($linkDb as $link) {
             if ($link['private'] != 0 && $selection == 'public') {
                 continue;
@@ -79,14 +79,14 @@ class NetscapeBookmarkUtils
         $duration=0
     )
     {
-        $status = 'File '.$filename.' ('.$filesize.' bytes) ';
+        $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize);
         if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) {
-            $status .= 'has an unknown file format. Nothing was imported.';
+            $status .= t('has an unknown file format. Nothing was imported.');
         } else {
-            $status .= 'was successfully processed in '. $duration .' seconds: ';
-            $status .= $importCount.' links imported, ';
-            $status .= $overwriteCount.' links overwritten, ';
-            $status .= $skipCount.' links skipped.';
+            $status .= vsprintf(
+                t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'),
+                [$duration, $importCount, $overwriteCount, $skipCount]
+            );
         }
         return $status;
     }
index 291860adeb61912af4f8671c8544849224804713..af29067173c3b3df87a6c39e79fe9ff7290d3efa 100644 (file)
@@ -159,9 +159,12 @@ class PageBuilder
      *
      * @param string $message A messate to display what is not found
      */
-    public function render404($message = 'The page you are trying to reach does not exist or has been deleted.')
+    public function render404($message = '')
     {
-        header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+        if (empty($message)) {
+            $message = t('The page you are trying to reach does not exist or has been deleted.');
+        }
+        header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found'));
         $this->tpl->assign('error_message', $message);
         $this->renderPage('404');
     }
index 59ece4fa9c735b8c2d150b939d48794fddeb2279..cf6038453e499e7e4030b68b1fffd13bfadbb79b 100644 (file)
@@ -188,6 +188,9 @@ class PluginManager
             $metaData[$plugin] = parse_ini_file($metaFile);
             $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins);
 
+            if (isset($metaData[$plugin]['description'])) {
+                $metaData[$plugin]['description'] = t($metaData[$plugin]['description']);
+            }
             // Read parameters and format them into an array.
             if (isset($metaData[$plugin]['parameters'])) {
                 $params = explode(';', $metaData[$plugin]['parameters']);
@@ -203,7 +206,7 @@ class PluginManager
                 $metaData[$plugin]['parameters'][$param]['value'] = '';
                 // Optional parameter description in parameter.PARAM_NAME=
                 if (isset($metaData[$plugin]['parameter.'. $param])) {
-                    $metaData[$plugin]['parameters'][$param]['desc'] = $metaData[$plugin]['parameter.'. $param];
+                    $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]);
                 }
             }
         }
@@ -237,6 +240,6 @@ class PluginFileNotFoundException extends Exception
      */
     public function __construct($pluginName)
     {
-        $this->message = 'Plugin "'. $pluginName .'" files not found.';
+        $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
     }
 }
index 72b2def019dea484d28b6de040097d614dc6e867..723a7a81dac89bbb1cc57d548c762ce698a4ae8c 100644 (file)
@@ -73,7 +73,7 @@ class Updater
         }
 
         if ($this->methods === null) {
-            throw new UpdaterException('Couldn\'t retrieve Updater class methods.');
+            throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.'));
         }
 
         foreach ($this->methods as $method) {
@@ -482,7 +482,7 @@ class UpdaterException extends Exception
         }
 
         if (! empty($this->method)) {
-            $out .= 'An error occurred while running the update '. $this->method . PHP_EOL;
+            $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL;
         }
 
         if (! empty($this->previous)) {
@@ -522,11 +522,11 @@ function read_updates_file($updatesFilepath)
 function write_updates_file($updatesFilepath, $updates)
 {
     if (empty($updatesFilepath)) {
-        throw new Exception('Updates file path is not set, can\'t write updates.');
+        throw new Exception(t('Updates file path is not set, can\'t write updates.'));
     }
 
     $res = file_put_contents($updatesFilepath, implode(';', $updates));
     if ($res === false) {
-        throw new Exception('Unable to write updates in '. $updatesFilepath . '.');
+        throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
     }
 }
index 4a2f5561cfdf5dfb38ed9a0a4c0f46b58b27c8c6..27eaafc5c9258820704213520a00db52812ab2e5 100644 (file)
@@ -452,7 +452,7 @@ function get_max_upload_size($limitPost, $limitUpload, $format = true)
  */
 function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
 {
-    $callback = function($a, $b) use ($reverse) {
+    $callback = function ($a, $b) use ($reverse) {
         // Collator is part of PHP intl.
         if (class_exists('Collator')) {
             $collator = new Collator(setlocale(LC_COLLATE, 0));
@@ -470,3 +470,18 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
         usort($data, $callback);
     }
 }
+
+/**
+ * Wrapper function for translation which match the API
+ * of gettext()/_() and ngettext().
+ *
+ * @param string $text   Text to translate.
+ * @param string $nText  The plural message ID.
+ * @param int    $nb     The number of items for plural forms.
+ * @param string $domain The domain where the translation is stored (default: shaarli).
+ *
+ * @return String Text translated.
+ */
+function t($text, $nText = '', $nb = 1, $domain = 'shaarli') {
+    return dn__($domain, $text, $nText, $nb);
+}
index 9ef2ef562634bb346cf398b290093cbd02e66926..8c8d5610efb145c49c3b319514f7e3ba0dab68ca 100644 (file)
@@ -22,10 +22,15 @@ class ConfigJson implements ConfigIO
         $data = json_decode($data, true);
         if ($data === null) {
             $errorCode = json_last_error();
-            $error  = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #';
-            $error .= $errorCode. '<br>âžś <code>' . json_last_error_msg() .'</code>';
+            $error  = sprintf(
+                'An error occurred while parsing JSON configuration file (%s): error code #%d',
+                $filepath,
+                $errorCode
+            );
+            $error .= '<br>âžś <code>' . json_last_error_msg() .'</code>';
             if ($errorCode === JSON_ERROR_SYNTAX) {
-                $error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
+                $error .= '<br>';
+                $error .= 'Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
                 $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.';
             }
             throw new \Exception($error);
@@ -44,8 +49,8 @@ class ConfigJson implements ConfigIO
         if (!file_put_contents($filepath, $data)) {
             throw new \IOException(
                 $filepath,
-                'Shaarli could not create the config file.
-                Please make sure Shaarli has the right to write in the folder is it installed in.'
+                t('Shaarli could not create the config file. '.
+                  'Please make sure Shaarli has the right to write in the folder is it installed in.')
             );
         }
     }
index 7ff2fe671e94b8b6809044a31d17331c92320237..9e4c9f6328d113d4de7dc1af370e6f8a26058a7f 100644 (file)
@@ -132,7 +132,7 @@ class ConfigManager
     public function set($setting, $value, $write = false, $isLoggedIn = false)
     {
         if (empty($setting) || ! is_string($setting)) {
-            throw new \Exception('Invalid setting key parameter. String expected, got: '. gettype($setting));
+            throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting));
         }
 
         // During the ConfigIO transition, map legacy settings to the new ones.
@@ -339,6 +339,10 @@ class ConfigManager
         $this->setEmpty('redirector.url', '');
         $this->setEmpty('redirector.encode_url', true);
 
+        $this->setEmpty('translation.language', 'auto');
+        $this->setEmpty('translation.mode', 'php');
+        $this->setEmpty('translation.extensions', []);
+
         $this->setEmpty('plugins', array());
     }
 
index 2633824d3197f209d3048f74949d95b96d128148..2f66e8e00ac18a537db96d77e2bf66e175c87c29 100644 (file)
@@ -118,8 +118,8 @@ class ConfigPhp implements ConfigIO
         ) {
             throw new \IOException(
                 $filepath,
-                'Shaarli could not create the config file.
-                Please make sure Shaarli has the right to write in the folder is it installed in.'
+                t('Shaarli could not create the config file. '.
+                  'Please make sure Shaarli has the right to write in the folder is it installed in.')
             );
         }
     }
index 6346c6a9ebbc60e6826753855c32cf5d8e680e02..9e0a93594d21f61ee76a8d85e2ea231f848ac83e 100644 (file)
@@ -18,6 +18,6 @@ class MissingFieldConfigException extends \Exception
     public function __construct($field)
     {
         $this->field = $field;
-        $this->message = 'Configuration value is required for '. $this->field;
+        $this->message = sprintf(t('Configuration value is required for %s'), $this->field);
     }
 }
index f9d68750030a9226f53a9b74058fa358fee2252d..f82ec26ed0d1a3bed7a19d9c5b9493e155f7362d 100644 (file)
@@ -12,6 +12,6 @@ class PluginConfigOrderException extends \Exception
      */
     public function __construct()
     {
-        $this->message = 'An error occurred while trying to save plugins loading order.';
+        $this->message = t('An error occurred while trying to save plugins loading order.');
     }
 }
index 79672c1bf95a3aa8019f09b853fc4d05162128e1..72311faeffc98ee0a76e7fc3bc884e6a69f8ca12 100644 (file)
@@ -13,6 +13,6 @@ class UnauthorizedConfigException extends \Exception
      */
     public function __construct()
     {
-        $this->message = 'You are not authorized to alter config.';
+        $this->message = t('You are not authorized to alter config.');
     }
 }
index b563b23d53671e37da9cce3fc6a25d6a428a1598..18e46b77a2291999eb9b3ee8656de4c4cb17efe6 100644 (file)
@@ -16,7 +16,7 @@ class IOException extends Exception
     public function __construct($path, $message = '')
     {
         $this->path = $path;
-        $this->message = empty($message) ? 'Error accessing' : $message;
+        $this->message = empty($message) ? t('Error accessing') : $message;
         $this->message .= ' "' . $this->path .'"';
     }
 }
index afb8aca4c42209a7f8ddc685a1c7626e416a313c..f331d6caf265174a86c537a303b2b38f95fe2d6b 100644 (file)
@@ -19,7 +19,8 @@
         "shaarli/netscape-bookmark-parser": "^2.0",
         "erusev/parsedown": "1.6",
         "slim/slim": "^3.0",
-        "pubsubhubbub/publisher": "dev-master"
+        "pubsubhubbub/publisher": "dev-master",
+        "gettext/gettext": "^4.4"
     },
     "require-dev": {
         "phpmd/phpmd" : "@stable",
index 435d6a8822f3b8fb2ecb799e4f96de7f255007d5..ea20025d1c9cc6bb44586e601587e4e30bff3afd 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "68beedbfa104c788029b079800cfd6e8",
+    "content-hash": "13b7e1e474fe9264b098ba86face0feb",
     "packages": [
         {
             "name": "container-interop/container-interop",
             ],
             "time": "2015-10-04T16:44:32+00:00"
         },
+        {
+            "name": "gettext/gettext",
+            "version": "v4.4.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/oscarotero/Gettext.git",
+                "reference": "4f57f004635cc6311a20815ebfdc0757cb337113"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/4f57f004635cc6311a20815ebfdc0757cb337113",
+                "reference": "4f57f004635cc6311a20815ebfdc0757cb337113",
+                "shasum": ""
+            },
+            "require": {
+                "gettext/languages": "^2.3",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "illuminate/view": "*",
+                "phpunit/phpunit": "^4.8|^5.7",
+                "squizlabs/php_codesniffer": "^3.0",
+                "symfony/yaml": "~2",
+                "twig/extensions": "*",
+                "twig/twig": "^1.31|^2.0"
+            },
+            "suggest": {
+                "illuminate/view": "Is necessary if you want to use the Blade extractor",
+                "symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator",
+                "twig/extensions": "Is necessary if you want to use the Twig extractor",
+                "twig/twig": "Is necessary if you want to use the Twig extractor"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Gettext\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Oscar Otero",
+                    "email": "oom@oscarotero.com",
+                    "homepage": "http://oscarotero.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP gettext manager",
+            "homepage": "https://github.com/oscarotero/Gettext",
+            "keywords": [
+                "JS",
+                "gettext",
+                "i18n",
+                "mo",
+                "po",
+                "translation"
+            ],
+            "time": "2017-08-09T16:59:46+00:00"
+        },
+        {
+            "name": "gettext/languages",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/mlocati/cldr-to-gettext-plural-rules.git",
+                "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/49c39e51569963cc917a924b489e7025bfb9d8c7",
+                "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4"
+            },
+            "bin": [
+                "bin/export-plural-rules",
+                "bin/export-plural-rules.php"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Gettext\\Languages\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michele Locati",
+                    "email": "mlocati@gmail.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "gettext languages with plural rules",
+            "homepage": "https://github.com/mlocati/cldr-to-gettext-plural-rules",
+            "keywords": [
+                "cldr",
+                "i18n",
+                "internationalization",
+                "l10n",
+                "language",
+                "languages",
+                "localization",
+                "php",
+                "plural",
+                "plural rules",
+                "plurals",
+                "translate",
+                "translations",
+                "unicode"
+            ],
+            "time": "2017-03-23T17:02:28+00:00"
+        },
         {
             "name": "katzgrau/klogger",
             "version": "1.2.1",
         },
         {
             "name": "phpdocumentor/reflection-docblock",
-            "version": "3.2.1",
+            "version": "3.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "183824db76118b9dddffc7e522b91fa175f75119"
+                "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/183824db76118b9dddffc7e522b91fa175f75119",
-                "reference": "183824db76118b9dddffc7e522b91fa175f75119",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157",
+                "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157",
                 "shasum": ""
             },
             "require": {
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2017-08-04T20:55:59+00:00"
+            "time": "2017-08-08T06:39:58+00:00"
         },
         {
             "name": "phpdocumentor/type-resolver",
         },
         {
             "name": "symfony/config",
-            "version": "v3.3.6",
+            "version": "v3.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297"
+                "reference": "6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/54ee12b0dd60f294132cabae6f5da9573d2e5297",
-                "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297",
+                "url": "https://api.github.com/repos/symfony/config/zipball/6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488",
+                "reference": "6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9",
+                "php": "^5.5.9|>=7.0.8",
                 "symfony/filesystem": "~2.8|~3.0"
             },
             "conflict": {
             ],
             "description": "Symfony Config Component",
             "homepage": "https://symfony.com",
-            "time": "2017-07-19T07:37:29+00:00"
+            "time": "2017-08-03T08:59:45+00:00"
         },
         {
             "name": "symfony/console",
-            "version": "v2.8.26",
+            "version": "v2.8.27",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "32a3c6b3398de5db8ed381f4ef92970c59c2fcdd"
+                "reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/32a3c6b3398de5db8ed381f4ef92970c59c2fcdd",
-                "reference": "32a3c6b3398de5db8ed381f4ef92970c59c2fcdd",
+                "url": "https://api.github.com/repos/symfony/console/zipball/c0807a2ca978e64d8945d373a9221a5c35d1a253",
+                "reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2017-07-29T21:26:04+00:00"
+            "time": "2017-08-27T14:29:03+00:00"
         },
         {
             "name": "symfony/debug",
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v3.3.6",
+            "version": "v3.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0"
+                "reference": "2ac658972626c75cbde7b0067c84b988170a6907"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8d70987f991481e809c63681ffe8ce3f3fde68a0",
-                "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2ac658972626c75cbde7b0067c84b988170a6907",
+                "reference": "2ac658972626c75cbde7b0067c84b988170a6907",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9",
+                "php": "^5.5.9|>=7.0.8",
                 "psr/container": "^1.0"
             },
             "conflict": {
             ],
             "description": "Symfony DependencyInjection Component",
             "homepage": "https://symfony.com",
-            "time": "2017-07-28T15:27:31+00:00"
+            "time": "2017-08-28T22:20:37+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v3.3.6",
+            "version": "v3.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "427987eb4eed764c3b6e38d52a0f87989e010676"
+                "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/427987eb4eed764c3b6e38d52a0f87989e010676",
-                "reference": "427987eb4eed764c3b6e38d52a0f87989e010676",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/b32a0e5f928d0fa3d1dd03c78d020777e50c10cb",
+                "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9"
+                "php": "^5.5.9|>=7.0.8"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2017-07-11T07:17:58+00:00"
+            "time": "2017-07-29T21:54:42+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v3.3.6",
+            "version": "v3.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4"
+                "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4",
-                "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/b2260dbc80f3c4198f903215f91a1ac7fe9fe09e",
+                "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9"
+                "php": "^5.5.9|>=7.0.8"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2017-06-01T21:01:25+00:00"
+            "time": "2017-07-29T21:54:42+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.4.0",
+            "version": "v1.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "f29dca382a6485c3cbe6379f0c61230167681937"
+                "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937",
-                "reference": "f29dca382a6485c3cbe6379f0c61230167681937",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
+                "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.4-dev"
+                    "dev-master": "1.5-dev"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2017-06-09T14:24:12+00:00"
+            "time": "2017-06-14T15:44:48+00:00"
         },
         {
             "name": "symfony/yaml",
-            "version": "v3.3.6",
+            "version": "v3.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed"
+                "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
-                "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
+                "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5.9"
+                "php": "^5.5.9|>=7.0.8"
             },
             "require-dev": {
                 "symfony/console": "~2.8|~3.0"
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2017-07-23T12:43:26+00:00"
+            "time": "2017-07-29T21:54:42+00:00"
         },
         {
             "name": "theseer/fdomdocument",
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.mo b/inc/languages/fr/LC_MESSAGES/shaarli.mo
new file mode 100644 (file)
index 0000000..d6b195d
Binary files /dev/null and b/inc/languages/fr/LC_MESSAGES/shaarli.mo differ
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
new file mode 100644 (file)
index 0000000..8763581
--- /dev/null
@@ -0,0 +1,1243 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Shaarli\n"
+"POT-Creation-Date: 2017-05-20 13:54+0200\n"
+"PO-Revision-Date: 2017-05-20 14:11+0200\n"
+"Last-Translator: \n"
+"Language-Team: Shaarli\n"
+"Language: fr_FR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.1\n"
+"X-Poedit-Basepath: ../../../..\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: t:1,2;t\n"
+"X-Poedit-SearchPath-0: .\n"
+
+#: application/ApplicationUtils.php:152
+#, php-format
+msgid ""
+"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
+"cannot run. Your PHP version has known security vulnerabilities and should "
+"be updated as soon as possible."
+msgstr ""
+"Votre version de PHP est obsolète ! Shaarli nĂ©cessite au moins PHP %s, et ne "
+"peut donc pas fonctionner. Votre version de PHP a des failles de sĂ©curitĂ©s "
+"connues et devrait ĂŞtre mise Ă  jour au plus tĂ´t."
+
+#: application/ApplicationUtils.php:180 application/ApplicationUtils.php:192
+msgid "directory is not readable"
+msgstr "le rĂ©pertoire n'est pas accessible en lecture"
+
+#: application/ApplicationUtils.php:195
+msgid "directory is not writable"
+msgstr "le rĂ©pertoire n'est pas accessible en Ă©criture"
+
+#: application/ApplicationUtils.php:213
+msgid "file is not readable"
+msgstr "le fichier n'est pas accessible en lecture"
+
+#: application/ApplicationUtils.php:216
+msgid "file is not writable"
+msgstr "le fichier n'est pas accessible en Ă©criture"
+
+#: application/Cache.php:16
+#, php-format
+msgid "Cannot purge %s: no directory"
+msgstr "Impossible de purger %s: le rĂ©pertoire n'existe pas"
+
+#: application/FeedBuilder.php:146
+msgid "Direct link"
+msgstr "Liens directs"
+
+#: application/FeedBuilder.php:148
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:242
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:245
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+msgid "Permalink"
+msgstr "Permalien"
+
+#: application/History.php:158
+msgid "History file isn't readable or writable"
+msgstr "Le fichier d'historique n'est pas accessible en lecture ou en Ă©criture"
+
+#: application/History.php:169
+msgid "Could not parse history file"
+msgstr "Format incorrect pour le fichier d'historique"
+
+#: application/LinkDB.php:136
+msgid "You are not authorized to add a link."
+msgstr "Vous n'ĂŞtes pas autorisĂ© Ă  ajouter un lien."
+
+#: application/LinkDB.php:139
+msgid "Internal Error: A link should always have an id and URL."
+msgstr "Erreur interne : un lien devrait toujours avoir un ID et une URL."
+
+#: application/LinkDB.php:142
+msgid "You must specify an integer as a key."
+msgstr "Vous devez utiliser un entier comme clĂ©."
+
+#: application/LinkDB.php:145
+msgid "Array offset and link ID must be equal."
+msgstr "La clĂ© du tableau et l'ID du lien doivent ĂŞtre Ă©gaux."
+
+#: application/LinkDB.php:251
+#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
+msgid ""
+"The personal, minimalist, super-fast, database free, bookmarking service"
+msgstr ""
+"Le gestionnaire de marque-page personnel, minimaliste, et sans base de "
+"données"
+
+#: application/LinkDB.php:253
+msgid ""
+"Welcome to Shaarli! This is your first public bookmark. To edit or delete "
+"me, you must first login.\n"
+"\n"
+"To learn how to use Shaarli, consult the link \"Documentation\" at the "
+"bottom of this page.\n"
+"\n"
+"You use the community supported version of the original Shaarli project, by "
+"Sebastien Sauvage."
+msgstr ""
+"Bienvenue sur Shaarli ! Ceci est votre premier marque-page public. Pour me "
+"modifier ou me supprimer, vous devez d'abord vous connecter.\n"
+"\n"
+"Pour apprendre comment utiliser Shaarli, consultez le lien Â« Documentation Â» "
+"en bas de page.\n"
+"\n"
+"Vous utilisez la version supportĂ©e par la communautĂ© du projet original "
+"Shaarli, de SĂ©bastien Sauvage."
+
+#: application/LinkDB.php:267
+msgid "My secret stuff... - Pastebin.com"
+msgstr "Mes trucs secrets... - Pastebin.com"
+
+#: application/LinkDB.php:269
+msgid "Shhhh! I'm a private link only YOU can see. You can delete me too."
+msgstr ""
+"Pssst ! Je suis un lien privĂ© que VOUS ĂŞtes le seul Ă  voir. Vous pouvez me "
+"supprimer aussi."
+
+#: application/LinkFilter.php:376
+msgid "The link you are trying to reach does not exist or has been deleted."
+msgstr "Le lien que vous essayez de consulter n'existe pas ou a Ă©tĂ© supprimĂ©."
+
+#: application/NetscapeBookmarkUtils.php:35
+msgid "Invalid export selection:"
+msgstr "SĂ©lection d'export invalide :"
+
+#: application/NetscapeBookmarkUtils.php:80
+#, php-format
+msgid "File %s (%d bytes) "
+msgstr "Le fichier %s (%d octets) "
+
+#: application/NetscapeBookmarkUtils.php:82
+msgid "has an unknown file format. Nothing was imported."
+msgstr "a un format inconnu. Rien n'a Ă©tĂ© importĂ©."
+
+#: application/NetscapeBookmarkUtils.php:85
+#, php-format
+msgid ""
+"was successfully processed: %d links imported, %d links overwritten, %d "
+"links skipped."
+msgstr ""
+"a Ă©tĂ© importĂ© avec succès : %d liens importĂ©s, %d liens Ă©crasĂ©s, %d liens "
+"ignorés."
+
+#: application/PageBuilder.php:159
+msgid "The page you are trying to reach does not exist or has been deleted."
+msgstr "La page que vous essayez de consulter n'existe pas ou a Ă©tĂ© supprimĂ©e."
+
+#: application/PageBuilder.php:161
+#, fuzzy
+#| msgid " 404 Not Found"
+msgid "404 Not Found"
+msgstr "404 Introuvable"
+
+#: application/PluginManager.php:243
+#, php-format
+msgid "Plugin \"%s\" files not found."
+msgstr "Les fichiers de l'extension \"%s\" sont introuvables."
+
+#: application/Updater.php:76
+msgid "Couldn't retrieve Updater class methods."
+msgstr "Impossible de rĂ©cupĂ©rer les mĂ©thodes de la classe Updater."
+
+#: application/Updater.php:500
+msgid "An error occurred while running the update "
+msgstr "Une erreur s'est produite lors de l'exĂ©cution de la mise Ă  jour "
+
+#: application/Updater.php:540
+msgid "Updates file path is not set, can't write updates."
+msgstr ""
+"Le chemin vers le fichier de mise Ă  jour n'est pas dĂ©fini, impossible "
+"d'Ă©crire les mises Ă  jour."
+
+#: application/Updater.php:545
+msgid "Unable to write updates in "
+msgstr "Impossible d'Ă©crire les mises Ă  jour dans "
+
+#: application/Utils.php:402 tests/UtilsTest.php:398
+msgid "Setting not set"
+msgstr "Paramètre non dĂ©fini"
+
+#: application/Utils.php:409 tests/UtilsTest.php:396 tests/UtilsTest.php:397
+msgid "Unlimited"
+msgstr "IllimitĂ©"
+
+#: application/Utils.php:412 tests/UtilsTest.php:393 tests/UtilsTest.php:394
+#: tests/UtilsTest.php:408
+msgid "B"
+msgstr "o"
+
+#: application/Utils.php:412 tests/UtilsTest.php:387 tests/UtilsTest.php:388
+#: tests/UtilsTest.php:395
+msgid "kiB"
+msgstr "ko"
+
+#: application/Utils.php:412 tests/UtilsTest.php:389 tests/UtilsTest.php:390
+#: tests/UtilsTest.php:406 tests/UtilsTest.php:407
+msgid "MiB"
+msgstr "Mo"
+
+#: application/Utils.php:412 tests/UtilsTest.php:391 tests/UtilsTest.php:392
+msgid "GiB"
+msgstr "Go"
+
+#: application/config/ConfigJson.php:26
+#, php-format
+msgid ""
+"An error occurred while parsing JSON configuration file (%s): error code #%d"
+msgstr ""
+"Une erreur s'est produite lors de la lecture du fichier de configuration "
+"JSON (%s) : code d'erreur #%d"
+
+#: application/config/ConfigJson.php:33
+msgid ""
+"Please check your JSON syntax (without PHP comment tags) using a JSON lint "
+"tool such as "
+msgstr ""
+"Merci de vĂ©rifier la syntaxe JSON (sans les balises de commentaires PHP) en "
+"utilisant un validateur de JSON tel que "
+
+#: application/config/ConfigJson.php:52 application/config/ConfigPhp.php:121
+msgid ""
+"Shaarli could not create the config file. Please make sure Shaarli has the "
+"right to write in the folder is it installed in."
+msgstr ""
+"Shaarli n'a pas pu crĂ©er le fichier de configuration. Merci de vĂ©rifier que "
+"Shaarli a les droits d'Ă©criture dans le dossier dans lequel il est installĂ©."
+
+#: application/config/ConfigManager.php:135
+msgid "Invalid setting key parameter. String expected, got: "
+msgstr "ClĂ© de paramĂ©trage invalide. ChaĂ®ne de caractères obtenue, attendu :"
+
+#: application/config/exception/MissingFieldConfigException.php:21
+#, php-format
+msgid "Configuration value is required for %s"
+msgstr "Le paramètre %s est obligatoire"
+
+#: application/config/exception/PluginConfigOrderException.php:15
+msgid "An error occurred while trying to save plugins loading order."
+msgstr ""
+"Une erreur s'est produite lors de la sauvegarde de l'ordre des extensions."
+
+#: application/config/exception/UnauthorizedConfigException.php:16
+msgid "You are not authorized to alter config."
+msgstr "Vous n'ĂŞtes pas autorisĂ© Ă  modifier la configuration."
+
+#: application/exceptions/IOException.php:19
+msgid "Error accessing"
+msgstr "Une erreur s'est produite en accĂ©dant Ă "
+
+#: index.php:48
+msgid ""
+"Error: missing Composer dependencies\n"
+"\n"
+"If you installed Shaarli through Git or using the development branch,\n"
+"please refer to the installation documentation to install PHP dependencies "
+"using Composer:\n"
+msgstr ""
+"Erreur : les dĂ©pendances Composer sont manquantes\n"
+"\n"
+"Si vous avez installĂ© Shaarli avec Git ou depuis la branche de "
+"développement\n"
+"merci de consulter la documentation d'installation pour installer les "
+"dĂ©pendances Composer :\n"
+"\n"
+
+#: index.php:137
+msgid "Shared links on "
+msgstr "Liens partagĂ©s sur "
+
+#: index.php:168
+msgid "Insufficient permissions:"
+msgstr "Permissions insuffisantes :"
+
+#: index.php:415
+msgid "I said: NO. You are banned for the moment. Go away."
+msgstr "NON. Vous ĂŞtes banni pour le moment. Revenez plus tard."
+
+#: index.php:479
+msgid "Wrong login/password."
+msgstr "Nom d'utilisateur ou mot de passe incorrects."
+
+#: index.php:1072
+msgid "You are not supposed to change a password on an Open Shaarli."
+msgstr ""
+"Vous n'ĂŞtes pas censĂ© modifier le mot de passe d'un Shaarli en mode ouvert."
+
+#: index.php:1077 index.php:1118 index.php:1189 index.php:1243 index.php:1350
+msgid "Wrong token."
+msgstr "Jeton invalide."
+
+#: index.php:1082
+msgid "The old password is not correct."
+msgstr "L'ancien mot de passe est incorrect."
+
+#: index.php:1102
+msgid "Your password has been changed"
+msgstr "Votre mot de passe a Ă©tĂ© modifiĂ©"
+
+#: index.php:1153
+msgid "Configuration was saved."
+msgstr "La configuration a Ă©tĂ© sauvegardĂ©."
+
+#: index.php:1206
+#, php-format
+msgid "Tag was removed from %d links."
+msgstr "Le tag a Ă©tĂ© supprimĂ© de %d liens."
+
+#: index.php:1225
+#, php-format
+msgid "Tag was renamed in %d links."
+msgstr "Le tag a Ă©tĂ© renommĂ© dans %d liens."
+
+#: index.php:1544
+#, php-format
+msgid ""
+"The file you are trying to upload is probably bigger than what this "
+"webserver can accept (%s). Please upload in smaller chunks."
+msgstr ""
+"Le fichier que vous essayer d'envoyer est probablement plus lourd que ce que "
+"le serveur web peut accepter (%s). Merci de l'envoyer en parties plus "
+"légères."
+
+#: index.php:1941
+#, php-format
+msgid ""
+"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
+"variable \"session.save_path\" is set correctly in your PHP config, and that "
+"you have write access to it.<br>It currently points to %s.<br>On some "
+"browsers, accessing your server via a hostname like 'localhost' or any "
+"custom hostname without a dot causes cookie storage to fail. We recommend "
+"accessing your server via it's IP address or Fully Qualified Domain Name.<br>"
+msgstr ""
+"<pre>Les sesssions ne semble pas fonctionner sur ce serveur.<br>Assurez vous "
+"que la variable Â« session.save_path Â» est correctement dĂ©finie dans votre "
+"fichier de configuration PHP, et que vous y avez les droits d'Ă©criture."
+"<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains navigateurs, "
+"accĂ©der Ă  votre serveur depuis un nom d'hĂ´te comme Â« localhost Â» ou autre "
+"nom personnalisĂ© sans point '.' entraine l'Ă©chec de la sauvegarde des "
+"cookies. Nous vous recommandons d'accĂ©der Ă  votre serveur depuis son adresse "
+"IP ou un <em>Fully Qualified Domain Name</em>.<br>"
+
+#: index.php:1951
+msgid "Click to try again."
+msgstr "Cliquer ici pour rĂ©essayer."
+
+#: plugins/addlink_toolbar/addlink_toolbar.php:29
+msgid "URI"
+msgstr "URI"
+
+#: plugins/addlink_toolbar/addlink_toolbar.php:33
+#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+msgid "Add link"
+msgstr "Shaare"
+
+#: plugins/addlink_toolbar/addlink_toolbar.php:50
+msgid "Adds the addlink input on the linklist page."
+msgstr "Ajout le formulaire d'ajout de liens sur la page principale."
+
+#: plugins/archiveorg/archiveorg.php:23
+msgid "View on archive.org"
+msgstr "Voir sur archive.org"
+
+#: plugins/archiveorg/archiveorg.php:36
+msgid "For each link, add an Archive.org icon."
+msgstr "Pour chaque lien, ajoute une icĂ´ne pour Archive.org."
+
+#: plugins/demo_plugin/demo_plugin.php:443
+msgid ""
+"A demo plugin covering all use cases for template designers and plugin "
+"developers."
+msgstr ""
+"Une extension de dĂ©monstration couvrant tous les cas d'utilisation pour les "
+"designers et les dĂ©veloppeurs."
+
+#: plugins/isso/isso.php:20
+msgid ""
+"Isso plugin error: Please define the \"ISSO_SERVER\" setting in the plugin "
+"administration page."
+msgstr ""
+"Erreur de l'extension Isso : Merci de dĂ©finir le paramètre Â« ISSO_SERVER Â» "
+"dans la page d'administration des extensions."
+
+#: plugins/isso/isso.php:63
+msgid "Let visitor comment your shaares on permalinks with Isso."
+msgstr ""
+"Permet aux visiteurs de commenter vos shaares sur les permaliens avec Isso."
+
+#: plugins/isso/isso.php:64
+msgid "Isso server URL (without 'http://')"
+msgstr "URL du serveur Isso (sans 'http://')"
+
+#: plugins/markdown/markdown.php:150
+msgid "Description will be rendered with"
+msgstr "La description sera gĂ©nĂ©rĂ©e avec"
+
+#: plugins/markdown/markdown.php:151
+msgid "Markdown syntax documentation"
+msgstr "Documentation sur la syntaxe Markdown"
+
+#: plugins/markdown/markdown.php:152
+msgid "Markdown syntax"
+msgstr "la syntaxe Markdown"
+
+#: plugins/markdown/markdown.php:311
+msgid ""
+"Render shaare description with Markdown syntax.<br><strong>Warning</"
+"strong>:\n"
+"If your shaared descriptions contained HTML tags before enabling the "
+"markdown plugin,\n"
+"enabling it might break your page.\n"
+"See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
+"markdown#html-rendering\">README</a>."
+msgstr ""
+"Utilise la syntaxe Markdown pour la description des liens."
+"<br><strong>Attention</strong> :\n"
+"Si vous aviez des descriptions contenant du HTML avant d'activer cette "
+"extension,\n"
+"l'activer pourrait dĂ©former vos pages.\n"
+"Voir le <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
+"markdown#html-rendering\">README</a>."
+
+#: plugins/piwik/piwik.php:21
+msgid ""
+"Piwik plugin error: Please define PIWIK_URL and PIWIK_SITEID in the plugin "
+"administration page."
+msgstr ""
+"Erreur de l'extension Piwik : Merci de dĂ©finir les paramètres PIWIK_URL et "
+"PIWIK_SITEID dans la page d'administration des extensions."
+
+#: plugins/piwik/piwik.php:70
+msgid "A plugin that adds Piwik tracking code to Shaarli pages."
+msgstr "Ajoute le code de traçage de Piwik sur les pages de Shaarli."
+
+#: plugins/piwik/piwik.php:71
+msgid "Piwik URL"
+msgstr "URL de Piwik"
+
+#: plugins/piwik/piwik.php:72
+msgid "Piwik site ID"
+msgstr "Site ID de Piwik"
+
+#: plugins/playvideos/playvideos.php:22
+msgid "Video player"
+msgstr "Lecteur vidĂ©o"
+
+#: plugins/playvideos/playvideos.php:25
+msgid "Play Videos"
+msgstr "Jouer les vidĂ©os"
+
+#: plugins/playvideos/playvideos.php:56
+msgid "Add a button in the toolbar allowing to watch all videos."
+msgstr ""
+"Ajoute un bouton dans la barre de menu pour regarder toutes les vidĂ©os."
+
+#: plugins/playvideos/youtube_playlist.js:214
+msgid "plugins/playvideos/jquery-1.11.2.min.js"
+msgstr ""
+
+#: plugins/pubsubhubbub/pubsubhubbub.php:69
+#, php-format
+msgid "Could not publish to PubSubHubbub: %s"
+msgstr "Impossible de publier vers PubSubHubbub : %s"
+
+#: plugins/pubsubhubbub/pubsubhubbub.php:95
+#, php-format
+msgid "Could not post to %s"
+msgstr "Impossible de publier vers %s"
+
+#: plugins/pubsubhubbub/pubsubhubbub.php:99
+#, php-format
+msgid "Bad response from the hub %s"
+msgstr "Mauvaise rĂ©ponse du hub %s"
+
+#: plugins/pubsubhubbub/pubsubhubbub.php:110
+msgid "Enable PubSubHubbub feed publishing."
+msgstr "Active la publication de flux vers PubSubHubbub"
+
+#: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68
+msgid "For each link, add a QRCode icon."
+msgstr "Pour chaque liens, ajouter une icĂ´ne de QRCode."
+
+#: plugins/wallabag/wallabag.php:21
+msgid ""
+"Wallabag plugin error: Please define the \"WALLABAG_URL\" setting in the "
+"plugin administration page."
+msgstr ""
+"Erreur de l'extension Wallabag : Merci de dĂ©finir le paramètre Â« "
+"WALLABAG_URL Â» dans la page d'administration des extensions."
+
+#: plugins/wallabag/wallabag.php:47
+msgid "Save to wallabag"
+msgstr "Sauvegarder dans Wallabag"
+
+#: plugins/wallabag/wallabag.php:69
+msgid "Wallabag API URL"
+msgstr "URL de l'API Wallabag "
+
+#: plugins/wallabag/wallabag.php:70
+msgid "Wallabag API version (1 or 2)"
+msgstr "Version de l'API Wallabag (1 ou 2)"
+
+#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
+msgid "Shaare a new link"
+msgstr "Partager un nouveau lien"
+
+#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "URL or leave empty to post a note"
+msgstr "URL ou laisser vide pour crĂ©er une note"
+
+#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+msgid "Change password"
+msgstr "Modification du mot de passe"
+
+#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Current password"
+msgstr "Mot de passe actuel"
+
+#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+msgid "New password"
+msgstr "Nouveau mot de passe\t"
+
+#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
+msgid "Change"
+msgstr "Changer"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+msgid "Manage tags"
+msgstr "GĂ©rer les tags"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
+msgid "Tag"
+msgstr "Tag"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
+msgid "New name"
+msgstr "Nouveau nom"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
+msgid "Case sensitive"
+msgstr "Sensible Ă  la casse"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
+msgid "Rename"
+msgstr "Renommer"
+
+#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
+#: tmp/editlink.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:60
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:288
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:313
+msgid "Delete"
+msgstr "Supprimer"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
+msgid "Configure"
+msgstr "Configurer"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+msgid "title"
+msgstr "titre"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
+msgid "Home link"
+msgstr "Lien vers l'accueil"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
+msgid "Default value"
+msgstr "Valeur par dĂ©faut"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
+msgid "Theme"
+msgstr "Thème"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
+msgid "Timezone"
+msgstr "Fuseau horaire"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+msgid "Continent"
+msgstr "Continent"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
+msgid "City"
+msgstr "Ville"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
+msgid "Redirector"
+msgstr "Redirecteur"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
+msgid "e. g."
+msgstr "ex :"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
+msgid "will mask the HTTP_REFERER"
+msgstr "masque le HTTP_REFERER"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
+msgid "Disable session cookie hijacking protection"
+msgstr "DĂ©sactiver la protection contre le dĂ©tournement de cookies"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
+msgid "Check this if you get disconnected or if your IP address changes often"
+msgstr ""
+"Cocher cette case si vous ĂŞtes souvent dĂ©connectĂ© ou si votre adresse IP "
+"change souvent"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
+msgid "Private links by default"
+msgstr "Liens privĂ©s par dĂ©faut"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
+msgid "All new links are private by default"
+msgstr "Tous les nouveaux liens sont privĂ©s par dĂ©faut"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:185
+msgid "RSS direct links"
+msgstr "Liens directs dans le flux RSS"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:186
+msgid "Check this to use direct URL instead of permalink in feeds"
+msgstr ""
+"Cocher cette case pour utiliser des liens directs au lieu des permaliens "
+"dans le flux RSS"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:201
+msgid "Hide public links"
+msgstr "Cacher les liens publics"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:202
+msgid "Do not show any links if the user is not logged in"
+msgstr "N'afficher aucun lien sans ĂŞtre connectĂ©"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:217
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95
+msgid "Check updates"
+msgstr "VĂ©rifier les mises Ă  jour"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:218
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:97
+msgid "Notify me when a new release is ready"
+msgstr "Me notifier lorsqu'une nouvelle version est disponible"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:233
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:114
+msgid "Enable REST API"
+msgstr "Activer l'API REST"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:234
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:115
+msgid "Allow third party software to use Shaarli such as mobile application"
+msgstr ""
+"Permets aux applications tierces d'utiliser Shaarli, par exemple les "
+"applications mobiles"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:249
+msgid "API secret"
+msgstr "ClĂ© d'API secrète"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:260
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:192
+msgid "Save"
+msgstr "Enregistrer"
+
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
+msgid "The Daily Shaarli"
+msgstr "Le Quotidien Shaarli"
+
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
+msgid "1 RSS entry per day"
+msgstr "1 entrĂ©e RSS par jour"
+
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
+msgid "Previous day"
+msgstr "Jour prĂ©cĂ©dent"
+
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
+msgid "All links of one day in a single page."
+msgstr "Tous les liens d'un jour sur une page."
+
+#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
+#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
+msgid "Next day"
+msgstr "Jour suivant"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26
+msgid "Shaare"
+msgstr "Shaare"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
+msgid "URL"
+msgstr "URL"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:27
+msgid "Title"
+msgstr "Titre"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124
+msgid "Description"
+msgstr "Description"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
+msgid "Tags"
+msgstr "Tags"
+
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:177
+msgid "Private"
+msgstr "PrivĂ©"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Export Database"
+msgstr "Exporter les donnĂ©es"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
+msgid "Selection"
+msgstr "Choisir"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
+msgid "All"
+msgstr "Tous"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+msgid "Public"
+msgstr "Publics"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
+msgid "Prepend note permalinks with this Shaarli instance's URL"
+msgstr "PrĂ©fixer les liens de notes avec l'URL de l'instance de Shaarli"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
+msgid "Useful to import bookmarks in a web browser"
+msgstr "Utile pour importer les marques-pages dans un navigateur"
+
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
+msgid "Export"
+msgstr "Exporter"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Import Database"
+msgstr "Importer des donnĂ©es"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
+msgid "Maximum size allowed:"
+msgstr "Taille maximum autorisĂ©e :"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+msgid "Visibility"
+msgstr "VisibilitĂ©"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
+msgid "Use values from the imported file, default to public"
+msgstr ""
+"Utiliser les valeurs prĂ©sentes dans le fichier d'import, public par dĂ©faut"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+msgid "Import all bookmarks as private"
+msgstr "Importer tous les liens comme privĂ©s"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
+msgid "Import all bookmarks as public"
+msgstr "Importer tous les liens comme publics"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
+msgid "Overwrite existing bookmarks"
+msgstr "Remplacer les liens existants"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
+msgid "Duplicates based on URL"
+msgstr "Les doublons s'appuient sur les URL"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
+msgid "Add default tags"
+msgstr "Ajouter des tags par dĂ©faut"
+
+#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
+msgid "Import"
+msgstr "Importer"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
+msgid "Install Shaarli"
+msgstr "Installation de Shaarli"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
+msgid "It looks like it's the first time you run Shaarli. Please configure it."
+msgstr ""
+"Il semblerait que Ă§a soit la première fois que vous lancez Shaarli. Merci de "
+"le configurer."
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:149
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:150
+msgid "Password"
+msgstr "Mot de passe"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80
+msgid "Shaarli title"
+msgstr "Titre du Shaarli"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
+msgid "My links"
+msgstr "Mes liens"
+
+#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
+msgid "Install"
+msgstr "Installer"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
+msgid "shaare"
+msgid_plural "shaares"
+msgstr[0] "shaare"
+msgstr[1] "shaares"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
+msgid "private link"
+msgid_plural "private links"
+msgstr[0] "lien privĂ©"
+msgstr[1] "liens privĂ©s"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:119
+msgid "Search text"
+msgstr "Recherche texte"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:131
+msgid "Filter by tag"
+msgstr "Filtrer par tag"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118
+msgid "Nothing found."
+msgstr "Aucun rĂ©sultat."
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:126
+#, php-format
+msgid "%s result"
+msgid_plural "%s results"
+msgstr[0] "%s rĂ©sultat"
+msgstr[1] "%s rĂ©sultats"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130
+msgid "for"
+msgstr "pour"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:137
+msgid "tagged"
+msgstr "taggĂ©"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:141
+msgid "Remove tag"
+msgstr "Retirer le tag"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
+msgid "with status"
+msgstr "avec le statut"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:181
+msgid "Edit"
+msgstr "Modifier"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
+msgid "Fold"
+msgstr "Replier"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:245
+msgid "Edited: "
+msgstr "ModifiĂ© :"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:257
+msgid "permalink"
+msgstr "permalien"
+
+#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7
+msgid "Filters"
+msgstr "Filtres"
+
+#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12
+msgid "Filter private links"
+msgstr "Filtrer par liens privĂ©s"
+
+#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:63
+msgid "Links per page"
+msgstr "Liens par page"
+
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
+msgid ""
+"You have been banned after too many failed login attempts. Try again later."
+msgstr ""
+"Vous avez Ă©tĂ© banni après trop d'Ă©chec d'authentification. Merci de "
+"rĂ©essayer plus tard."
+
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:71
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:95
+msgid "Login"
+msgstr "Connexion"
+
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:153
+msgid "Remember me"
+msgstr "Rester connectĂ©"
+
+#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
+msgid "by the Shaarli community"
+msgstr "par la communautĂ© Shaarli"
+
+#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15
+msgid "Documentation"
+msgstr "Documentation"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
+msgid "Tools"
+msgstr "Outils"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
+#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+#: tmp/tagcloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Tag cloud"
+msgstr "Nuage de tags"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:39
+msgid "Picture wall"
+msgstr "Mur d'images"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:42
+msgid "Daily"
+msgstr "Quotidien"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:61
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:86
+msgid "RSS Feed"
+msgstr "Flux RSS"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:66
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:102
+msgid "Logout"
+msgstr "DĂ©connexion"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:81
+msgid "Search"
+msgstr "Rechercher"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:171
+msgid "is available"
+msgstr "est disponible"
+
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:178
+msgid "Error"
+msgstr "Erreur"
+
+#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Picture Wall"
+msgstr "Mur d'images"
+
+#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "pics"
+msgstr "images"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
+msgid "You need to enable Javascript to change plugin loading order."
+msgstr ""
+"Vous devez activer Javascript pour pouvoir modifier l'ordre des extensions."
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
+msgid "Plugin administration"
+msgstr "Administration des extensions"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+msgid "Enabled Plugins"
+msgstr "Extensions activĂ©es"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155
+msgid "No plugin enabled."
+msgstr "Aucune extension activĂ©e."
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:73
+msgid "Disable"
+msgstr "DĂ©sactiver"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:98
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
+msgid "Name"
+msgstr "Nom"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
+msgid "Order"
+msgstr "Ordre"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
+msgid "Disabled Plugins"
+msgstr "Extensions dĂ©sactivĂ©es"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
+msgid "No plugin disabled."
+msgstr "Aucune extension dĂ©sactivĂ©e."
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:97
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
+msgid "Enable"
+msgstr "Activer"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
+msgid "More plugins available"
+msgstr "Plus d'extensions disponibles"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
+msgid "in the documentation"
+msgstr "dans la documentation"
+
+#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
+msgid "Plugin configuration"
+msgstr "Configuration des extensions"
+
+#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+#: tmp/tagcloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "tags"
+msgstr "tags"
+
+#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+msgid "Tag list"
+msgstr "List des tags"
+
+#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3
+msgid "Sort by:"
+msgstr "Trier par :"
+
+#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5
+msgid "Cloud"
+msgstr "Nuage"
+
+#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6
+msgid "Most used"
+msgstr "Plus utilisĂ©s"
+
+#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7
+msgid "Alphabetical"
+msgstr "AlphabĂ©tique"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
+msgid "Settings"
+msgstr "Paramètres"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
+msgid "Change Shaarli settings: title, timezone, etc."
+msgstr "Changer les paramètres de Shaarli : titre, fuseau horaire, etc."
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
+msgid "Configure your Shaarli"
+msgstr "Conguration de Shaarli"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
+msgid "Enable, disable and configure plugins"
+msgstr "Activer, dĂ©sactiver et configurer les extensions"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+msgid "Change your password"
+msgstr "Modification du mot de passe"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
+msgid "Rename or delete a tag in all links"
+msgstr "Rename or delete a tag in all links"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+msgid ""
+"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
+"delicious...)"
+msgstr ""
+"Importer des marques pages au format Netscape HTML (comme exportĂ©s depuis "
+"Firefox, Chrome, Opera, delicious...)"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
+msgid "Import links"
+msgstr "Importer des liens"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
+msgid ""
+"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
+"Opera, delicious...)"
+msgstr ""
+"Exporter les marques pages au format Netscape HTML (comme exportĂ©s depuis "
+"Firefox, Chrome, Opera, delicious...)"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
+msgid "Export database"
+msgstr "Exporter les donnĂ©es"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
+msgid ""
+"Drag one of these button to your bookmarks toolbar or right-click it and "
+"\"Bookmark This Link\""
+msgstr ""
+"Glisser un de ces bouttons dans votre barre de favoris ou cliquer droit "
+"dessus et Â« Ajouter aux favoris Â»"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
+msgid "then click on the bookmarklet in any page you want to share."
+msgstr ""
+"puis cliquer sur le marque page depuis un site que vous souhaitez partager."
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95
+msgid ""
+"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
+"Link"
+msgstr ""
+"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et Â« "
+"Ajouter aux favoris Â»"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
+msgid "then click âśšShaare link button in any page you want to share"
+msgstr "puis cliquer sur âśšShaare depuis un site que vous souhaitez partager"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
+msgid "Shaare link"
+msgstr "Shaare"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96
+msgid ""
+"Then click âśšAdd Note button anytime to start composing a private Note (text "
+"post) to your Shaarli"
+msgstr ""
+"Puis cliquer sur âśšAdd Note pour commencer Ă  rĂ©diger une Note sur Shaarli"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
+msgid "Add Note"
+msgstr "Ajouter une Note"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111
+msgid ""
+"You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
+"functionality."
+msgstr ""
+"Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
+"fonctionalité."
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116
+msgid "Add to"
+msgstr "Ajouter Ă "
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
+msgid "3rd party"
+msgstr "Applications tierces"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
+msgid "Plugin"
+msgstr "Extension"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
+msgid "plugin"
+msgstr "extension"
+
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:157
+msgid ""
+"Drag this link to your bookmarks toolbar, or right-click it and choose "
+"Bookmark This Link"
+msgstr ""
+"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et Â« "
+"Ajouter aux favoris Â»"
+
+#~ msgid "Sessions do not seem to work correctly on your server."
+#~ msgstr "Les sessions ne semblent "
+
+#~ msgid "Tag was renamed in "
+#~ msgstr "Le tag a Ă©tĂ© renommĂ© dans "
+
+#, fuzzy
+#~| msgid "My links"
+#~ msgid " links"
+#~ msgstr "Mes liens"
+
+#, fuzzy
+#~| msgid ""
+#~| "Error: missing Composer configuration\n"
+#~| "\n"
+#~ msgid "Error: missing Composer configuration"
+#~ msgstr ""
+#~ "Erreur : la configuration Composer est manquante\n"
+#~ "\n"
+
+#, fuzzy
+#~| msgid ""
+#~| "Shaarli could not create the config file. Please make sure Shaarli has "
+#~| "the right to write in the folder is it installed in."
+#~ msgid ""
+#~ "Shaarli could not create the config file. \n"
+#~ "                   Please make sure Shaarli has the right to write in the "
+#~ "folder is it installed in."
+#~ msgstr ""
+#~ "Shaarli n'a pas pu crĂ©er le fichier de configuration. Merci de vĂ©rifier "
+#~ "que Shaarli a les droits d'Ă©criture dans le dossier dans lequel il est "
+#~ "installĂ©."
+
+#, fuzzy
+#~| msgid "Plugin"
+#~ msgid "Plugin \""
+#~ msgstr "Extension"
+
+#~ msgid "Your PHP version is obsolete!"
+#~ msgstr "Votre version de PHP est obsolète !"
+
+#~ msgid " Shaarli requires at least PHP "
+#~ msgstr "Shaarli nĂ©cessite au moins PHP"
index 4068a828f10293ee40a8e245e5052e908cbe8bf3..98171d786a75e3a03db57d6e024523f1fe94dec9 100644 (file)
--- a/index.php
+++ b/index.php
@@ -64,7 +64,6 @@ require_once 'application/FeedBuilder.php';
 require_once 'application/FileUtils.php';
 require_once 'application/History.php';
 require_once 'application/HttpUtils.php';
-require_once 'application/Languages.php';
 require_once 'application/LinkDB.php';
 require_once 'application/LinkFilter.php';
 require_once 'application/LinkUtils.php';
@@ -76,6 +75,7 @@ require_once 'application/Utils.php';
 require_once 'application/PluginManager.php';
 require_once 'application/Router.php';
 require_once 'application/Updater.php';
+use \Shaarli\Languages;
 use \Shaarli\ThemeUtils;
 use \Shaarli\Config\ConfigManager;
 
@@ -121,8 +121,16 @@ if (isset($_COOKIE['shaarli']) && !is_session_id_valid($_COOKIE['shaarli'])) {
 }
 
 $conf = new ConfigManager();
+
+// Sniff browser language and set date format accordingly.
+if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+    autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
+}
+
+new Languages(setlocale(LC_MESSAGES, 0), $conf);
+
 $conf->setEmpty('general.timezone', date_default_timezone_get());
-$conf->setEmpty('general.title', 'Shared links on '. escape(index_url($_SERVER)));
+$conf->setEmpty('general.title', t('Shared links on '). escape(index_url($_SERVER)));
 RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory
 RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory
 
@@ -144,7 +152,7 @@ if (! is_file($conf->getConfigFileExt())) {
     $errors = ApplicationUtils::checkResourcePermissions($conf);
 
     if ($errors != array()) {
-        $message = '<p>Insufficient permissions:</p><ul>';
+        $message = '<p>'. t('Insufficient permissions:') .'</p><ul>';
 
         foreach ($errors as $error) {
             $message .= '<li>'.$error.'</li>';
@@ -163,11 +171,6 @@ if (! is_file($conf->getConfigFileExt())) {
 // a token depending of deployment salt, user password, and the current ip
 define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt')));
 
-// Sniff browser language and set date format accordingly.
-if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-    autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
-}
-
 /**
  * Checking session state (i.e. is the user still logged in)
  *
@@ -376,7 +379,7 @@ function ban_canLogin($conf)
 // Process login form: Check if login/password is correct.
 if (isset($_POST['login']))
 {
-    if (!ban_canLogin($conf)) die('I said: NO. You are banned for the moment. Go away.');
+    if (!ban_canLogin($conf)) die(t('I said: NO. You are banned for the moment. Go away.'));
     if (isset($_POST['password'])
         && tokenOk($_POST['token'])
         && (check_auth($_POST['login'], $_POST['password'], $conf))
@@ -440,7 +443,8 @@ if (isset($_POST['login']))
                 }
             }
         }
-        echo '<script>alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen.
+        // Redirect to login screen.
+        echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'?do=login'.$redir.'\';</script>';
         exit;
     }
 }
@@ -1100,16 +1104,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
     if ($targetPage == Router::$PAGE_CHANGEPASSWORD)
     {
         if ($conf->get('security.open_shaarli')) {
-            die('You are not supposed to change a password on an Open Shaarli.');
+            die(t('You are not supposed to change a password on an Open Shaarli.'));
         }
 
         if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
         {
-            if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
+            if (!tokenOk($_POST['token'])) die(t('Wrong token.')); // Go away!
 
             // Make sure old password is correct.
             $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt'));
-            if ($oldhash!= $conf->get('credentials.hash')) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; }
+            if ($oldhash!= $conf->get('credentials.hash')) {
+                echo '<script>alert("'. t('The old password is not correct.') .'");document.location=\'?do=changepasswd\';</script>';
+                exit; 
+            }
             // Save new password
             // Salt renders rainbow-tables attacks useless.
             $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
@@ -1127,7 +1134,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
                 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>';
                 exit;
             }
-            echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
+            echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>';
             exit;
         }
         else // show the change password form.
@@ -1143,7 +1150,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
         if (!empty($_POST['title']) )
         {
             if (!tokenOk($_POST['token'])) {
-                die('Wrong token.'); // Go away!
+                die(t('Wrong token.')); // Go away!
             }
             $tz = 'UTC';
             if (!empty($_POST['continent']) && !empty($_POST['city'])
@@ -1178,7 +1185,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
                 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>';
                 exit;
             }
-            echo '<script>alert("Configuration was saved.");document.location=\'?do=configure\';</script>';
+            echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>';
             exit;
         }
         else // Show the configuration form.
@@ -1215,7 +1222,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
         }
 
         if (!tokenOk($_POST['token'])) {
-            die('Wrong token.');
+            die(t('Wrong token.'));
         }
 
         $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag']));
@@ -1244,7 +1251,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
     {
         // Go away!
         if (! tokenOk($_POST['token'])) {
-            die('Wrong token.');
+            die(t('Wrong token.'));
         }
 
         // lf_id should only be present if the link exists.
@@ -1344,7 +1351,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
     if ($targetPage == Router::$PAGE_DELETELINK)
     {
         if (! tokenOk($_GET['token'])) {
-            die('Wrong token.');
+            die(t('Wrong token.'));
         }
 
         $ids = trim($_GET['lf_linkdate']);
@@ -1550,11 +1557,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
         // Import bookmarks from an uploaded file
         if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
             // The file is too big or some form field may be missing.
-            echo '<script>alert("The file you are trying to upload is probably'
-                .' bigger than what this webserver can accept ('
-                .get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')).').'
-                .' Please upload in smaller chunks.");document.location=\'?do='
-                .Router::$PAGE_IMPORT .'\';</script>';
+            $msg = sprintf(
+                t(
+                    'The file you are trying to upload is probably bigger than what this webserver can accept'
+                    .' (%s). Please upload in smaller chunks.'
+                ),
+                get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize'))
+            );
+            echo '<script>alert("'. $msg .'");document.location=\'?do='.Router::$PAGE_IMPORT .'\';</script>';
             exit;
         }
         if (! tokenOk($_POST['token'])) {
@@ -1962,12 +1972,20 @@ function install($conf)
     // (Because on some hosts, session.save_path may not be set correctly,
     // or we may not have write access to it.)
     if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working'))
-    {   // Step 2: Check if data in session is correct.
-        echo '<pre>Sessions do not seem to work correctly on your server.<br>';
-        echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>';
-        echo 'It currently points to '.session_save_path().'<br>';
-        echo 'Check that the hostname used to access Shaarli contains a dot. On some browsers, accessing your server via a hostname like \'localhost\' or any custom hostname without a dot causes cookie storage to fail. We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>';
-        echo '<br><a href="?">Click to try again.</a></pre>';
+    {
+        // Step 2: Check if data in session is correct.
+        $msg = t(
+            '<pre>Sessions do not seem to work correctly on your server.<br>'.
+            'Make sure the variable "session.save_path" is set correctly in your PHP config, '.
+            'and that you have write access to it.<br>'.
+            'It currently points to %s.<br>'.
+            'On some browsers, accessing your server via a hostname like \'localhost\' '.
+            'or any custom hostname without a dot causes cookie storage to fail. '.
+            'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'
+        );
+        $msg = sprintf($msg, session_save_path());
+        echo $msg;
+        echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>';
         die;
     }
     if (!isset($_SESSION['session_tested']))
diff --git a/plugins/TODO.md b/plugins/TODO.md
deleted file mode 100644 (file)
index e3313d6..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-https://github.com/shaarli/Shaarli/issues/181 -  Add Disqus or Isso comments box on a permalink page  
-
- * http://posativ.org/isso/
- * install debian package https://packages.debian.org/sid/isso  
- * configure server http://posativ.org/isso/docs/configuration/server/  
- * configure client http://posativ.org/isso/docs/configuration/client/  
- * http://posativ.org/isso/docs/quickstart/ and add `<script data-isso="//comments.example.tld/" src="//comments.example.tld/js/embed.min.js"></script>` to includes.html template; then add `<section id="isso-thread"></section>` in the linklist template where you want the comments (in the linklist_plugins loop for example)
-
-Problem: by default, Isso thread ID is guessed from the current url (only one thread per page).  
-if we want multiple threads on a single page (shaarli linklist), we must use : the `data-isso-id` client config,
-with data-isso-id being the permalink of an item.
-
-`<section data-isso-id="aH7klxW" id="isso-thread"></section>` 
-`data-isso-id: Set a custom thread id, defaults to current URI.`
-
-Problem: feature is currently broken https://github.com/posativ/isso/issues/27 
-
-Another option, only display isso threads when current URL is a permalink (`\?(A-Z|a-z|0-9|-){7}`) (only show thread
-when displaying only this link), and just display a "comments" button on each linklist item. Optionally show the comment
-count on each item using the API (http://posativ.org/isso/docs/extras/api/#get-comment-count). API requests can be done
-by raintpl `{function` or client-side with js. The former should be faster if isso and shaarli are on ther same server.
-
-Showing all full isso threads in the linklist would destroy layout
-
------------------------------------------------------------
-
-http://www.git-attitude.fr/2014/11/04/git-rerere/ for the merge
index ddf50aaf3aba2b2eccd116030d355a7678f8914b..8c05a23176ba960f8efaf61f5194a679bb1f7f36 100644 (file)
@@ -26,11 +26,11 @@ function hook_addlink_toolbar_render_header($data)
                 array(
                     'type' => 'text',
                     'name' => 'post',
-                    'placeholder' => 'URI',
+                    'placeholder' => t('URI'),
                 ),
                 array(
                     'type' => 'submit',
-                    'value' => 'Add link',
+                    'value' => t('Add link'),
                     'class' => 'bigbutton',
                 ),
             ),
@@ -40,3 +40,12 @@ function hook_addlink_toolbar_render_header($data)
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function addlink_toolbar_dummy_translation()
+{
+    // meta
+    t('Adds the addlink input on the linklist page.');
+}
index 0781fe35d045631971f304533496f1bec82ddcc2..ad501f4799b5770b6bd6f77c550c49317ac55bbe 100644 (file)
@@ -1 +1,5 @@
-<span><a href="https://web.archive.org/web/%s"><img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" alt="archive.org" /></a></span>
+<span>
+  <a href="https://web.archive.org/web/%s">
+    <img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
+  </a>
+</span>
index 03d13d0efb2db403c0d2bef225f66546cee18ae3..cda35751041933c6caffd061c59121da5833c24f 100644 (file)
@@ -20,9 +20,18 @@ function hook_archiveorg_render_linklist($data)
         if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
             continue;
         }
-        $archive = sprintf($archive_html, $value['url']);
+        $archive = sprintf($archive_html, $value['url'], t('View on archive.org'));
         $value['link_plugin'][] = $archive;
     }
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function archiveorg_dummy_translation()
+{
+    // meta
+    t('For each link, add an Archive.org icon.');
+}
index 8fdbf66383c144226770cc03334701801e7f5da8..3a90ae6a39a719459aae5147dec96ef0820e2f80 100644 (file)
@@ -433,3 +433,12 @@ function hook_demo_plugin_render_feed($data)
     }
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function demo_dummy_translation()
+{
+    // meta
+    t('A demo plugin covering all use cases for template designers and plugin developers.');
+}
index ce16645f9ad72f15121c511e7a2077aa65c3983f..5bc1cce26e79d1be3cc64fa3bda9109a12c2ea3f 100644 (file)
@@ -4,10 +4,11 @@
  * Plugin Isso.
  */
 
+use Shaarli\Config\ConfigManager;
+
 /**
  * Display an error everywhere if the plugin is enabled without configuration.
  *
- * @param $data array         List of links
  * @param $conf ConfigManager instance
  *
  * @return mixed - linklist data with Isso plugin.
@@ -16,8 +17,8 @@ function isso_init($conf)
 {
     $issoUrl = $conf->get('plugins.ISSO_SERVER');
     if (empty($issoUrl)) {
-        $error = 'Isso plugin error: '.
-            'Please define the "ISSO_SERVER" setting in the plugin administration page.';
+        $error = t('Isso plugin error: '.
+            'Please define the "ISSO_SERVER" setting in the plugin administration page.');
         return array($error);
     }
 }
@@ -52,3 +53,13 @@ function hook_isso_render_linklist($data, $conf)
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function isso_dummy_translation()
+{
+    // meta
+    t('Let visitor comment your shaares on permalinks with Isso.');
+    t('Isso server URL (without \'http://\')');
+}
index 9c4e5ae06c1a42d446cd27f8bcc66e946169f82d..ded3d3472394f14237957eb69602fa421f2aaaaa 100644 (file)
@@ -1,5 +1,5 @@
 <div class="md_help">
-    Description will be rendered with
-    <a href="http://daringfireball.net/projects/markdown/syntax" title="Markdown syntax documentation">
-        Markdown syntax</a>.
+    %s
+    <a href="http://daringfireball.net/projects/markdown/syntax" title="%s">
+        %s</a>.
 </div>
index 772c56e8e2295ed2eab29364a84eebec7305bf5c..1531549d8f613e594b6fd36259e3e825932ea43e 100644 (file)
@@ -154,8 +154,13 @@ function hook_markdown_render_includes($data)
 function hook_markdown_render_editlink($data)
 {
     // Load help HTML into a string
-    $data['edit_link_plugin'][] = file_get_contents(PluginManager::$PLUGINS_PATH .'/markdown/help.html');
-
+    $txt = file_get_contents(PluginManager::$PLUGINS_PATH .'/markdown/help.html');
+    $translations = [
+        t('Description will be rendered with'),
+        t('Markdown syntax documentation'),
+        t('Markdown syntax'),
+    ];
+    $data['edit_link_plugin'][] = vsprintf($txt, $translations);
     // Add no markdown 'meta-tag' in tag list if it was never used, for autocompletion.
     if (! in_array(NO_MD_TAG, $data['tags'])) {
         $data['tags'][NO_MD_TAG] = 0;
@@ -325,3 +330,15 @@ function process_markdown($description, $escape = true, $allowedProtocols = [])
 
     return $processedDescription;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function markdown_dummy_translation()
+{
+    // meta
+    t('Render shaare description with Markdown syntax.<br><strong>Warning</strong>:
+If your shaared descriptions contained HTML tags before enabling the markdown plugin,
+enabling it might break your page.
+See the <a href="https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering">README</a>.');
+}
index 4a2b48a121fcd4249fe2c257beb35fb28150d16e..ca00c2be71f33dd566be43fb2fb2f791d41e26cf 100644 (file)
@@ -18,8 +18,8 @@ function piwik_init($conf)
     $piwikUrl = $conf->get('plugins.PIWIK_URL');
     $piwikSiteid = $conf->get('plugins.PIWIK_SITEID');
     if (empty($piwikUrl) || empty($piwikSiteid)) {
-        $error = 'Piwik plugin error: ' .
-            'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.';
+        $error = t('Piwik plugin error: ' .
+            'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.');
         return array($error);
     }
 }
@@ -60,3 +60,14 @@ function hook_piwik_render_footer($data, $conf)
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function piwik_dummy_translation()
+{
+    // meta
+    t('A plugin that adds Piwik tracking code to Shaarli pages.');
+    t('Piwik URL');
+    t('Piwik site ID');
+}
index 644845049ff6599407fc43f9c5d99a9c0d4efd81..c6d6b0cc6815e4f5c2547efdc395a67ccde08e3d 100644 (file)
@@ -19,10 +19,10 @@ function hook_playvideos_render_header($data)
         $playvideo = array(
             'attr' => array(
                 'href' => '#',
-                'title' => 'Video player',
+                'title' => t('Video player'),
                 'id' => 'playvideos',
             ),
-            'html' => 'â–ş Play Videos'
+            'html' => 'â–ş '. t('Play Videos')
         );
         $data['buttons_toolbar'][] = $playvideo;
     }
@@ -46,3 +46,12 @@ function hook_playvideos_render_footer($data)
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function playvideos_dummy_translation()
+{
+    // meta
+    t('Add a button in the toolbar allowing to watch all videos.');
+}
index 03b6757bd3687f9f46c2402c72beb6f8641392bb..184b588b7f44d62b89df0fc2f0e07094da0e8b41 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 use pubsubhubbub\publisher\Publisher;
+use Shaarli\Config\ConfigManager;
 
 /**
  * Plugin init function - set the hub to the default appspot one.
@@ -65,7 +66,7 @@ function hook_pubsubhubbub_save_link($data, $conf)
         $p = new Publisher($conf->get('plugins.PUBSUBHUB_URL'));
         $p->publish_update($feeds, $httpPost);
     } catch (Exception $e) {
-        error_log('Could not publish to PubSubHubbub: ' . $e->getMessage());
+        error_log(sprintf(t('Could not publish to PubSubHubbub: %s'), $e->getMessage()));
     }
 
     return $data;
@@ -91,11 +92,20 @@ function nocurl_http_post($url, $postString) {
     $context = stream_context_create($params);
     $fp = @fopen($url, 'rb', false, $context);
     if (!$fp) {
-        throw new Exception('Could not post to '. $url);
+        throw new Exception(sprintf(t('Could not post to %s'), $url));
     }
     $response = @stream_get_contents($fp);
     if ($response === false) {
-        throw new Exception('Bad response from the hub '. $url);
+        throw new Exception(sprintf(t('Bad response from the hub %s'), $url));
     }
     return $response;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function pubsubhubbub_dummy_translation()
+{
+    // meta
+    t('Enable PubSubHubbub feed publishing.');
+}
index cbf371ea21c78b0667572aadc45a40817f04204e..1812cd21154709feca5ce069e4b659c24316d1b3 100644 (file)
@@ -1 +1 @@
-description="For each link, add a QRCode icon ."
+description="For each link, add a QRCode icon."
index 8bc610d1fad120ec34694c4d3c1a3c7501f14e64..0f96a106921e856eff1160cc0b881c1b68b2e75d 100644 (file)
@@ -59,3 +59,12 @@ function hook_qrcode_render_includes($data)
 
     return $data;
 }
+
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function qrcode_dummy_translation()
+{
+    // meta
+    t('For each link, add a QRCode icon.');
+}
index e861536d53866fe4fe7f2a47182f3442043a5b84..4c57691d0387705cb5e0ebacc1334cb175b45e3a 100644 (file)
@@ -1 +1,5 @@
-<span><a href="%s%s" target="_blank"><img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="Save to wallabag" alt="wallabag" /></a></span>
+<span>
+  <a href="%s%s" target="_blank">
+    <img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="%s" alt="wallabag" />
+  </a>
+</span>
index 641e4cc237ed4f20a44fafda7eae1b871bf2e118..9dfd079eb21da9d027dc71a3aacfb9fe5d0608a0 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 require_once 'WallabagInstance.php';
+use Shaarli\Config\ConfigManager;
 
 /**
  * Init function, return an error if the server is not set.
@@ -17,8 +18,8 @@ function wallabag_init($conf)
 {
     $wallabagUrl = $conf->get('plugins.WALLABAG_URL');
     if (empty($wallabagUrl)) {
-        $error = 'Wallabag plugin error: '.
-            'Please define the "WALLABAG_URL" setting in the plugin administration page.';
+        $error = t('Wallabag plugin error: '.
+            'Please define the "WALLABAG_URL" setting in the plugin administration page.');
         return array($error);
     }
 }
@@ -43,12 +44,14 @@ function hook_wallabag_render_linklist($data, $conf)
 
     $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html');
 
+    $linkTitle = t('Save to wallabag');
     foreach ($data['links'] as &$value) {
         $wallabag = sprintf(
             $wallabagHtml,
             $wallabagInstance->getWallabagUrl(),
             urlencode($value['url']),
-            PluginManager::$PLUGINS_PATH
+            PluginManager::$PLUGINS_PATH,
+            $linkTitle
         );
         $value['link_plugin'][] = $wallabag;
     }
@@ -56,3 +59,14 @@ function hook_wallabag_render_linklist($data, $conf)
     return $data;
 }
 
+/**
+ * This function is never called, but contains translation calls for GNU gettext extraction.
+ */
+function wallabag_dummy_translation()
+{
+    // meta
+    t('For each link, add a QRCode icon.');
+    t('Wallabag API URL');
+    t('Wallabag API version (1 or 2)');
+}
+
index 79c136c88d5dea79ab89f1ebf24603685fc27245..46bfcd7254000b84f9d05d6694f75dbbd2abe3e3 100644 (file)
 <?php
 
-require_once 'application/Languages.php';
+namespace Shaarli;
+
+use Shaarli\Config\ConfigManager;
 
 /**
  * Class LanguagesTest.
  */
-class LanguagesTest extends PHPUnit_Framework_TestCase
+class LanguagesTest extends \PHPUnit_Framework_TestCase
 {
+    /**
+     * @var string Config file path (without extension).
+     */
+    protected static $configFile = 'tests/utils/config/configJson';
+
+    /**
+     * @var ConfigManager
+     */
+    protected $conf;
+
+    /**
+     *
+     */
+    public function setUp()
+    {
+        $this->conf = new ConfigManager(self::$configFile);
+    }
+
+    /**
+     * Test t() with a simple non identified value.
+     */
+    public function testTranslateSingleNotIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'abcdĂ© 564 fgK';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with a simple identified value in gettext mode.
+     */
+    public function testTranslateSingleIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'permalink';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with a non identified plural form in gettext mode.
+     */
+    public function testTranslatePluralNotIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'sandwich';
+        $nText = 'sandwiches';
+        $this->assertEquals('sandwiches', t($text, $nText, 0));
+        $this->assertEquals('sandwich', t($text, $nText, 1));
+        $this->assertEquals('sandwiches', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with an identified plural form in gettext mode.
+     */
+    public function testTranslatePluralIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'shaare';
+        $nText = 'shaares';
+        // In english, zero is followed by plural form
+        $this->assertEquals('shaares', t($text, $nText, 0));
+        $this->assertEquals('shaare', t($text, $nText, 1));
+        $this->assertEquals('shaares', t($text, $nText, 2));
+    }
+
     /**
      * Test t() with a simple non identified value.
      */
-    public function testTranslateSingleNotID()
+    public function testTranslateSingleNotIDPhp()
     {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
         $text = 'abcdĂ© 564 fgK';
         $this->assertEquals($text, t($text));
     }
 
     /**
-     * Test t() with a non identified plural form.
+     * Test t() with a simple identified value in PHP mode.
      */
-    public function testTranslatePluralNotID()
+    public function testTranslateSingleIDPhp()
     {
-        $text = '%s sandwich';
-        $nText = '%s sandwiches';
-        $this->assertEquals('0 sandwich', t($text, $nText));
-        $this->assertEquals('1 sandwich', t($text, $nText, 1));
-        $this->assertEquals('2 sandwiches', t($text, $nText, 2));
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'permalink';
+        $this->assertEquals($text, t($text));
     }
 
     /**
-     * Test t() with a non identified invalid plural form.
+     * Test t() with a non identified plural form in PHP mode.
      */
-    public function testTranslatePluralNotIDInvalid()
+    public function testTranslatePluralNotIDPhp()
     {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
         $text = 'sandwich';
         $nText = 'sandwiches';
+        $this->assertEquals('sandwiches', t($text, $nText, 0));
         $this->assertEquals('sandwich', t($text, $nText, 1));
         $this->assertEquals('sandwiches', t($text, $nText, 2));
     }
+
+    /**
+     * Test t() with an identified plural form in PHP mode.
+     */
+    public function testTranslatePluralIDPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'shaare';
+        $nText = 'shaares';
+        // In english, zero is followed by plural form
+        $this->assertEquals('shaares', t($text, $nText, 0));
+        $this->assertEquals('shaare', t($text, $nText, 1));
+        $this->assertEquals('shaares', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with an invalid language set in the configuration in gettext mode.
+     */
+    public function testTranslateWithInvalidConfLanguageGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        $this->conf->set('translation.language', 'nope');
+        new Languages('fr', $this->conf);
+        $text = 'grumble';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with an invalid language set in the configuration in PHP mode.
+     */
+    public function testTranslateWithInvalidConfLanguagePhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        $this->conf->set('translation.language', 'nope');
+        new Languages('fr', $this->conf);
+        $text = 'grumble';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with an invalid language set with auto language in gettext mode.
+     */
+    public function testTranslateWithInvalidAutoLanguageGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('nope', $this->conf);
+        $text = 'grumble';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with an invalid language set with auto language in PHP mode.
+     */
+    public function testTranslateWithInvalidAutoLanguagePhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('nope', $this->conf);
+        $text = 'grumble';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with an extension language file in gettext mode
+     */
+    public function testTranslationExtensionGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
+        new Languages('en', $this->conf);
+        $this->assertEquals('car', t('car', 'car', 1, 'test'));
+        $this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
+    }
+
+    /**
+     * Test t() with an extension language file in PHP mode
+     */
+    public function testTranslationExtensionPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
+        new Languages('en', $this->conf);
+        $this->assertEquals('car', t('car', 'car', 1, 'test'));
+        $this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
+    }
 }
index 3d1aa6538918f8ad48dc5436b91dd03253a3eadd..840eaf21089e5c40400f813ef43726c7acf8a72c 100644 (file)
@@ -384,18 +384,18 @@ class UtilsTest extends PHPUnit_Framework_TestCase
      */
     public function testHumanBytes()
     {
-        $this->assertEquals('2kiB', human_bytes(2 * 1024));
-        $this->assertEquals('2kiB', human_bytes(strval(2 * 1024)));
-        $this->assertEquals('2MiB', human_bytes(2 * (pow(1024, 2))));
-        $this->assertEquals('2MiB', human_bytes(strval(2 * (pow(1024, 2)))));
-        $this->assertEquals('2GiB', human_bytes(2 * (pow(1024, 3))));
-        $this->assertEquals('2GiB', human_bytes(strval(2 * (pow(1024, 3)))));
-        $this->assertEquals('374B', human_bytes(374));
-        $this->assertEquals('374B', human_bytes('374'));
-        $this->assertEquals('232kiB', human_bytes(237481));
-        $this->assertEquals('Unlimited', human_bytes('0'));
-        $this->assertEquals('Unlimited', human_bytes(0));
-        $this->assertEquals('Setting not set', human_bytes(''));
+        $this->assertEquals('2'. t('kiB'), human_bytes(2 * 1024));
+        $this->assertEquals('2'. t('kiB'), human_bytes(strval(2 * 1024)));
+        $this->assertEquals('2'. t('MiB'), human_bytes(2 * (pow(1024, 2))));
+        $this->assertEquals('2'. t('MiB'), human_bytes(strval(2 * (pow(1024, 2)))));
+        $this->assertEquals('2'. t('GiB'), human_bytes(2 * (pow(1024, 3))));
+        $this->assertEquals('2'. t('GiB'), human_bytes(strval(2 * (pow(1024, 3)))));
+        $this->assertEquals('374'. t('B'), human_bytes(374));
+        $this->assertEquals('374'. t('B'), human_bytes('374'));
+        $this->assertEquals('232'. t('kiB'), human_bytes(237481));
+        $this->assertEquals(t('Unlimited'), human_bytes('0'));
+        $this->assertEquals(t('Unlimited'), human_bytes(0));
+        $this->assertEquals(t('Setting not set'), human_bytes(''));
     }
 
     /**
@@ -403,9 +403,9 @@ class UtilsTest extends PHPUnit_Framework_TestCase
      */
     public function testGetMaxUploadSize()
     {
-        $this->assertEquals('1MiB', get_max_upload_size(2097152, '1024k'));
-        $this->assertEquals('1MiB', get_max_upload_size('1m', '2m'));
-        $this->assertEquals('100B', get_max_upload_size(100, 100));
+        $this->assertEquals('1'. t('MiB'), get_max_upload_size(2097152, '1024k'));
+        $this->assertEquals('1'. t('MiB'), get_max_upload_size('1m', '2m'));
+        $this->assertEquals('100'. t('B'), get_max_upload_size(100, 100));
     }
 
     /**
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644 (file)
index 0000000..d36d73c
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+require_once 'vendor/autoload.php';
+
+$conf = new \Shaarli\Config\ConfigManager('tests/utils/config/configJson');
+new \Shaarli\Languages('en', $conf);
index 95609210f30ec1f66c17fa97c3196a63a01119a2..da6ac2e4e2ed3c86bec9d8a615cfd6b8ff4496a3 100644 (file)
@@ -1,7 +1,6 @@
 <?php
-if (! empty('UT_LOCALE')) {
+require_once 'tests/bootstrap.php';
+
+if (! empty(getenv('UT_LOCALE'))) {
     setlocale(LC_ALL, getenv('UT_LOCALE'));
 }
-
-require_once 'vendor/autoload.php';
-
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php
new file mode 100644 (file)
index 0000000..c05a0f9
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+
+
+namespace Shaarli;
+
+
+use Shaarli\Config\ConfigManager;
+
+/**
+ * Class LanguagesFrTest
+ *
+ * Test the translation system in PHP and gettext mode with French language.
+ *
+ * @package Shaarli
+ */
+class LanguagesFrTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var string Config file path (without extension).
+     */
+    protected static $configFile = 'tests/utils/config/configJson';
+
+    /**
+     * @var ConfigManager
+     */
+    protected $conf;
+
+    /**
+     * Init: force French
+     */
+    public function setUp()
+    {
+        $this->conf = new ConfigManager(self::$configFile);
+        $this->conf->set('translation.language', 'fr');
+    }
+
+    /**
+     * Reset the locale since gettext seems to mess with it, making it too long
+     */
+    public static function tearDownAfterClass()
+    {
+        if (! empty(getenv('UT_LOCALE'))) {
+            setlocale(LC_ALL, getenv('UT_LOCALE'));
+        }
+    }
+
+    /**
+     * Test t() with a simple non identified value.
+     */
+    public function testTranslateSingleNotIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'abcdĂ© 564 fgK';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with a simple identified value in gettext mode.
+     */
+    public function testTranslateSingleIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'permalink';
+        $this->assertEquals('permalien', t($text));
+    }
+
+    /**
+     * Test t() with a non identified plural form in gettext mode.
+     */
+    public function testTranslatePluralNotIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'sandwich';
+        $nText = 'sandwiches';
+        // Not ID, so English fallback, and in english, plural 0
+        $this->assertEquals('sandwiches', t($text, $nText, 0));
+        $this->assertEquals('sandwich', t($text, $nText, 1));
+        $this->assertEquals('sandwiches', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with an identified plural form in gettext mode.
+     */
+    public function testTranslatePluralIDGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        new Languages('en', $this->conf);
+        $text = 'shaare';
+        $nText = 'shaares';
+        $this->assertEquals('shaare', t($text, $nText, 0));
+        $this->assertEquals('shaare', t($text, $nText, 1));
+        $this->assertEquals('shaares', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with a simple non identified value.
+     */
+    public function testTranslateSingleNotIDPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'abcdĂ© 564 fgK';
+        $this->assertEquals($text, t($text));
+    }
+
+    /**
+     * Test t() with a simple identified value in PHP mode.
+     */
+    public function testTranslateSingleIDPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'permalink';
+        $this->assertEquals('permalien', t($text));
+    }
+
+    /**
+     * Test t() with a non identified plural form in PHP mode.
+     */
+    public function testTranslatePluralNotIDPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'sandwich';
+        $nText = 'sandwiches';
+        // Not ID, so English fallback, and in english, plural 0
+        $this->assertEquals('sandwiches', t($text, $nText, 0));
+        $this->assertEquals('sandwich', t($text, $nText, 1));
+        $this->assertEquals('sandwiches', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with an identified plural form in PHP mode.
+     */
+    public function testTranslatePluralIDPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        new Languages('en', $this->conf);
+        $text = 'shaare';
+        $nText = 'shaares';
+        // In english, zero is followed by plural form
+        $this->assertEquals('shaare', t($text, $nText, 0));
+        $this->assertEquals('shaare', t($text, $nText, 1));
+        $this->assertEquals('shaares', t($text, $nText, 2));
+    }
+
+    /**
+     * Test t() with an extension language file in gettext mode
+     */
+    public function testTranslationExtensionGettext()
+    {
+        $this->conf->set('translation.mode', 'gettext');
+        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
+        new Languages('en', $this->conf);
+        $this->assertEquals('voiture', t('car', 'car', 1, 'test'));
+        $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
+    }
+
+    /**
+     * Test t() with an extension language file in PHP mode
+     */
+    public function testTranslationExtensionPhp()
+    {
+        $this->conf->set('translation.mode', 'php');
+        $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
+        new Languages('en', $this->conf);
+        $this->assertEquals('voiture', t('car', 'car', 1, 'test'));
+        $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
+    }
+}
diff --git a/tests/utils/languages/fr/LC_MESSAGES/test.mo b/tests/utils/languages/fr/LC_MESSAGES/test.mo
new file mode 100644 (file)
index 0000000..416c783
Binary files /dev/null and b/tests/utils/languages/fr/LC_MESSAGES/test.mo differ
diff --git a/tests/utils/languages/fr/LC_MESSAGES/test.po b/tests/utils/languages/fr/LC_MESSAGES/test.po
new file mode 100644 (file)
index 0000000..89a4fd9
--- /dev/null
@@ -0,0 +1,19 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Extension test\n"
+"POT-Creation-Date: 2017-05-20 13:54+0200\n"
+"PO-Revision-Date: 2017-05-20 14:16+0200\n"
+"Last-Translator: \n"
+"Language-Team: Shaarli\n"
+"Language: fr_FR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 2.0.1\n"
+
+msgid "car"
+msgstr "voiture"
+
+msgid "Search"
+msgstr "Fouille"
index 1f040685caa5195f0a2e9a41ec199bf720bee178..000a50ac3d069ee9223f345faf4d0a9fc1170dd4 100644 (file)
@@ -18,7 +18,7 @@
       <div class="center" id="import-field">
         <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}">
         <input type="file" name="filetoupload">
-        <p><br>Maximum size allowed: <strong>{$maxfilesizeHuman}</strong></p>
+        <p><br>{'Maximum size allowed:'|t} <strong>{$maxfilesizeHuman}</strong></p>
       </div>
 
       <div class="pure-g">
           <div class="radio-buttons">
             <div>
               <input type="radio" name="privacy" value="default" checked="checked">
-              Use values from the imported file, default to public
+              {'Use values from the imported file, default to public'|t}
             </div>
             <div>
               <input type="radio" name="privacy" value="private">
-              Import all bookmarks as private
+              {'Import all bookmarks as private'|t}
             </div>
             <div>
               <input type="radio" name="privacy" value="public">
-              Import all bookmarks as public
+              {'Import all bookmarks as public'|t}
             </div>
           </div>
         </div>
index 685821e395bfb184208ad2a96c6fd583220bc4d4..5dab8e9a9b8ec95173843cff85948fedfa55a724 100644 (file)
@@ -86,7 +86,7 @@
     <div class="pure-g pure-alert pure-alert-success search-result">
       <div class="pure-u-2-24"></div>
       <div class="pure-u-20-24">
-        {function="t('%s result', '%s results', $result_count)"}
+        {function="sprintf(t('%s result', '%s results', $result_count), $result_count)"}
         {if="!empty($search_term)"}
           {'for'|t} <em><strong>{$search_term}</strong></em>
         {/if}
   <div class="pure-g">
     <div class="pure-u-lg-2-24 pure-u-1-24"></div>
     <div class="pure-u-lg-20-24 pure-u-22-24">
+      {ignore}Set translation here, for performances{/ignore}
+      {$strPrivate=t('Private')}
+      {$strEdit=t('Edit')}
+      {$strDelete=t('Delete')}
+      {$strFold=t('Fold')}
+      {$strEdited=t('Edited: ')}
+      {$strPermalink=t('Permalink')}
+      {$strPermalinkLc=t('permalink')}
+      {$strAddTag=t('Add tag')}
+      {ignore}End of translations{/ignore}
       {loop="links"}
         <div class="anchor" id="{$value.shorturl}"></div>
         <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
             {if="isLoggedIn()"}
               <div class="linklist-item-editbuttons">
                 {if="$value.private"}
-                  <span class="label label-private">{'Private'|t}</span>
+                  <span class="label label-private">{$strPrivate}</span>
                 {/if}
                 <input type="checkbox" class="delete-checkbox" value="{$value.id}">
                 <!-- FIXME! JS translation -->
-                <a href="?edit_link={$value.id}" title="{'Edit'|t}"><i class="fa fa-pencil-square-o edit-link"></i></a>
-                <a href="#" title="{'Fold'|t}" class="fold-button"><i class="fa fa-chevron-up"></i></a>
+                <a href="?edit_link={$value.id}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link"></i></a>
+                <a href="#" title="{$strFold}" class="fold-button"><i class="fa fa-chevron-up"></i></a>
               </div>
             {/if}
 
                 <i class="fa fa-tags"></i>
                 {$tag_counter=count($value.taglist)}
                 {loop="value.taglist"}
-                  <span class="label label-tag" title="Add tag">
+                  <span class="label label-tag" title="{$strAddTag}">
                     <a href="?addtag={$value|urlencode}">{$value}</a>
                   </span>
                   {if="$tag_counter - 1 != $counter"}&middot;{/if}
 
             <div class="pure-g">
               <div class="linklist-item-infos-dateblock pure-u-lg-3-8 pure-u-1">
-                <a href="?{$value.shorturl}" title="{'Permalink'|t}">
+                <a href="?{$value.shorturl}" title="{$strPermalink}">
                   {if="!$hide_timestamps || isLoggedIn()"}
-                    {$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'}
+                    {$updated=$value.updated_timestamp ? $strEdited. format_date($value.updated) : $strPermalink}
                     <span class="linkdate" title="{$updated}">
                       <i class="fa fa-clock-o"></i>
                       {$value.created|format_date}
                       &middot;
                     </span>
                   {/if}
-                  {'permalink'|t}
+                  {$strPermalinkLc}
                 </a>
 
                 <div class="pure-u-0 pure-u-lg-visible">
                 </a>
                 {if="isLoggedIn()"}
                   <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}"
-                     title="{'Delete'|t}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete">
+                     title="{$strDelete}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete">
                     <i class="fa fa-trash"></i>
                   </a>
                 {/if}
                 {if="isLoggedIn()"}
                   &middot;
                   <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}"
-                     title="{'Delete'|t}" class="delete-link confirm-delete">
+                     title="{$strDelete}" class="delete-link confirm-delete">
                     <i class="fa fa-trash"></i>
                   </a>
                 {/if}
index 54b16e8a3454dabcdd248dd21ae567ed6cac7814..2c788e2f07602af9a81b8b72582f8289296e5a6b 100644 (file)
@@ -8,8 +8,8 @@
       {$version}
     {/if}
     &middot;
-    The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community &middot;
-    <a href="doc/html/index.html" rel="nofollow">Documentation</a>
+    {'The personal, minimalist, super-fast, database free, bookmarking service'|t} {'by the Shaarli community'|t} &middot;
+    <a href="doc/html/index.html" rel="nofollow">{'Documentation'|t}</a>
       {loop="$plugins_footer.text"}
           {$value}
       {/loop}
index 5cc1802f77dff26790ced7d1c77ce52c176c7438..717cb517dfafde4021b697dd697eb3cba957bef7 100644 (file)
       </section>
 
       <div class="center more">
-        More plugins available
-        <a href="doc/Community-&-Related-software.html#third-party-plugins">in the documentation</a>.
+        {"More plugins available"|t}
+        <a href="doc/Community-&-Related-software.html#third-party-plugins">{"in the documentation"|t}</a>.
       </div>
       <div class="center">
         <input type="submit" value="{'Save'|t}" name="save">