aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--application/ApplicationUtils.php19
-rw-r--r--application/Cache.php2
-rw-r--r--application/FeedBuilder.php4
-rw-r--r--application/History.php4
-rw-r--r--application/Languages.php153
-rw-r--r--application/LinkDB.php20
-rw-r--r--application/LinkFilter.php8
-rw-r--r--application/NetscapeBookmarkUtils.php16
-rw-r--r--application/PageBuilder.php7
-rw-r--r--application/PluginManager.php7
-rw-r--r--application/Updater.php8
-rw-r--r--application/Utils.php17
-rw-r--r--application/config/ConfigJson.php15
-rw-r--r--application/config/ConfigManager.php6
-rw-r--r--application/config/ConfigPhp.php4
-rw-r--r--application/config/exception/MissingFieldConfigException.php2
-rw-r--r--application/config/exception/PluginConfigOrderException.php2
-rw-r--r--application/config/exception/UnauthorizedConfigException.php2
-rw-r--r--application/exceptions/IOException.php2
-rw-r--r--composer.json3
-rw-r--r--composer.lock217
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.mobin0 -> 22943 bytes
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.po1243
-rw-r--r--index.php78
-rw-r--r--plugins/TODO.md28
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.php13
-rw-r--r--plugins/archiveorg/archiveorg.html6
-rw-r--r--plugins/archiveorg/archiveorg.php11
-rw-r--r--plugins/demo_plugin/demo_plugin.php9
-rw-r--r--plugins/isso/isso.php17
-rw-r--r--plugins/markdown/help.html6
-rw-r--r--plugins/markdown/markdown.php21
-rw-r--r--plugins/piwik/piwik.php15
-rw-r--r--plugins/playvideos/playvideos.php13
-rw-r--r--plugins/pubsubhubbub/pubsubhubbub.php16
-rw-r--r--plugins/qrcode/qrcode.meta2
-rw-r--r--plugins/qrcode/qrcode.php9
-rw-r--r--plugins/wallabag/wallabag.html6
-rw-r--r--plugins/wallabag/wallabag.php20
-rw-r--r--tests/LanguagesTest.php184
-rw-r--r--tests/UtilsTest.php30
-rw-r--r--tests/bootstrap.php6
-rw-r--r--tests/languages/bootstrap.php7
-rw-r--r--tests/languages/fr/LanguagesFrTest.php173
-rw-r--r--tests/utils/languages/fr/LC_MESSAGES/test.mobin0 -> 456 bytes
-rw-r--r--tests/utils/languages/fr/LC_MESSAGES/test.po19
-rw-r--r--tpl/default/import.html8
-rw-r--r--tpl/default/linklist.html30
-rw-r--r--tpl/default/page.footer.html4
-rw-r--r--tpl/default/pluginsadmin.html4
51 files changed, 2252 insertions, 246 deletions
diff --git a/Makefile b/Makefile
index 656c27b0..300f1d7f 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ test:
135 @echo "PHPUNIT" 135 @echo "PHPUNIT"
136 @echo "-------" 136 @echo "-------"
137 @mkdir -p sandbox coverage 137 @mkdir -p sandbox coverage
138 @$(BIN)/phpunit --coverage-php coverage/main.cov --testsuite unit-tests 138 @$(BIN)/phpunit --coverage-php coverage/main.cov --bootstrap tests/bootstrap.php --testsuite unit-tests
139 139
140locale_test_%: 140locale_test_%:
141 @UT_LOCALE=$*.utf8 \ 141 @UT_LOCALE=$*.utf8 \
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index 5643f4a0..911873a0 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -149,12 +149,13 @@ class ApplicationUtils
149 public static function checkPHPVersion($minVersion, $curVersion) 149 public static function checkPHPVersion($minVersion, $curVersion)
150 { 150 {
151 if (version_compare($curVersion, $minVersion) < 0) { 151 if (version_compare($curVersion, $minVersion) < 0) {
152 throw new Exception( 152 $msg = t(
153 'Your PHP version is obsolete!' 153 'Your PHP version is obsolete!'
154 .' Shaarli requires at least PHP '.$minVersion.', and thus cannot run.' 154 . ' Shaarli requires at least PHP %s, and thus cannot run.'
155 .' Your PHP version has known security vulnerabilities and should be' 155 . ' Your PHP version has known security vulnerabilities and should be'
156 .' updated as soon as possible.' 156 . ' updated as soon as possible.'
157 ); 157 );
158 throw new Exception(sprintf($msg, $minVersion));
158 } 159 }
159 } 160 }
160 161
@@ -179,7 +180,7 @@ class ApplicationUtils
179 $rainTplDir.'/'.$conf->get('resource.theme'), 180 $rainTplDir.'/'.$conf->get('resource.theme'),
180 ) as $path) { 181 ) as $path) {
181 if (! is_readable(realpath($path))) { 182 if (! is_readable(realpath($path))) {
182 $errors[] = '"'.$path.'" directory is not readable'; 183 $errors[] = '"'.$path.'" '. t('directory is not readable');
183 } 184 }
184 } 185 }
185 186
@@ -191,10 +192,10 @@ class ApplicationUtils
191 $conf->get('resource.raintpl_tmp'), 192 $conf->get('resource.raintpl_tmp'),
192 ) as $path) { 193 ) as $path) {
193 if (! is_readable(realpath($path))) { 194 if (! is_readable(realpath($path))) {
194 $errors[] = '"'.$path.'" directory is not readable'; 195 $errors[] = '"'.$path.'" '. t('directory is not readable');
195 } 196 }
196 if (! is_writable(realpath($path))) { 197 if (! is_writable(realpath($path))) {
197 $errors[] = '"'.$path.'" directory is not writable'; 198 $errors[] = '"'.$path.'" '. t('directory is not writable');
198 } 199 }
199 } 200 }
200 201
@@ -212,10 +213,10 @@ class ApplicationUtils
212 } 213 }
213 214
214 if (! is_readable(realpath($path))) { 215 if (! is_readable(realpath($path))) {
215 $errors[] = '"'.$path.'" file is not readable'; 216 $errors[] = '"'.$path.'" '. t('file is not readable');
216 } 217 }
217 if (! is_writable(realpath($path))) { 218 if (! is_writable(realpath($path))) {
218 $errors[] = '"'.$path.'" file is not writable'; 219 $errors[] = '"'.$path.'" '. t('file is not writable');
219 } 220 }
220 } 221 }
221 222
diff --git a/application/Cache.php b/application/Cache.php
index 5d050165..e5d43e61 100644
--- a/application/Cache.php
+++ b/application/Cache.php
@@ -13,7 +13,7 @@
13function purgeCachedPages($pageCacheDir) 13function purgeCachedPages($pageCacheDir)
14{ 14{
15 if (! is_dir($pageCacheDir)) { 15 if (! is_dir($pageCacheDir)) {
16 $error = 'Cannot purge '.$pageCacheDir.': no directory'; 16 $error = sprintf(t('Cannot purge %s: no directory'), $pageCacheDir);
17 error_log($error); 17 error_log($error);
18 return $error; 18 return $error;
19 } 19 }
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php
index 7377bcec..3cfaafb4 100644
--- a/application/FeedBuilder.php
+++ b/application/FeedBuilder.php
@@ -148,9 +148,9 @@ class FeedBuilder
148 $link['url'] = $pageaddr . $link['url']; 148 $link['url'] = $pageaddr . $link['url'];
149 } 149 }
150 if ($this->usePermalinks === true) { 150 if ($this->usePermalinks === true) {
151 $permalink = '<a href="'. $link['url'] .'" title="Direct link">Direct link</a>'; 151 $permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>';
152 } else { 152 } else {
153 $permalink = '<a href="'. $link['guid'] .'" title="Permalink">Permalink</a>'; 153 $permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>';
154 } 154 }
155 $link['description'] = format_description($link['description'], '', $pageaddr); 155 $link['description'] = format_description($link['description'], '', $pageaddr);
156 $link['description'] .= PHP_EOL .'<br>&#8212; '. $permalink; 156 $link['description'] .= PHP_EOL .'<br>&#8212; '. $permalink;
diff --git a/application/History.php b/application/History.php
index 5e3b1b72..35ec016a 100644
--- a/application/History.php
+++ b/application/History.php
@@ -171,7 +171,7 @@ class History
171 } 171 }
172 172
173 if (! is_writable($this->historyFilePath)) { 173 if (! is_writable($this->historyFilePath)) {
174 throw new Exception('History file isn\'t readable or writable'); 174 throw new Exception(t('History file isn\'t readable or writable'));
175 } 175 }
176 } 176 }
177 177
@@ -182,7 +182,7 @@ class History
182 { 182 {
183 $this->history = FileUtils::readFlatDB($this->historyFilePath, []); 183 $this->history = FileUtils::readFlatDB($this->historyFilePath, []);
184 if ($this->history === false) { 184 if ($this->history === false) {
185 throw new Exception('Could not parse history file'); 185 throw new Exception(t('Could not parse history file'));
186 } 186 }
187 } 187 }
188 188
diff --git a/application/Languages.php b/application/Languages.php
index c8b0a25a..4ba32f29 100644
--- a/application/Languages.php
+++ b/application/Languages.php
@@ -1,21 +1,150 @@
1<?php 1<?php
2 2
3namespace Shaarli;
4
5use Gettext\GettextTranslator;
6use Gettext\Merge;
7use Gettext\Translations;
8use Gettext\Translator;
9use Gettext\TranslatorInterface;
10use Shaarli\Config\ConfigManager;
11
3/** 12/**
4 * Wrapper function for translation which match the API 13 * Class Languages
5 * of gettext()/_() and ngettext(). 14 *
15 * Load Shaarli translations using 'gettext/gettext'.
16 * This class allows to either use PHP gettext extension, or a PHP implementation of gettext,
17 * with a fixed language, or dynamically using autoLocale().
6 * 18 *
7 * Not doing translation for now. 19 * Translation files PO/MO files follow gettext standard and must be placed under:
20 * <translation path>/<language>/LC_MESSAGES/<domain>.[po|mo]
8 * 21 *
9 * @param string $text Text to translate. 22 * Pros/cons:
10 * @param string $nText The plural message ID. 23 * - gettext extension is faster
11 * @param int $nb The number of items for plural forms. 24 * - gettext is very system dependent (PHP extension, the locale must be installed, and web server reloaded)
12 * 25 *
13 * @return String Text translated. 26 * Settings:
27 * - translation.mode:
28 * - auto: use default setting (PHP implementation)
29 * - php: use PHP implementation
30 * - gettext: use gettext wrapper
31 * - translation.language:
32 * - auto: use autoLocale() and the language change according to user HTTP headers
33 * - fixed language: e.g. 'fr'
34 * - translation.extensions:
35 * - domain => translation_path: allow plugins and themes to extend the defaut extension
36 * The domain must be unique, and translation path must be relative, and contains the tree mentioned above.
37 *
38 * @package Shaarli
14 */ 39 */
15function t($text, $nText = '', $nb = 0) { 40class Languages
16 if (empty($nText)) { 41{
17 return $text; 42 /**
43 * Core translations domain
44 */
45 const DEFAULT_DOMAIN = 'shaarli';
46
47 /**
48 * @var TranslatorInterface
49 */
50 protected $translator;
51
52 /**
53 * @var string
54 */
55 protected $language;
56
57 /**
58 * @var ConfigManager
59 */
60 protected $conf;
61
62 /**
63 * Languages constructor.
64 *
65 * @param string $language lang determined by autoLocale(), can be override.
66 * @param ConfigManager $conf instance.
67 */
68 public function __construct($language, $conf)
69 {
70 $this->conf = $conf;
71 $confLanguage = $this->conf->get('translation.language', 'auto');
72 if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) {
73 $this->language = substr($language, 0, 5);
74 } else {
75 $this->language = $confLanguage;
76 }
77
78 if (! extension_loaded('gettext')
79 || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php'])
80 ) {
81 $this->initPhpTranslator();
82 } else {
83 $this->initGettextTranslator();
84 }
85
86 // Register default functions (e.g. '__()') to use our Translator
87 $this->translator->register();
88 }
89
90 /**
91 * Initialize the translator using php gettext extension (gettext dependency act as a wrapper).
92 */
93 protected function initGettextTranslator ()
94 {
95 $this->translator = new GettextTranslator();
96 $this->translator->setLanguage($this->language);
97 $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages');
98
99 foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
100 if ($domain !== self::DEFAULT_DOMAIN) {
101 $this->translator->loadDomain($domain, $translationPath, false);
102 }
103 }
104 }
105
106 /**
107 * Initialize the translator using a PHP implementation of gettext.
108 *
109 * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language).
110 */
111 protected function initPhpTranslator()
112 {
113 $this->translator = new Translator();
114 $translations = new Translations();
115 // Core translations
116 try {
117 /** @var Translations $translations */
118 $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po');
119 $translations->setDomain('shaarli');
120 $this->translator->loadTranslations($translations);
121 } catch (\InvalidArgumentException $e) {}
122
123
124 // Extension translations (plugins, themes, etc.).
125 foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
126 if ($domain === self::DEFAULT_DOMAIN) {
127 continue;
128 }
129
130 try {
131 /** @var Translations $extension */
132 $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po');
133 $extension->setDomain($domain);
134 $this->translator->loadTranslations($extension);
135 } catch (\InvalidArgumentException $e) {}
136 }
137 }
138
139 /**
140 * Checks if a language string is valid.
141 *
142 * @param string $language e.g. 'fr' or 'en_US'
143 *
144 * @return bool true if valid, false otherwise
145 */
146 protected function isValidLanguage($language)
147 {
148 return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1;
18 } 149 }
19 $actualForm = $nb > 1 ? $nText : $text;
20 return sprintf($actualForm, $nb);
21} 150}
diff --git a/application/LinkDB.php b/application/LinkDB.php
index 22c1f0ab..f026a041 100644
--- a/application/LinkDB.php
+++ b/application/LinkDB.php
@@ -133,16 +133,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess
133 { 133 {
134 // TODO: use exceptions instead of "die" 134 // TODO: use exceptions instead of "die"
135 if (!$this->loggedIn) { 135 if (!$this->loggedIn) {
136 die('You are not authorized to add a link.'); 136 die(t('You are not authorized to add a link.'));
137 } 137 }
138 if (!isset($value['id']) || empty($value['url'])) { 138 if (!isset($value['id']) || empty($value['url'])) {
139 die('Internal Error: A link should always have an id and URL.'); 139 die(t('Internal Error: A link should always have an id and URL.'));
140 } 140 }
141 if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { 141 if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) {
142 die('You must specify an integer as a key.'); 142 die(t('You must specify an integer as a key.'));
143 } 143 }
144 if ($offset !== null && $offset !== $value['id']) { 144 if ($offset !== null && $offset !== $value['id']) {
145 die('Array offset and link ID must be equal.'); 145 die(t('Array offset and link ID must be equal.'));
146 } 146 }
147 147
148 // If the link exists, we reuse the real offset, otherwise new entry 148 // If the link exists, we reuse the real offset, otherwise new entry
@@ -248,13 +248,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess
248 $this->links = array(); 248 $this->links = array();
249 $link = array( 249 $link = array(
250 'id' => 1, 250 'id' => 1,
251 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', 251 'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'),
252 'url'=>'https://shaarli.readthedocs.io', 252 'url'=>'https://shaarli.readthedocs.io',
253 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. 253 'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login.
254 254
255To learn how to use Shaarli, consult the link "Help/documentation" at the bottom of this page. 255To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page.
256 256
257You use the community supported version of the original Shaarli project, by Sebastien Sauvage.', 257You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'),
258 'private'=>0, 258 'private'=>0,
259 'created'=> new DateTime(), 259 'created'=> new DateTime(),
260 'tags'=>'opensource software' 260 'tags'=>'opensource software'
@@ -264,9 +264,9 @@ You use the community supported version of the original Shaarli project, by Seba
264 264
265 $link = array( 265 $link = array(
266 'id' => 0, 266 'id' => 0,
267 'title'=>'My secret stuff... - Pastebin.com', 267 'title'=> t('My secret stuff... - Pastebin.com'),
268 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', 268 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
269 'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.', 269 'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'),
270 'private'=>1, 270 'private'=>1,
271 'created'=> new DateTime('1 minute ago'), 271 'created'=> new DateTime('1 minute ago'),
272 'tags'=>'secretstuff', 272 'tags'=>'secretstuff',
diff --git a/application/LinkFilter.php b/application/LinkFilter.php
index 99ecd1e2..12376e27 100644
--- a/application/LinkFilter.php
+++ b/application/LinkFilter.php
@@ -444,5 +444,11 @@ class LinkFilter
444 444
445class LinkNotFoundException extends Exception 445class LinkNotFoundException extends Exception
446{ 446{
447 protected $message = 'The link you are trying to reach does not exist or has been deleted.'; 447 /**
448 * LinkNotFoundException constructor.
449 */
450 public function __construct()
451 {
452 $this->message = t('The link you are trying to reach does not exist or has been deleted.');
453 }
448} 454}
diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php
index 31796367..31a14537 100644
--- a/application/NetscapeBookmarkUtils.php
+++ b/application/NetscapeBookmarkUtils.php
@@ -32,11 +32,11 @@ class NetscapeBookmarkUtils
32 { 32 {
33 // see tpl/export.html for possible values 33 // see tpl/export.html for possible values
34 if (! in_array($selection, array('all', 'public', 'private'))) { 34 if (! in_array($selection, array('all', 'public', 'private'))) {
35 throw new Exception('Invalid export selection: "'.$selection.'"'); 35 throw new Exception(t('Invalid export selection:') .' "'.$selection.'"');
36 } 36 }
37 37
38 $bookmarkLinks = array(); 38 $bookmarkLinks = array();
39 397
40 foreach ($linkDb as $link) { 40 foreach ($linkDb as $link) {
41 if ($link['private'] != 0 && $selection == 'public') { 41 if ($link['private'] != 0 && $selection == 'public') {
42 continue; 42 continue;
@@ -79,14 +79,14 @@ class NetscapeBookmarkUtils
79 $duration=0 79 $duration=0
80 ) 80 )
81 { 81 {
82 $status = 'File '.$filename.' ('.$filesize.' bytes) '; 82 $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize);
83 if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { 83 if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) {
84 $status .= 'has an unknown file format. Nothing was imported.'; 84 $status .= t('has an unknown file format. Nothing was imported.');
85 } else { 85 } else {
86 $status .= 'was successfully processed in '. $duration .' seconds: '; 86 $status .= vsprintf(
87 $status .= $importCount.' links imported, '; 87 t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'),
88 $status .= $overwriteCount.' links overwritten, '; 88 [$duration, $importCount, $overwriteCount, $skipCount]
89 $status .= $skipCount.' links skipped.'; 89 );
90 } 90 }
91 return $status; 91 return $status;
92 } 92 }
diff --git a/application/PageBuilder.php b/application/PageBuilder.php
index 291860ad..af290671 100644
--- a/application/PageBuilder.php
+++ b/application/PageBuilder.php
@@ -159,9 +159,12 @@ class PageBuilder
159 * 159 *
160 * @param string $message A messate to display what is not found 160 * @param string $message A messate to display what is not found
161 */ 161 */
162 public function render404($message = 'The page you are trying to reach does not exist or has been deleted.') 162 public function render404($message = '')
163 { 163 {
164 header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); 164 if (empty($message)) {
165 $message = t('The page you are trying to reach does not exist or has been deleted.');
166 }
167 header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found'));
165 $this->tpl->assign('error_message', $message); 168 $this->tpl->assign('error_message', $message);
166 $this->renderPage('404'); 169 $this->renderPage('404');
167 } 170 }
diff --git a/application/PluginManager.php b/application/PluginManager.php
index 59ece4fa..cf603845 100644
--- a/application/PluginManager.php
+++ b/application/PluginManager.php
@@ -188,6 +188,9 @@ class PluginManager
188 $metaData[$plugin] = parse_ini_file($metaFile); 188 $metaData[$plugin] = parse_ini_file($metaFile);
189 $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins); 189 $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins);
190 190
191 if (isset($metaData[$plugin]['description'])) {
192 $metaData[$plugin]['description'] = t($metaData[$plugin]['description']);
193 }
191 // Read parameters and format them into an array. 194 // Read parameters and format them into an array.
192 if (isset($metaData[$plugin]['parameters'])) { 195 if (isset($metaData[$plugin]['parameters'])) {
193 $params = explode(';', $metaData[$plugin]['parameters']); 196 $params = explode(';', $metaData[$plugin]['parameters']);
@@ -203,7 +206,7 @@ class PluginManager
203 $metaData[$plugin]['parameters'][$param]['value'] = ''; 206 $metaData[$plugin]['parameters'][$param]['value'] = '';
204 // Optional parameter description in parameter.PARAM_NAME= 207 // Optional parameter description in parameter.PARAM_NAME=
205 if (isset($metaData[$plugin]['parameter.'. $param])) { 208 if (isset($metaData[$plugin]['parameter.'. $param])) {
206 $metaData[$plugin]['parameters'][$param]['desc'] = $metaData[$plugin]['parameter.'. $param]; 209 $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]);
207 } 210 }
208 } 211 }
209 } 212 }
@@ -237,6 +240,6 @@ class PluginFileNotFoundException extends Exception
237 */ 240 */
238 public function __construct($pluginName) 241 public function __construct($pluginName)
239 { 242 {
240 $this->message = 'Plugin "'. $pluginName .'" files not found.'; 243 $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
241 } 244 }
242} 245}
diff --git a/application/Updater.php b/application/Updater.php
index 72b2def0..723a7a81 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -73,7 +73,7 @@ class Updater
73 } 73 }
74 74
75 if ($this->methods === null) { 75 if ($this->methods === null) {
76 throw new UpdaterException('Couldn\'t retrieve Updater class methods.'); 76 throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.'));
77 } 77 }
78 78
79 foreach ($this->methods as $method) { 79 foreach ($this->methods as $method) {
@@ -482,7 +482,7 @@ class UpdaterException extends Exception
482 } 482 }
483 483
484 if (! empty($this->method)) { 484 if (! empty($this->method)) {
485 $out .= 'An error occurred while running the update '. $this->method . PHP_EOL; 485 $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL;
486 } 486 }
487 487
488 if (! empty($this->previous)) { 488 if (! empty($this->previous)) {
@@ -522,11 +522,11 @@ function read_updates_file($updatesFilepath)
522function write_updates_file($updatesFilepath, $updates) 522function write_updates_file($updatesFilepath, $updates)
523{ 523{
524 if (empty($updatesFilepath)) { 524 if (empty($updatesFilepath)) {
525 throw new Exception('Updates file path is not set, can\'t write updates.'); 525 throw new Exception(t('Updates file path is not set, can\'t write updates.'));
526 } 526 }
527 527
528 $res = file_put_contents($updatesFilepath, implode(';', $updates)); 528 $res = file_put_contents($updatesFilepath, implode(';', $updates));
529 if ($res === false) { 529 if ($res === false) {
530 throw new Exception('Unable to write updates in '. $updatesFilepath . '.'); 530 throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
531 } 531 }
532} 532}
diff --git a/application/Utils.php b/application/Utils.php
index 4a2f5561..27eaafc5 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -452,7 +452,7 @@ function get_max_upload_size($limitPost, $limitUpload, $format = true)
452 */ 452 */
453function alphabetical_sort(&$data, $reverse = false, $byKeys = false) 453function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
454{ 454{
455 $callback = function($a, $b) use ($reverse) { 455 $callback = function ($a, $b) use ($reverse) {
456 // Collator is part of PHP intl. 456 // Collator is part of PHP intl.
457 if (class_exists('Collator')) { 457 if (class_exists('Collator')) {
458 $collator = new Collator(setlocale(LC_COLLATE, 0)); 458 $collator = new Collator(setlocale(LC_COLLATE, 0));
@@ -470,3 +470,18 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
470 usort($data, $callback); 470 usort($data, $callback);
471 } 471 }
472} 472}
473
474/**
475 * Wrapper function for translation which match the API
476 * of gettext()/_() and ngettext().
477 *
478 * @param string $text Text to translate.
479 * @param string $nText The plural message ID.
480 * @param int $nb The number of items for plural forms.
481 * @param string $domain The domain where the translation is stored (default: shaarli).
482 *
483 * @return String Text translated.
484 */
485function t($text, $nText = '', $nb = 1, $domain = 'shaarli') {
486 return dn__($domain, $text, $nText, $nb);
487}
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php
index 9ef2ef56..8c8d5610 100644
--- a/application/config/ConfigJson.php
+++ b/application/config/ConfigJson.php
@@ -22,10 +22,15 @@ class ConfigJson implements ConfigIO
22 $data = json_decode($data, true); 22 $data = json_decode($data, true);
23 if ($data === null) { 23 if ($data === null) {
24 $errorCode = json_last_error(); 24 $errorCode = json_last_error();
25 $error = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #'; 25 $error = sprintf(
26 $error .= $errorCode. '<br>➜ <code>' . json_last_error_msg() .'</code>'; 26 'An error occurred while parsing JSON configuration file (%s): error code #%d',
27 $filepath,
28 $errorCode
29 );
30 $error .= '<br>➜ <code>' . json_last_error_msg() .'</code>';
27 if ($errorCode === JSON_ERROR_SYNTAX) { 31 if ($errorCode === JSON_ERROR_SYNTAX) {
28 $error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as '; 32 $error .= '<br>';
33 $error .= 'Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
29 $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.'; 34 $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.';
30 } 35 }
31 throw new \Exception($error); 36 throw new \Exception($error);
@@ -44,8 +49,8 @@ class ConfigJson implements ConfigIO
44 if (!file_put_contents($filepath, $data)) { 49 if (!file_put_contents($filepath, $data)) {
45 throw new \IOException( 50 throw new \IOException(
46 $filepath, 51 $filepath,
47 'Shaarli could not create the config file. 52 t('Shaarli could not create the config file. '.
48 Please make sure Shaarli has the right to write in the folder is it installed in.' 53 'Please make sure Shaarli has the right to write in the folder is it installed in.')
49 ); 54 );
50 } 55 }
51 } 56 }
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 7ff2fe67..9e4c9f63 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -132,7 +132,7 @@ class ConfigManager
132 public function set($setting, $value, $write = false, $isLoggedIn = false) 132 public function set($setting, $value, $write = false, $isLoggedIn = false)
133 { 133 {
134 if (empty($setting) || ! is_string($setting)) { 134 if (empty($setting) || ! is_string($setting)) {
135 throw new \Exception('Invalid setting key parameter. String expected, got: '. gettype($setting)); 135 throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting));
136 } 136 }
137 137
138 // During the ConfigIO transition, map legacy settings to the new ones. 138 // During the ConfigIO transition, map legacy settings to the new ones.
@@ -339,6 +339,10 @@ class ConfigManager
339 $this->setEmpty('redirector.url', ''); 339 $this->setEmpty('redirector.url', '');
340 $this->setEmpty('redirector.encode_url', true); 340 $this->setEmpty('redirector.encode_url', true);
341 341
342 $this->setEmpty('translation.language', 'auto');
343 $this->setEmpty('translation.mode', 'php');
344 $this->setEmpty('translation.extensions', []);
345
342 $this->setEmpty('plugins', array()); 346 $this->setEmpty('plugins', array());
343 } 347 }
344 348
diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php
index 2633824d..2f66e8e0 100644
--- a/application/config/ConfigPhp.php
+++ b/application/config/ConfigPhp.php
@@ -118,8 +118,8 @@ class ConfigPhp implements ConfigIO
118 ) { 118 ) {
119 throw new \IOException( 119 throw new \IOException(
120 $filepath, 120 $filepath,
121 'Shaarli could not create the config file. 121 t('Shaarli could not create the config file. '.
122 Please make sure Shaarli has the right to write in the folder is it installed in.' 122 'Please make sure Shaarli has the right to write in the folder is it installed in.')
123 ); 123 );
124 } 124 }
125 } 125 }
diff --git a/application/config/exception/MissingFieldConfigException.php b/application/config/exception/MissingFieldConfigException.php
index 6346c6a9..9e0a9359 100644
--- a/application/config/exception/MissingFieldConfigException.php
+++ b/application/config/exception/MissingFieldConfigException.php
@@ -18,6 +18,6 @@ class MissingFieldConfigException extends \Exception
18 public function __construct($field) 18 public function __construct($field)
19 { 19 {
20 $this->field = $field; 20 $this->field = $field;
21 $this->message = 'Configuration value is required for '. $this->field; 21 $this->message = sprintf(t('Configuration value is required for %s'), $this->field);
22 } 22 }
23} 23}
diff --git a/application/config/exception/PluginConfigOrderException.php b/application/config/exception/PluginConfigOrderException.php
index f9d68750..f82ec26e 100644
--- a/application/config/exception/PluginConfigOrderException.php
+++ b/application/config/exception/PluginConfigOrderException.php
@@ -12,6 +12,6 @@ class PluginConfigOrderException extends \Exception
12 */ 12 */
13 public function __construct() 13 public function __construct()
14 { 14 {
15 $this->message = 'An error occurred while trying to save plugins loading order.'; 15 $this->message = t('An error occurred while trying to save plugins loading order.');
16 } 16 }
17} 17}
diff --git a/application/config/exception/UnauthorizedConfigException.php b/application/config/exception/UnauthorizedConfigException.php
index 79672c1b..72311fae 100644
--- a/application/config/exception/UnauthorizedConfigException.php
+++ b/application/config/exception/UnauthorizedConfigException.php
@@ -13,6 +13,6 @@ class UnauthorizedConfigException extends \Exception
13 */ 13 */
14 public function __construct() 14 public function __construct()
15 { 15 {
16 $this->message = 'You are not authorized to alter config.'; 16 $this->message = t('You are not authorized to alter config.');
17 } 17 }
18} 18}
diff --git a/application/exceptions/IOException.php b/application/exceptions/IOException.php
index b563b23d..18e46b77 100644
--- a/application/exceptions/IOException.php
+++ b/application/exceptions/IOException.php
@@ -16,7 +16,7 @@ class IOException extends Exception
16 public function __construct($path, $message = '') 16 public function __construct($path, $message = '')
17 { 17 {
18 $this->path = $path; 18 $this->path = $path;
19 $this->message = empty($message) ? 'Error accessing' : $message; 19 $this->message = empty($message) ? t('Error accessing') : $message;
20 $this->message .= ' "' . $this->path .'"'; 20 $this->message .= ' "' . $this->path .'"';
21 } 21 }
22} 22}
diff --git a/composer.json b/composer.json
index afb8aca4..f331d6ca 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,8 @@
19 "shaarli/netscape-bookmark-parser": "^2.0", 19 "shaarli/netscape-bookmark-parser": "^2.0",
20 "erusev/parsedown": "1.6", 20 "erusev/parsedown": "1.6",
21 "slim/slim": "^3.0", 21 "slim/slim": "^3.0",
22 "pubsubhubbub/publisher": "dev-master" 22 "pubsubhubbub/publisher": "dev-master",
23 "gettext/gettext": "^4.4"
23 }, 24 },
24 "require-dev": { 25 "require-dev": {
25 "phpmd/phpmd" : "@stable", 26 "phpmd/phpmd" : "@stable",
diff --git a/composer.lock b/composer.lock
index 435d6a88..ea20025d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 "content-hash": "68beedbfa104c788029b079800cfd6e8", 7 "content-hash": "13b7e1e474fe9264b098ba86face0feb",
8 "packages": [ 8 "packages": [
9 { 9 {
10 "name": "container-interop/container-interop", 10 "name": "container-interop/container-interop",
@@ -77,6 +77,129 @@
77 "time": "2015-10-04T16:44:32+00:00" 77 "time": "2015-10-04T16:44:32+00:00"
78 }, 78 },
79 { 79 {
80 "name": "gettext/gettext",
81 "version": "v4.4.3",
82 "source": {
83 "type": "git",
84 "url": "https://github.com/oscarotero/Gettext.git",
85 "reference": "4f57f004635cc6311a20815ebfdc0757cb337113"
86 },
87 "dist": {
88 "type": "zip",
89 "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/4f57f004635cc6311a20815ebfdc0757cb337113",
90 "reference": "4f57f004635cc6311a20815ebfdc0757cb337113",
91 "shasum": ""
92 },
93 "require": {
94 "gettext/languages": "^2.3",
95 "php": ">=5.4.0"
96 },
97 "require-dev": {
98 "illuminate/view": "*",
99 "phpunit/phpunit": "^4.8|^5.7",
100 "squizlabs/php_codesniffer": "^3.0",
101 "symfony/yaml": "~2",
102 "twig/extensions": "*",
103 "twig/twig": "^1.31|^2.0"
104 },
105 "suggest": {
106 "illuminate/view": "Is necessary if you want to use the Blade extractor",
107 "symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator",
108 "twig/extensions": "Is necessary if you want to use the Twig extractor",
109 "twig/twig": "Is necessary if you want to use the Twig extractor"
110 },
111 "type": "library",
112 "autoload": {
113 "psr-4": {
114 "Gettext\\": "src"
115 }
116 },
117 "notification-url": "https://packagist.org/downloads/",
118 "license": [
119 "MIT"
120 ],
121 "authors": [
122 {
123 "name": "Oscar Otero",
124 "email": "oom@oscarotero.com",
125 "homepage": "http://oscarotero.com",
126 "role": "Developer"
127 }
128 ],
129 "description": "PHP gettext manager",
130 "homepage": "https://github.com/oscarotero/Gettext",
131 "keywords": [
132 "JS",
133 "gettext",
134 "i18n",
135 "mo",
136 "po",
137 "translation"
138 ],
139 "time": "2017-08-09T16:59:46+00:00"
140 },
141 {
142 "name": "gettext/languages",
143 "version": "2.3.0",
144 "source": {
145 "type": "git",
146 "url": "https://github.com/mlocati/cldr-to-gettext-plural-rules.git",
147 "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7"
148 },
149 "dist": {
150 "type": "zip",
151 "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/49c39e51569963cc917a924b489e7025bfb9d8c7",
152 "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7",
153 "shasum": ""
154 },
155 "require": {
156 "php": ">=5.3"
157 },
158 "require-dev": {
159 "phpunit/phpunit": "^4"
160 },
161 "bin": [
162 "bin/export-plural-rules",
163 "bin/export-plural-rules.php"
164 ],
165 "type": "library",
166 "autoload": {
167 "psr-4": {
168 "Gettext\\Languages\\": "src/"
169 }
170 },
171 "notification-url": "https://packagist.org/downloads/",
172 "license": [
173 "MIT"
174 ],
175 "authors": [
176 {
177 "name": "Michele Locati",
178 "email": "mlocati@gmail.com",
179 "role": "Developer"
180 }
181 ],
182 "description": "gettext languages with plural rules",
183 "homepage": "https://github.com/mlocati/cldr-to-gettext-plural-rules",
184 "keywords": [
185 "cldr",
186 "i18n",
187 "internationalization",
188 "l10n",
189 "language",
190 "languages",
191 "localization",
192 "php",
193 "plural",
194 "plural rules",
195 "plurals",
196 "translate",
197 "translations",
198 "unicode"
199 ],
200 "time": "2017-03-23T17:02:28+00:00"
201 },
202 {
80 "name": "katzgrau/klogger", 203 "name": "katzgrau/klogger",
81 "version": "1.2.1", 204 "version": "1.2.1",
82 "source": { 205 "source": {
@@ -686,16 +809,16 @@
686 }, 809 },
687 { 810 {
688 "name": "phpdocumentor/reflection-docblock", 811 "name": "phpdocumentor/reflection-docblock",
689 "version": "3.2.1", 812 "version": "3.2.2",
690 "source": { 813 "source": {
691 "type": "git", 814 "type": "git",
692 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 815 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
693 "reference": "183824db76118b9dddffc7e522b91fa175f75119" 816 "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157"
694 }, 817 },
695 "dist": { 818 "dist": {
696 "type": "zip", 819 "type": "zip",
697 "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/183824db76118b9dddffc7e522b91fa175f75119", 820 "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157",
698 "reference": "183824db76118b9dddffc7e522b91fa175f75119", 821 "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157",
699 "shasum": "" 822 "shasum": ""
700 }, 823 },
701 "require": { 824 "require": {
@@ -727,7 +850,7 @@
727 } 850 }
728 ], 851 ],
729 "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 852 "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
730 "time": "2017-08-04T20:55:59+00:00" 853 "time": "2017-08-08T06:39:58+00:00"
731 }, 854 },
732 { 855 {
733 "name": "phpdocumentor/type-resolver", 856 "name": "phpdocumentor/type-resolver",
@@ -1875,20 +1998,20 @@
1875 }, 1998 },
1876 { 1999 {
1877 "name": "symfony/config", 2000 "name": "symfony/config",
1878 "version": "v3.3.6", 2001 "version": "v3.3.8",
1879 "source": { 2002 "source": {
1880 "type": "git", 2003 "type": "git",
1881 "url": "https://github.com/symfony/config.git", 2004 "url": "https://github.com/symfony/config.git",
1882 "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297" 2005 "reference": "6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488"
1883 }, 2006 },
1884 "dist": { 2007 "dist": {
1885 "type": "zip", 2008 "type": "zip",
1886 "url": "https://api.github.com/repos/symfony/config/zipball/54ee12b0dd60f294132cabae6f5da9573d2e5297", 2009 "url": "https://api.github.com/repos/symfony/config/zipball/6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488",
1887 "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297", 2010 "reference": "6ac0cc1f047c1dbc058fc25b7a4d91b068ed4488",
1888 "shasum": "" 2011 "shasum": ""
1889 }, 2012 },
1890 "require": { 2013 "require": {
1891 "php": ">=5.5.9", 2014 "php": "^5.5.9|>=7.0.8",
1892 "symfony/filesystem": "~2.8|~3.0" 2015 "symfony/filesystem": "~2.8|~3.0"
1893 }, 2016 },
1894 "conflict": { 2017 "conflict": {
@@ -1933,20 +2056,20 @@
1933 ], 2056 ],
1934 "description": "Symfony Config Component", 2057 "description": "Symfony Config Component",
1935 "homepage": "https://symfony.com", 2058 "homepage": "https://symfony.com",
1936 "time": "2017-07-19T07:37:29+00:00" 2059 "time": "2017-08-03T08:59:45+00:00"
1937 }, 2060 },
1938 { 2061 {
1939 "name": "symfony/console", 2062 "name": "symfony/console",
1940 "version": "v2.8.26", 2063 "version": "v2.8.27",
1941 "source": { 2064 "source": {
1942 "type": "git", 2065 "type": "git",
1943 "url": "https://github.com/symfony/console.git", 2066 "url": "https://github.com/symfony/console.git",
1944 "reference": "32a3c6b3398de5db8ed381f4ef92970c59c2fcdd" 2067 "reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253"
1945 }, 2068 },
1946 "dist": { 2069 "dist": {
1947 "type": "zip", 2070 "type": "zip",
1948 "url": "https://api.github.com/repos/symfony/console/zipball/32a3c6b3398de5db8ed381f4ef92970c59c2fcdd", 2071 "url": "https://api.github.com/repos/symfony/console/zipball/c0807a2ca978e64d8945d373a9221a5c35d1a253",
1949 "reference": "32a3c6b3398de5db8ed381f4ef92970c59c2fcdd", 2072 "reference": "c0807a2ca978e64d8945d373a9221a5c35d1a253",
1950 "shasum": "" 2073 "shasum": ""
1951 }, 2074 },
1952 "require": { 2075 "require": {
@@ -1994,7 +2117,7 @@
1994 ], 2117 ],
1995 "description": "Symfony Console Component", 2118 "description": "Symfony Console Component",
1996 "homepage": "https://symfony.com", 2119 "homepage": "https://symfony.com",
1997 "time": "2017-07-29T21:26:04+00:00" 2120 "time": "2017-08-27T14:29:03+00:00"
1998 }, 2121 },
1999 { 2122 {
2000 "name": "symfony/debug", 2123 "name": "symfony/debug",
@@ -2055,20 +2178,20 @@
2055 }, 2178 },
2056 { 2179 {
2057 "name": "symfony/dependency-injection", 2180 "name": "symfony/dependency-injection",
2058 "version": "v3.3.6", 2181 "version": "v3.3.8",
2059 "source": { 2182 "source": {
2060 "type": "git", 2183 "type": "git",
2061 "url": "https://github.com/symfony/dependency-injection.git", 2184 "url": "https://github.com/symfony/dependency-injection.git",
2062 "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0" 2185 "reference": "2ac658972626c75cbde7b0067c84b988170a6907"
2063 }, 2186 },
2064 "dist": { 2187 "dist": {
2065 "type": "zip", 2188 "type": "zip",
2066 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8d70987f991481e809c63681ffe8ce3f3fde68a0", 2189 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2ac658972626c75cbde7b0067c84b988170a6907",
2067 "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0", 2190 "reference": "2ac658972626c75cbde7b0067c84b988170a6907",
2068 "shasum": "" 2191 "shasum": ""
2069 }, 2192 },
2070 "require": { 2193 "require": {
2071 "php": ">=5.5.9", 2194 "php": "^5.5.9|>=7.0.8",
2072 "psr/container": "^1.0" 2195 "psr/container": "^1.0"
2073 }, 2196 },
2074 "conflict": { 2197 "conflict": {
@@ -2121,24 +2244,24 @@
2121 ], 2244 ],
2122 "description": "Symfony DependencyInjection Component", 2245 "description": "Symfony DependencyInjection Component",
2123 "homepage": "https://symfony.com", 2246 "homepage": "https://symfony.com",
2124 "time": "2017-07-28T15:27:31+00:00" 2247 "time": "2017-08-28T22:20:37+00:00"
2125 }, 2248 },
2126 { 2249 {
2127 "name": "symfony/filesystem", 2250 "name": "symfony/filesystem",
2128 "version": "v3.3.6", 2251 "version": "v3.3.8",
2129 "source": { 2252 "source": {
2130 "type": "git", 2253 "type": "git",
2131 "url": "https://github.com/symfony/filesystem.git", 2254 "url": "https://github.com/symfony/filesystem.git",
2132 "reference": "427987eb4eed764c3b6e38d52a0f87989e010676" 2255 "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb"
2133 }, 2256 },
2134 "dist": { 2257 "dist": {
2135 "type": "zip", 2258 "type": "zip",
2136 "url": "https://api.github.com/repos/symfony/filesystem/zipball/427987eb4eed764c3b6e38d52a0f87989e010676", 2259 "url": "https://api.github.com/repos/symfony/filesystem/zipball/b32a0e5f928d0fa3d1dd03c78d020777e50c10cb",
2137 "reference": "427987eb4eed764c3b6e38d52a0f87989e010676", 2260 "reference": "b32a0e5f928d0fa3d1dd03c78d020777e50c10cb",
2138 "shasum": "" 2261 "shasum": ""
2139 }, 2262 },
2140 "require": { 2263 "require": {
2141 "php": ">=5.5.9" 2264 "php": "^5.5.9|>=7.0.8"
2142 }, 2265 },
2143 "type": "library", 2266 "type": "library",
2144 "extra": { 2267 "extra": {
@@ -2170,24 +2293,24 @@
2170 ], 2293 ],
2171 "description": "Symfony Filesystem Component", 2294 "description": "Symfony Filesystem Component",
2172 "homepage": "https://symfony.com", 2295 "homepage": "https://symfony.com",
2173 "time": "2017-07-11T07:17:58+00:00" 2296 "time": "2017-07-29T21:54:42+00:00"
2174 }, 2297 },
2175 { 2298 {
2176 "name": "symfony/finder", 2299 "name": "symfony/finder",
2177 "version": "v3.3.6", 2300 "version": "v3.3.8",
2178 "source": { 2301 "source": {
2179 "type": "git", 2302 "type": "git",
2180 "url": "https://github.com/symfony/finder.git", 2303 "url": "https://github.com/symfony/finder.git",
2181 "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4" 2304 "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e"
2182 }, 2305 },
2183 "dist": { 2306 "dist": {
2184 "type": "zip", 2307 "type": "zip",
2185 "url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4", 2308 "url": "https://api.github.com/repos/symfony/finder/zipball/b2260dbc80f3c4198f903215f91a1ac7fe9fe09e",
2186 "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4", 2309 "reference": "b2260dbc80f3c4198f903215f91a1ac7fe9fe09e",
2187 "shasum": "" 2310 "shasum": ""
2188 }, 2311 },
2189 "require": { 2312 "require": {
2190 "php": ">=5.5.9" 2313 "php": "^5.5.9|>=7.0.8"
2191 }, 2314 },
2192 "type": "library", 2315 "type": "library",
2193 "extra": { 2316 "extra": {
@@ -2219,20 +2342,20 @@
2219 ], 2342 ],
2220 "description": "Symfony Finder Component", 2343 "description": "Symfony Finder Component",
2221 "homepage": "https://symfony.com", 2344 "homepage": "https://symfony.com",
2222 "time": "2017-06-01T21:01:25+00:00" 2345 "time": "2017-07-29T21:54:42+00:00"
2223 }, 2346 },
2224 { 2347 {
2225 "name": "symfony/polyfill-mbstring", 2348 "name": "symfony/polyfill-mbstring",
2226 "version": "v1.4.0", 2349 "version": "v1.5.0",
2227 "source": { 2350 "source": {
2228 "type": "git", 2351 "type": "git",
2229 "url": "https://github.com/symfony/polyfill-mbstring.git", 2352 "url": "https://github.com/symfony/polyfill-mbstring.git",
2230 "reference": "f29dca382a6485c3cbe6379f0c61230167681937" 2353 "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803"
2231 }, 2354 },
2232 "dist": { 2355 "dist": {
2233 "type": "zip", 2356 "type": "zip",
2234 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", 2357 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
2235 "reference": "f29dca382a6485c3cbe6379f0c61230167681937", 2358 "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
2236 "shasum": "" 2359 "shasum": ""
2237 }, 2360 },
2238 "require": { 2361 "require": {
@@ -2244,7 +2367,7 @@
2244 "type": "library", 2367 "type": "library",
2245 "extra": { 2368 "extra": {
2246 "branch-alias": { 2369 "branch-alias": {
2247 "dev-master": "1.4-dev" 2370 "dev-master": "1.5-dev"
2248 } 2371 }
2249 }, 2372 },
2250 "autoload": { 2373 "autoload": {
@@ -2278,24 +2401,24 @@
2278 "portable", 2401 "portable",
2279 "shim" 2402 "shim"
2280 ], 2403 ],
2281 "time": "2017-06-09T14:24:12+00:00" 2404 "time": "2017-06-14T15:44:48+00:00"
2282 }, 2405 },
2283 { 2406 {
2284 "name": "symfony/yaml", 2407 "name": "symfony/yaml",
2285 "version": "v3.3.6", 2408 "version": "v3.3.8",
2286 "source": { 2409 "source": {
2287 "type": "git", 2410 "type": "git",
2288 "url": "https://github.com/symfony/yaml.git", 2411 "url": "https://github.com/symfony/yaml.git",
2289 "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed" 2412 "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0"
2290 }, 2413 },
2291 "dist": { 2414 "dist": {
2292 "type": "zip", 2415 "type": "zip",
2293 "url": "https://api.github.com/repos/symfony/yaml/zipball/ddc23324e6cfe066f3dd34a37ff494fa80b617ed", 2416 "url": "https://api.github.com/repos/symfony/yaml/zipball/1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
2294 "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed", 2417 "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
2295 "shasum": "" 2418 "shasum": ""
2296 }, 2419 },
2297 "require": { 2420 "require": {
2298 "php": ">=5.5.9" 2421 "php": "^5.5.9|>=7.0.8"
2299 }, 2422 },
2300 "require-dev": { 2423 "require-dev": {
2301 "symfony/console": "~2.8|~3.0" 2424 "symfony/console": "~2.8|~3.0"
@@ -2333,7 +2456,7 @@
2333 ], 2456 ],
2334 "description": "Symfony Yaml Component", 2457 "description": "Symfony Yaml Component",
2335 "homepage": "https://symfony.com", 2458 "homepage": "https://symfony.com",
2336 "time": "2017-07-23T12:43:26+00:00" 2459 "time": "2017-07-29T21:54:42+00:00"
2337 }, 2460 },
2338 { 2461 {
2339 "name": "theseer/fdomdocument", 2462 "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
index 00000000..d6b195da
--- /dev/null
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.mo
Binary files differ
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
new file mode 100644
index 00000000..8763581b
--- /dev/null
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.po
@@ -0,0 +1,1243 @@
1msgid ""
2msgstr ""
3"Project-Id-Version: Shaarli\n"
4"POT-Creation-Date: 2017-05-20 13:54+0200\n"
5"PO-Revision-Date: 2017-05-20 14:11+0200\n"
6"Last-Translator: \n"
7"Language-Team: Shaarli\n"
8"Language: fr_FR\n"
9"MIME-Version: 1.0\n"
10"Content-Type: text/plain; charset=UTF-8\n"
11"Content-Transfer-Encoding: 8bit\n"
12"X-Generator: Poedit 2.0.1\n"
13"X-Poedit-Basepath: ../../../..\n"
14"Plural-Forms: nplurals=2; plural=(n > 1);\n"
15"X-Poedit-SourceCharset: UTF-8\n"
16"X-Poedit-KeywordsList: t:1,2;t\n"
17"X-Poedit-SearchPath-0: .\n"
18
19#: application/ApplicationUtils.php:152
20#, php-format
21msgid ""
22"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
23"cannot run. Your PHP version has known security vulnerabilities and should "
24"be updated as soon as possible."
25msgstr ""
26"Votre version de PHP est obsolète ! Shaarli nécessite au moins PHP %s, et ne "
27"peut donc pas fonctionner. Votre version de PHP a des failles de sécurités "
28"connues et devrait être mise à jour au plus tôt."
29
30#: application/ApplicationUtils.php:180 application/ApplicationUtils.php:192
31msgid "directory is not readable"
32msgstr "le répertoire n'est pas accessible en lecture"
33
34#: application/ApplicationUtils.php:195
35msgid "directory is not writable"
36msgstr "le répertoire n'est pas accessible en écriture"
37
38#: application/ApplicationUtils.php:213
39msgid "file is not readable"
40msgstr "le fichier n'est pas accessible en lecture"
41
42#: application/ApplicationUtils.php:216
43msgid "file is not writable"
44msgstr "le fichier n'est pas accessible en écriture"
45
46#: application/Cache.php:16
47#, php-format
48msgid "Cannot purge %s: no directory"
49msgstr "Impossible de purger %s: le répertoire n'existe pas"
50
51#: application/FeedBuilder.php:146
52msgid "Direct link"
53msgstr "Liens directs"
54
55#: application/FeedBuilder.php:148
56#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
57#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:242
58#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:245
59#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
60msgid "Permalink"
61msgstr "Permalien"
62
63#: application/History.php:158
64msgid "History file isn't readable or writable"
65msgstr "Le fichier d'historique n'est pas accessible en lecture ou en écriture"
66
67#: application/History.php:169
68msgid "Could not parse history file"
69msgstr "Format incorrect pour le fichier d'historique"
70
71#: application/LinkDB.php:136
72msgid "You are not authorized to add a link."
73msgstr "Vous n'êtes pas autorisé à ajouter un lien."
74
75#: application/LinkDB.php:139
76msgid "Internal Error: A link should always have an id and URL."
77msgstr "Erreur interne : un lien devrait toujours avoir un ID et une URL."
78
79#: application/LinkDB.php:142
80msgid "You must specify an integer as a key."
81msgstr "Vous devez utiliser un entier comme clé."
82
83#: application/LinkDB.php:145
84msgid "Array offset and link ID must be equal."
85msgstr "La clé du tableau et l'ID du lien doivent être égaux."
86
87#: application/LinkDB.php:251
88#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
89msgid ""
90"The personal, minimalist, super-fast, database free, bookmarking service"
91msgstr ""
92"Le gestionnaire de marque-page personnel, minimaliste, et sans base de "
93"données"
94
95#: application/LinkDB.php:253
96msgid ""
97"Welcome to Shaarli! This is your first public bookmark. To edit or delete "
98"me, you must first login.\n"
99"\n"
100"To learn how to use Shaarli, consult the link \"Documentation\" at the "
101"bottom of this page.\n"
102"\n"
103"You use the community supported version of the original Shaarli project, by "
104"Sebastien Sauvage."
105msgstr ""
106"Bienvenue sur Shaarli ! Ceci est votre premier marque-page public. Pour me "
107"modifier ou me supprimer, vous devez d'abord vous connecter.\n"
108"\n"
109"Pour apprendre comment utiliser Shaarli, consultez le lien « Documentation » "
110"en bas de page.\n"
111"\n"
112"Vous utilisez la version supportée par la communauté du projet original "
113"Shaarli, de Sébastien Sauvage."
114
115#: application/LinkDB.php:267
116msgid "My secret stuff... - Pastebin.com"
117msgstr "Mes trucs secrets... - Pastebin.com"
118
119#: application/LinkDB.php:269
120msgid "Shhhh! I'm a private link only YOU can see. You can delete me too."
121msgstr ""
122"Pssst ! Je suis un lien privé que VOUS êtes le seul à voir. Vous pouvez me "
123"supprimer aussi."
124
125#: application/LinkFilter.php:376
126msgid "The link you are trying to reach does not exist or has been deleted."
127msgstr "Le lien que vous essayez de consulter n'existe pas ou a été supprimé."
128
129#: application/NetscapeBookmarkUtils.php:35
130msgid "Invalid export selection:"
131msgstr "Sélection d'export invalide :"
132
133#: application/NetscapeBookmarkUtils.php:80
134#, php-format
135msgid "File %s (%d bytes) "
136msgstr "Le fichier %s (%d octets) "
137
138#: application/NetscapeBookmarkUtils.php:82
139msgid "has an unknown file format. Nothing was imported."
140msgstr "a un format inconnu. Rien n'a été importé."
141
142#: application/NetscapeBookmarkUtils.php:85
143#, php-format
144msgid ""
145"was successfully processed: %d links imported, %d links overwritten, %d "
146"links skipped."
147msgstr ""
148"a été importé avec succès : %d liens importés, %d liens écrasés, %d liens "
149"ignorés."
150
151#: application/PageBuilder.php:159
152msgid "The page you are trying to reach does not exist or has been deleted."
153msgstr "La page que vous essayez de consulter n'existe pas ou a été supprimée."
154
155#: application/PageBuilder.php:161
156#, fuzzy
157#| msgid " 404 Not Found"
158msgid "404 Not Found"
159msgstr "404 Introuvable"
160
161#: application/PluginManager.php:243
162#, php-format
163msgid "Plugin \"%s\" files not found."
164msgstr "Les fichiers de l'extension \"%s\" sont introuvables."
165
166#: application/Updater.php:76
167msgid "Couldn't retrieve Updater class methods."
168msgstr "Impossible de récupérer les méthodes de la classe Updater."
169
170#: application/Updater.php:500
171msgid "An error occurred while running the update "
172msgstr "Une erreur s'est produite lors de l'exécution de la mise à jour "
173
174#: application/Updater.php:540
175msgid "Updates file path is not set, can't write updates."
176msgstr ""
177"Le chemin vers le fichier de mise à jour n'est pas défini, impossible "
178"d'écrire les mises à jour."
179
180#: application/Updater.php:545
181msgid "Unable to write updates in "
182msgstr "Impossible d'écrire les mises à jour dans "
183
184#: application/Utils.php:402 tests/UtilsTest.php:398
185msgid "Setting not set"
186msgstr "Paramètre non défini"
187
188#: application/Utils.php:409 tests/UtilsTest.php:396 tests/UtilsTest.php:397
189msgid "Unlimited"
190msgstr "Illimité"
191
192#: application/Utils.php:412 tests/UtilsTest.php:393 tests/UtilsTest.php:394
193#: tests/UtilsTest.php:408
194msgid "B"
195msgstr "o"
196
197#: application/Utils.php:412 tests/UtilsTest.php:387 tests/UtilsTest.php:388
198#: tests/UtilsTest.php:395
199msgid "kiB"
200msgstr "ko"
201
202#: application/Utils.php:412 tests/UtilsTest.php:389 tests/UtilsTest.php:390
203#: tests/UtilsTest.php:406 tests/UtilsTest.php:407
204msgid "MiB"
205msgstr "Mo"
206
207#: application/Utils.php:412 tests/UtilsTest.php:391 tests/UtilsTest.php:392
208msgid "GiB"
209msgstr "Go"
210
211#: application/config/ConfigJson.php:26
212#, php-format
213msgid ""
214"An error occurred while parsing JSON configuration file (%s): error code #%d"
215msgstr ""
216"Une erreur s'est produite lors de la lecture du fichier de configuration "
217"JSON (%s) : code d'erreur #%d"
218
219#: application/config/ConfigJson.php:33
220msgid ""
221"Please check your JSON syntax (without PHP comment tags) using a JSON lint "
222"tool such as "
223msgstr ""
224"Merci de vérifier la syntaxe JSON (sans les balises de commentaires PHP) en "
225"utilisant un validateur de JSON tel que "
226
227#: application/config/ConfigJson.php:52 application/config/ConfigPhp.php:121
228msgid ""
229"Shaarli could not create the config file. Please make sure Shaarli has the "
230"right to write in the folder is it installed in."
231msgstr ""
232"Shaarli n'a pas pu créer le fichier de configuration. Merci de vérifier que "
233"Shaarli a les droits d'écriture dans le dossier dans lequel il est installé."
234
235#: application/config/ConfigManager.php:135
236msgid "Invalid setting key parameter. String expected, got: "
237msgstr "Clé de paramétrage invalide. Chaîne de caractères obtenue, attendu :"
238
239#: application/config/exception/MissingFieldConfigException.php:21
240#, php-format
241msgid "Configuration value is required for %s"
242msgstr "Le paramètre %s est obligatoire"
243
244#: application/config/exception/PluginConfigOrderException.php:15
245msgid "An error occurred while trying to save plugins loading order."
246msgstr ""
247"Une erreur s'est produite lors de la sauvegarde de l'ordre des extensions."
248
249#: application/config/exception/UnauthorizedConfigException.php:16
250msgid "You are not authorized to alter config."
251msgstr "Vous n'êtes pas autorisé à modifier la configuration."
252
253#: application/exceptions/IOException.php:19
254msgid "Error accessing"
255msgstr "Une erreur s'est produite en accédant à"
256
257#: index.php:48
258msgid ""
259"Error: missing Composer dependencies\n"
260"\n"
261"If you installed Shaarli through Git or using the development branch,\n"
262"please refer to the installation documentation to install PHP dependencies "
263"using Composer:\n"
264msgstr ""
265"Erreur : les dépendances Composer sont manquantes\n"
266"\n"
267"Si vous avez installé Shaarli avec Git ou depuis la branche de "
268"développement\n"
269"merci de consulter la documentation d'installation pour installer les "
270"dépendances Composer :\n"
271"\n"
272
273#: index.php:137
274msgid "Shared links on "
275msgstr "Liens partagés sur "
276
277#: index.php:168
278msgid "Insufficient permissions:"
279msgstr "Permissions insuffisantes :"
280
281#: index.php:415
282msgid "I said: NO. You are banned for the moment. Go away."
283msgstr "NON. Vous êtes banni pour le moment. Revenez plus tard."
284
285#: index.php:479
286msgid "Wrong login/password."
287msgstr "Nom d'utilisateur ou mot de passe incorrects."
288
289#: index.php:1072
290msgid "You are not supposed to change a password on an Open Shaarli."
291msgstr ""
292"Vous n'êtes pas censé modifier le mot de passe d'un Shaarli en mode ouvert."
293
294#: index.php:1077 index.php:1118 index.php:1189 index.php:1243 index.php:1350
295msgid "Wrong token."
296msgstr "Jeton invalide."
297
298#: index.php:1082
299msgid "The old password is not correct."
300msgstr "L'ancien mot de passe est incorrect."
301
302#: index.php:1102
303msgid "Your password has been changed"
304msgstr "Votre mot de passe a été modifié"
305
306#: index.php:1153
307msgid "Configuration was saved."
308msgstr "La configuration a été sauvegardé."
309
310#: index.php:1206
311#, php-format
312msgid "Tag was removed from %d links."
313msgstr "Le tag a été supprimé de %d liens."
314
315#: index.php:1225
316#, php-format
317msgid "Tag was renamed in %d links."
318msgstr "Le tag a été renommé dans %d liens."
319
320#: index.php:1544
321#, php-format
322msgid ""
323"The file you are trying to upload is probably bigger than what this "
324"webserver can accept (%s). Please upload in smaller chunks."
325msgstr ""
326"Le fichier que vous essayer d'envoyer est probablement plus lourd que ce que "
327"le serveur web peut accepter (%s). Merci de l'envoyer en parties plus "
328"légères."
329
330#: index.php:1941
331#, php-format
332msgid ""
333"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
334"variable \"session.save_path\" is set correctly in your PHP config, and that "
335"you have write access to it.<br>It currently points to %s.<br>On some "
336"browsers, accessing your server via a hostname like 'localhost' or any "
337"custom hostname without a dot causes cookie storage to fail. We recommend "
338"accessing your server via it's IP address or Fully Qualified Domain Name.<br>"
339msgstr ""
340"<pre>Les sesssions ne semble pas fonctionner sur ce serveur.<br>Assurez vous "
341"que la variable « session.save_path » est correctement définie dans votre "
342"fichier de configuration PHP, et que vous y avez les droits d'écriture."
343"<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains navigateurs, "
344"accéder à votre serveur depuis un nom d'hôte comme « localhost » ou autre "
345"nom personnalisé sans point '.' entraine l'échec de la sauvegarde des "
346"cookies. Nous vous recommandons d'accéder à votre serveur depuis son adresse "
347"IP ou un <em>Fully Qualified Domain Name</em>.<br>"
348
349#: index.php:1951
350msgid "Click to try again."
351msgstr "Cliquer ici pour réessayer."
352
353#: plugins/addlink_toolbar/addlink_toolbar.php:29
354msgid "URI"
355msgstr "URI"
356
357#: plugins/addlink_toolbar/addlink_toolbar.php:33
358#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
359msgid "Add link"
360msgstr "Shaare"
361
362#: plugins/addlink_toolbar/addlink_toolbar.php:50
363msgid "Adds the addlink input on the linklist page."
364msgstr "Ajout le formulaire d'ajout de liens sur la page principale."
365
366#: plugins/archiveorg/archiveorg.php:23
367msgid "View on archive.org"
368msgstr "Voir sur archive.org"
369
370#: plugins/archiveorg/archiveorg.php:36
371msgid "For each link, add an Archive.org icon."
372msgstr "Pour chaque lien, ajoute une icône pour Archive.org."
373
374#: plugins/demo_plugin/demo_plugin.php:443
375msgid ""
376"A demo plugin covering all use cases for template designers and plugin "
377"developers."
378msgstr ""
379"Une extension de démonstration couvrant tous les cas d'utilisation pour les "
380"designers et les développeurs."
381
382#: plugins/isso/isso.php:20
383msgid ""
384"Isso plugin error: Please define the \"ISSO_SERVER\" setting in the plugin "
385"administration page."
386msgstr ""
387"Erreur de l'extension Isso : Merci de définir le paramètre « ISSO_SERVER » "
388"dans la page d'administration des extensions."
389
390#: plugins/isso/isso.php:63
391msgid "Let visitor comment your shaares on permalinks with Isso."
392msgstr ""
393"Permet aux visiteurs de commenter vos shaares sur les permaliens avec Isso."
394
395#: plugins/isso/isso.php:64
396msgid "Isso server URL (without 'http://')"
397msgstr "URL du serveur Isso (sans 'http://')"
398
399#: plugins/markdown/markdown.php:150
400msgid "Description will be rendered with"
401msgstr "La description sera générée avec"
402
403#: plugins/markdown/markdown.php:151
404msgid "Markdown syntax documentation"
405msgstr "Documentation sur la syntaxe Markdown"
406
407#: plugins/markdown/markdown.php:152
408msgid "Markdown syntax"
409msgstr "la syntaxe Markdown"
410
411#: plugins/markdown/markdown.php:311
412msgid ""
413"Render shaare description with Markdown syntax.<br><strong>Warning</"
414"strong>:\n"
415"If your shaared descriptions contained HTML tags before enabling the "
416"markdown plugin,\n"
417"enabling it might break your page.\n"
418"See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
419"markdown#html-rendering\">README</a>."
420msgstr ""
421"Utilise la syntaxe Markdown pour la description des liens."
422"<br><strong>Attention</strong> :\n"
423"Si vous aviez des descriptions contenant du HTML avant d'activer cette "
424"extension,\n"
425"l'activer pourrait déformer vos pages.\n"
426"Voir le <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
427"markdown#html-rendering\">README</a>."
428
429#: plugins/piwik/piwik.php:21
430msgid ""
431"Piwik plugin error: Please define PIWIK_URL and PIWIK_SITEID in the plugin "
432"administration page."
433msgstr ""
434"Erreur de l'extension Piwik : Merci de définir les paramètres PIWIK_URL et "
435"PIWIK_SITEID dans la page d'administration des extensions."
436
437#: plugins/piwik/piwik.php:70
438msgid "A plugin that adds Piwik tracking code to Shaarli pages."
439msgstr "Ajoute le code de traçage de Piwik sur les pages de Shaarli."
440
441#: plugins/piwik/piwik.php:71
442msgid "Piwik URL"
443msgstr "URL de Piwik"
444
445#: plugins/piwik/piwik.php:72
446msgid "Piwik site ID"
447msgstr "Site ID de Piwik"
448
449#: plugins/playvideos/playvideos.php:22
450msgid "Video player"
451msgstr "Lecteur vidéo"
452
453#: plugins/playvideos/playvideos.php:25
454msgid "Play Videos"
455msgstr "Jouer les vidéos"
456
457#: plugins/playvideos/playvideos.php:56
458msgid "Add a button in the toolbar allowing to watch all videos."
459msgstr ""
460"Ajoute un bouton dans la barre de menu pour regarder toutes les vidéos."
461
462#: plugins/playvideos/youtube_playlist.js:214
463msgid "plugins/playvideos/jquery-1.11.2.min.js"
464msgstr ""
465
466#: plugins/pubsubhubbub/pubsubhubbub.php:69
467#, php-format
468msgid "Could not publish to PubSubHubbub: %s"
469msgstr "Impossible de publier vers PubSubHubbub : %s"
470
471#: plugins/pubsubhubbub/pubsubhubbub.php:95
472#, php-format
473msgid "Could not post to %s"
474msgstr "Impossible de publier vers %s"
475
476#: plugins/pubsubhubbub/pubsubhubbub.php:99
477#, php-format
478msgid "Bad response from the hub %s"
479msgstr "Mauvaise réponse du hub %s"
480
481#: plugins/pubsubhubbub/pubsubhubbub.php:110
482msgid "Enable PubSubHubbub feed publishing."
483msgstr "Active la publication de flux vers PubSubHubbub"
484
485#: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68
486msgid "For each link, add a QRCode icon."
487msgstr "Pour chaque liens, ajouter une icône de QRCode."
488
489#: plugins/wallabag/wallabag.php:21
490msgid ""
491"Wallabag plugin error: Please define the \"WALLABAG_URL\" setting in the "
492"plugin administration page."
493msgstr ""
494"Erreur de l'extension Wallabag : Merci de définir le paramètre « "
495"WALLABAG_URL » dans la page d'administration des extensions."
496
497#: plugins/wallabag/wallabag.php:47
498msgid "Save to wallabag"
499msgstr "Sauvegarder dans Wallabag"
500
501#: plugins/wallabag/wallabag.php:69
502msgid "Wallabag API URL"
503msgstr "URL de l'API Wallabag "
504
505#: plugins/wallabag/wallabag.php:70
506msgid "Wallabag API version (1 or 2)"
507msgstr "Version de l'API Wallabag (1 ou 2)"
508
509#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
510msgid "Shaare a new link"
511msgstr "Partager un nouveau lien"
512
513#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
514msgid "URL or leave empty to post a note"
515msgstr "URL ou laisser vide pour créer une note"
516
517#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
518#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
519msgid "Change password"
520msgstr "Modification du mot de passe"
521
522#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
523msgid "Current password"
524msgstr "Mot de passe actuel"
525
526#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
527msgid "New password"
528msgstr "Nouveau mot de passe\t"
529
530#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
531msgid "Change"
532msgstr "Changer"
533
534#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
535#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
536msgid "Manage tags"
537msgstr "Gérer les tags"
538
539#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
540#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
541msgid "Tag"
542msgstr "Tag"
543
544#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
545msgid "New name"
546msgstr "Nouveau nom"
547
548#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
549msgid "Case sensitive"
550msgstr "Sensible à la casse"
551
552#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
553msgid "Rename"
554msgstr "Renommer"
555
556#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
557#: tmp/editlink.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:60
558#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
559#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:288
560#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:313
561msgid "Delete"
562msgstr "Supprimer"
563
564#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
565msgid "Configure"
566msgstr "Configurer"
567
568#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
569msgid "title"
570msgstr "titre"
571
572#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
573msgid "Home link"
574msgstr "Lien vers l'accueil"
575
576#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
577msgid "Default value"
578msgstr "Valeur par défaut"
579
580#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
581msgid "Theme"
582msgstr "Thème"
583
584#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
585#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
586msgid "Timezone"
587msgstr "Fuseau horaire"
588
589#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
590msgid "Continent"
591msgstr "Continent"
592
593#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
594msgid "City"
595msgstr "Ville"
596
597#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
598msgid "Redirector"
599msgstr "Redirecteur"
600
601#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
602msgid "e. g."
603msgstr "ex :"
604
605#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
606msgid "will mask the HTTP_REFERER"
607msgstr "masque le HTTP_REFERER"
608
609#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
610msgid "Disable session cookie hijacking protection"
611msgstr "Désactiver la protection contre le détournement de cookies"
612
613#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
614msgid "Check this if you get disconnected or if your IP address changes often"
615msgstr ""
616"Cocher cette case si vous êtes souvent déconnecté ou si votre adresse IP "
617"change souvent"
618
619#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
620msgid "Private links by default"
621msgstr "Liens privés par défaut"
622
623#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
624msgid "All new links are private by default"
625msgstr "Tous les nouveaux liens sont privés par défaut"
626
627#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:185
628msgid "RSS direct links"
629msgstr "Liens directs dans le flux RSS"
630
631#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:186
632msgid "Check this to use direct URL instead of permalink in feeds"
633msgstr ""
634"Cocher cette case pour utiliser des liens directs au lieu des permaliens "
635"dans le flux RSS"
636
637#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:201
638msgid "Hide public links"
639msgstr "Cacher les liens publics"
640
641#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:202
642msgid "Do not show any links if the user is not logged in"
643msgstr "N'afficher aucun lien sans être connecté"
644
645#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:217
646#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95
647msgid "Check updates"
648msgstr "Vérifier les mises à jour"
649
650#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:218
651#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:97
652msgid "Notify me when a new release is ready"
653msgstr "Me notifier lorsqu'une nouvelle version est disponible"
654
655#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:233
656#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:114
657msgid "Enable REST API"
658msgstr "Activer l'API REST"
659
660#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:234
661#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:115
662msgid "Allow third party software to use Shaarli such as mobile application"
663msgstr ""
664"Permets aux applications tierces d'utiliser Shaarli, par exemple les "
665"applications mobiles"
666
667#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:249
668msgid "API secret"
669msgstr "Clé d'API secrète"
670
671#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:260
672#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
673#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
674#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:192
675msgid "Save"
676msgstr "Enregistrer"
677
678#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
679#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
680msgid "The Daily Shaarli"
681msgstr "Le Quotidien Shaarli"
682
683#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
684#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
685msgid "1 RSS entry per day"
686msgstr "1 entrée RSS par jour"
687
688#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
689#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
690msgid "Previous day"
691msgstr "Jour précédent"
692
693#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
694#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
695msgid "All links of one day in a single page."
696msgstr "Tous les liens d'un jour sur une page."
697
698#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
699#: tmp/paper.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
700msgid "Next day"
701msgstr "Jour suivant"
702
703#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
704#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26
705msgid "Shaare"
706msgstr "Shaare"
707
708#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
709msgid "URL"
710msgstr "URL"
711
712#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:27
713msgid "Title"
714msgstr "Titre"
715
716#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
717#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
718#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
719#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
720#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124
721msgid "Description"
722msgstr "Description"
723
724#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
725msgid "Tags"
726msgstr "Tags"
727
728#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
729#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
730#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:177
731msgid "Private"
732msgstr "Privé"
733
734#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
735msgid "Export Database"
736msgstr "Exporter les données"
737
738#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
739msgid "Selection"
740msgstr "Choisir"
741
742#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
743msgid "All"
744msgstr "Tous"
745
746#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
747msgid "Public"
748msgstr "Publics"
749
750#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
751msgid "Prepend note permalinks with this Shaarli instance's URL"
752msgstr "Préfixer les liens de notes avec l'URL de l'instance de Shaarli"
753
754#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
755msgid "Useful to import bookmarks in a web browser"
756msgstr "Utile pour importer les marques-pages dans un navigateur"
757
758#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
759msgid "Export"
760msgstr "Exporter"
761
762#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
763msgid "Import Database"
764msgstr "Importer des données"
765
766#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
767msgid "Maximum size allowed:"
768msgstr "Taille maximum autorisée :"
769
770#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
771msgid "Visibility"
772msgstr "Visibilité"
773
774#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
775msgid "Use values from the imported file, default to public"
776msgstr ""
777"Utiliser les valeurs présentes dans le fichier d'import, public par défaut"
778
779#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
780msgid "Import all bookmarks as private"
781msgstr "Importer tous les liens comme privés"
782
783#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
784msgid "Import all bookmarks as public"
785msgstr "Importer tous les liens comme publics"
786
787#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
788msgid "Overwrite existing bookmarks"
789msgstr "Remplacer les liens existants"
790
791#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
792msgid "Duplicates based on URL"
793msgstr "Les doublons s'appuient sur les URL"
794
795#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
796msgid "Add default tags"
797msgstr "Ajouter des tags par défaut"
798
799#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
800msgid "Import"
801msgstr "Importer"
802
803#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
804msgid "Install Shaarli"
805msgstr "Installation de Shaarli"
806
807#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
808msgid "It looks like it's the first time you run Shaarli. Please configure it."
809msgstr ""
810"Il semblerait que ça soit la première fois que vous lancez Shaarli. Merci de "
811"le configurer."
812
813#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
814#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
815#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:149
816msgid "Username"
817msgstr "Nom d'utilisateur"
818
819#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
820#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
821#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:150
822msgid "Password"
823msgstr "Mot de passe"
824
825#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80
826msgid "Shaarli title"
827msgstr "Titre du Shaarli"
828
829#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
830msgid "My links"
831msgstr "Mes liens"
832
833#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
834msgid "Install"
835msgstr "Installer"
836
837#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
838#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
839msgid "shaare"
840msgid_plural "shaares"
841msgstr[0] "shaare"
842msgstr[1] "shaares"
843
844#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
845#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
846msgid "private link"
847msgid_plural "private links"
848msgstr[0] "lien privé"
849msgstr[1] "liens privés"
850
851#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
852#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:119
853msgid "Search text"
854msgstr "Recherche texte"
855
856#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
857#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:131
858msgid "Filter by tag"
859msgstr "Filtrer par tag"
860
861#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118
862msgid "Nothing found."
863msgstr "Aucun résultat."
864
865#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:126
866#, php-format
867msgid "%s result"
868msgid_plural "%s results"
869msgstr[0] "%s résultat"
870msgstr[1] "%s résultats"
871
872#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130
873msgid "for"
874msgstr "pour"
875
876#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:137
877msgid "tagged"
878msgstr "taggé"
879
880#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:141
881msgid "Remove tag"
882msgstr "Retirer le tag"
883
884#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
885msgid "with status"
886msgstr "avec le statut"
887
888#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:181
889msgid "Edit"
890msgstr "Modifier"
891
892#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
893msgid "Fold"
894msgstr "Replier"
895
896#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:245
897msgid "Edited: "
898msgstr "Modifié :"
899
900#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:257
901msgid "permalink"
902msgstr "permalien"
903
904#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7
905msgid "Filters"
906msgstr "Filtres"
907
908#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12
909msgid "Filter private links"
910msgstr "Filtrer par liens privés"
911
912#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:63
913msgid "Links per page"
914msgstr "Liens par page"
915
916#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
917msgid ""
918"You have been banned after too many failed login attempts. Try again later."
919msgstr ""
920"Vous avez été banni après trop d'échec d'authentification. Merci de "
921"réessayer plus tard."
922
923#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
924#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
925#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:71
926#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:95
927msgid "Login"
928msgstr "Connexion"
929
930#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
931#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:153
932msgid "Remember me"
933msgstr "Rester connecté"
934
935#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
936msgid "by the Shaarli community"
937msgstr "par la communauté Shaarli"
938
939#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15
940msgid "Documentation"
941msgstr "Documentation"
942
943#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
944msgid "Tools"
945msgstr "Outils"
946
947#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
948#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
949#: tmp/tagcloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
950msgid "Tag cloud"
951msgstr "Nuage de tags"
952
953#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:39
954msgid "Picture wall"
955msgstr "Mur d'images"
956
957#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:42
958msgid "Daily"
959msgstr "Quotidien"
960
961#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:61
962#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:86
963msgid "RSS Feed"
964msgstr "Flux RSS"
965
966#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:66
967#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:102
968msgid "Logout"
969msgstr "Déconnexion"
970
971#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:81
972msgid "Search"
973msgstr "Rechercher"
974
975#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:171
976msgid "is available"
977msgstr "est disponible"
978
979#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:178
980msgid "Error"
981msgstr "Erreur"
982
983#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
984msgid "Picture Wall"
985msgstr "Mur d'images"
986
987#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
988msgid "pics"
989msgstr "images"
990
991#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
992msgid "You need to enable Javascript to change plugin loading order."
993msgstr ""
994"Vous devez activer Javascript pour pouvoir modifier l'ordre des extensions."
995
996#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
997#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
998msgid "Plugin administration"
999msgstr "Administration des extensions"
1000
1001#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
1002msgid "Enabled Plugins"
1003msgstr "Extensions activées"
1004
1005#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
1006#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155
1007msgid "No plugin enabled."
1008msgstr "Aucune extension activée."
1009
1010#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40
1011#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:73
1012msgid "Disable"
1013msgstr "Désactiver"
1014
1015#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1016#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
1017#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:98
1018#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
1019msgid "Name"
1020msgstr "Nom"
1021
1022#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
1023#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
1024msgid "Order"
1025msgstr "Ordre"
1026
1027#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
1028msgid "Disabled Plugins"
1029msgstr "Extensions désactivées"
1030
1031#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
1032msgid "No plugin disabled."
1033msgstr "Aucune extension désactivée."
1034
1035#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:97
1036#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
1037msgid "Enable"
1038msgstr "Activer"
1039
1040#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
1041msgid "More plugins available"
1042msgstr "Plus d'extensions disponibles"
1043
1044#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
1045msgid "in the documentation"
1046msgstr "dans la documentation"
1047
1048#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
1049msgid "Plugin configuration"
1050msgstr "Configuration des extensions"
1051
1052#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1053#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1054#: tmp/tagcloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1055msgid "tags"
1056msgstr "tags"
1057
1058#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1059msgid "Tag list"
1060msgstr "List des tags"
1061
1062#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3
1063msgid "Sort by:"
1064msgstr "Trier par :"
1065
1066#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5
1067msgid "Cloud"
1068msgstr "Nuage"
1069
1070#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6
1071msgid "Most used"
1072msgstr "Plus utilisés"
1073
1074#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7
1075msgid "Alphabetical"
1076msgstr "Alphabétique"
1077
1078#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1079msgid "Settings"
1080msgstr "Paramètres"
1081
1082#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1083msgid "Change Shaarli settings: title, timezone, etc."
1084msgstr "Changer les paramètres de Shaarli : titre, fuseau horaire, etc."
1085
1086#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
1087msgid "Configure your Shaarli"
1088msgstr "Conguration de Shaarli"
1089
1090#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1091msgid "Enable, disable and configure plugins"
1092msgstr "Activer, désactiver et configurer les extensions"
1093
1094#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
1095msgid "Change your password"
1096msgstr "Modification du mot de passe"
1097
1098#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
1099msgid "Rename or delete a tag in all links"
1100msgstr "Rename or delete a tag in all links"
1101
1102#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1103msgid ""
1104"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
1105"delicious...)"
1106msgstr ""
1107"Importer des marques pages au format Netscape HTML (comme exportés depuis "
1108"Firefox, Chrome, Opera, delicious...)"
1109
1110#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
1111msgid "Import links"
1112msgstr "Importer des liens"
1113
1114#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
1115msgid ""
1116"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
1117"Opera, delicious...)"
1118msgstr ""
1119"Exporter les marques pages au format Netscape HTML (comme exportés depuis "
1120"Firefox, Chrome, Opera, delicious...)"
1121
1122#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1123msgid "Export database"
1124msgstr "Exporter les données"
1125
1126#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
1127msgid ""
1128"Drag one of these button to your bookmarks toolbar or right-click it and "
1129"\"Bookmark This Link\""
1130msgstr ""
1131"Glisser un de ces bouttons dans votre barre de favoris ou cliquer droit "
1132"dessus et « Ajouter aux favoris »"
1133
1134#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
1135msgid "then click on the bookmarklet in any page you want to share."
1136msgstr ""
1137"puis cliquer sur le marque page depuis un site que vous souhaitez partager."
1138
1139#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
1140#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95
1141msgid ""
1142"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
1143"Link"
1144msgstr ""
1145"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « "
1146"Ajouter aux favoris »"
1147
1148#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
1149msgid "then click ✚Shaare link button in any page you want to share"
1150msgstr "puis cliquer sur ✚Shaare depuis un site que vous souhaitez partager"
1151
1152#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
1153msgid "Shaare link"
1154msgstr "Shaare"
1155
1156#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96
1157msgid ""
1158"Then click ✚Add Note button anytime to start composing a private Note (text "
1159"post) to your Shaarli"
1160msgstr ""
1161"Puis cliquer sur ✚Add Note pour commencer à rédiger une Note sur Shaarli"
1162
1163#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
1164msgid "Add Note"
1165msgstr "Ajouter une Note"
1166
1167#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111
1168msgid ""
1169"You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
1170"functionality."
1171msgstr ""
1172"Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
1173"fonctionalité."
1174
1175#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116
1176msgid "Add to"
1177msgstr "Ajouter à"
1178
1179#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
1180msgid "3rd party"
1181msgstr "Applications tierces"
1182
1183#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129
1184#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:135
1185msgid "Plugin"
1186msgstr "Extension"
1187
1188#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130
1189#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
1190msgid "plugin"
1191msgstr "extension"
1192
1193#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:157
1194msgid ""
1195"Drag this link to your bookmarks toolbar, or right-click it and choose "
1196"Bookmark This Link"
1197msgstr ""
1198"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « "
1199"Ajouter aux favoris »"
1200
1201#~ msgid "Sessions do not seem to work correctly on your server."
1202#~ msgstr "Les sessions ne semblent "
1203
1204#~ msgid "Tag was renamed in "
1205#~ msgstr "Le tag a été renommé dans "
1206
1207#, fuzzy
1208#~| msgid "My links"
1209#~ msgid " links"
1210#~ msgstr "Mes liens"
1211
1212#, fuzzy
1213#~| msgid ""
1214#~| "Error: missing Composer configuration\n"
1215#~| "\n"
1216#~ msgid "Error: missing Composer configuration"
1217#~ msgstr ""
1218#~ "Erreur : la configuration Composer est manquante\n"
1219#~ "\n"
1220
1221#, fuzzy
1222#~| msgid ""
1223#~| "Shaarli could not create the config file. Please make sure Shaarli has "
1224#~| "the right to write in the folder is it installed in."
1225#~ msgid ""
1226#~ "Shaarli could not create the config file. \n"
1227#~ " Please make sure Shaarli has the right to write in the "
1228#~ "folder is it installed in."
1229#~ msgstr ""
1230#~ "Shaarli n'a pas pu créer le fichier de configuration. Merci de vérifier "
1231#~ "que Shaarli a les droits d'écriture dans le dossier dans lequel il est "
1232#~ "installé."
1233
1234#, fuzzy
1235#~| msgid "Plugin"
1236#~ msgid "Plugin \""
1237#~ msgstr "Extension"
1238
1239#~ msgid "Your PHP version is obsolete!"
1240#~ msgstr "Votre version de PHP est obsolète !"
1241
1242#~ msgid " Shaarli requires at least PHP "
1243#~ msgstr "Shaarli nécessite au moins PHP"
diff --git a/index.php b/index.php
index 4068a828..98171d78 100644
--- a/index.php
+++ b/index.php
@@ -64,7 +64,6 @@ require_once 'application/FeedBuilder.php';
64require_once 'application/FileUtils.php'; 64require_once 'application/FileUtils.php';
65require_once 'application/History.php'; 65require_once 'application/History.php';
66require_once 'application/HttpUtils.php'; 66require_once 'application/HttpUtils.php';
67require_once 'application/Languages.php';
68require_once 'application/LinkDB.php'; 67require_once 'application/LinkDB.php';
69require_once 'application/LinkFilter.php'; 68require_once 'application/LinkFilter.php';
70require_once 'application/LinkUtils.php'; 69require_once 'application/LinkUtils.php';
@@ -76,6 +75,7 @@ require_once 'application/Utils.php';
76require_once 'application/PluginManager.php'; 75require_once 'application/PluginManager.php';
77require_once 'application/Router.php'; 76require_once 'application/Router.php';
78require_once 'application/Updater.php'; 77require_once 'application/Updater.php';
78use \Shaarli\Languages;
79use \Shaarli\ThemeUtils; 79use \Shaarli\ThemeUtils;
80use \Shaarli\Config\ConfigManager; 80use \Shaarli\Config\ConfigManager;
81 81
@@ -121,8 +121,16 @@ if (isset($_COOKIE['shaarli']) && !is_session_id_valid($_COOKIE['shaarli'])) {
121} 121}
122 122
123$conf = new ConfigManager(); 123$conf = new ConfigManager();
124
125// Sniff browser language and set date format accordingly.
126if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
127 autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
128}
129
130new Languages(setlocale(LC_MESSAGES, 0), $conf);
131
124$conf->setEmpty('general.timezone', date_default_timezone_get()); 132$conf->setEmpty('general.timezone', date_default_timezone_get());
125$conf->setEmpty('general.title', 'Shared links on '. escape(index_url($_SERVER))); 133$conf->setEmpty('general.title', t('Shared links on '). escape(index_url($_SERVER)));
126RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory 134RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory
127RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory 135RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory
128 136
@@ -144,7 +152,7 @@ if (! is_file($conf->getConfigFileExt())) {
144 $errors = ApplicationUtils::checkResourcePermissions($conf); 152 $errors = ApplicationUtils::checkResourcePermissions($conf);
145 153
146 if ($errors != array()) { 154 if ($errors != array()) {
147 $message = '<p>Insufficient permissions:</p><ul>'; 155 $message = '<p>'. t('Insufficient permissions:') .'</p><ul>';
148 156
149 foreach ($errors as $error) { 157 foreach ($errors as $error) {
150 $message .= '<li>'.$error.'</li>'; 158 $message .= '<li>'.$error.'</li>';
@@ -163,11 +171,6 @@ if (! is_file($conf->getConfigFileExt())) {
163// a token depending of deployment salt, user password, and the current ip 171// a token depending of deployment salt, user password, and the current ip
164define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); 172define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt')));
165 173
166// Sniff browser language and set date format accordingly.
167if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
168 autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
169}
170
171/** 174/**
172 * Checking session state (i.e. is the user still logged in) 175 * Checking session state (i.e. is the user still logged in)
173 * 176 *
@@ -376,7 +379,7 @@ function ban_canLogin($conf)
376// Process login form: Check if login/password is correct. 379// Process login form: Check if login/password is correct.
377if (isset($_POST['login'])) 380if (isset($_POST['login']))
378{ 381{
379 if (!ban_canLogin($conf)) die('I said: NO. You are banned for the moment. Go away.'); 382 if (!ban_canLogin($conf)) die(t('I said: NO. You are banned for the moment. Go away.'));
380 if (isset($_POST['password']) 383 if (isset($_POST['password'])
381 && tokenOk($_POST['token']) 384 && tokenOk($_POST['token'])
382 && (check_auth($_POST['login'], $_POST['password'], $conf)) 385 && (check_auth($_POST['login'], $_POST['password'], $conf))
@@ -440,7 +443,8 @@ if (isset($_POST['login']))
440 } 443 }
441 } 444 }
442 } 445 }
443 echo '<script>alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. 446 // Redirect to login screen.
447 echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'?do=login'.$redir.'\';</script>';
444 exit; 448 exit;
445 } 449 }
446} 450}
@@ -1100,16 +1104,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1100 if ($targetPage == Router::$PAGE_CHANGEPASSWORD) 1104 if ($targetPage == Router::$PAGE_CHANGEPASSWORD)
1101 { 1105 {
1102 if ($conf->get('security.open_shaarli')) { 1106 if ($conf->get('security.open_shaarli')) {
1103 die('You are not supposed to change a password on an Open Shaarli.'); 1107 die(t('You are not supposed to change a password on an Open Shaarli.'));
1104 } 1108 }
1105 1109
1106 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) 1110 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
1107 { 1111 {
1108 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! 1112 if (!tokenOk($_POST['token'])) die(t('Wrong token.')); // Go away!
1109 1113
1110 // Make sure old password is correct. 1114 // Make sure old password is correct.
1111 $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')); 1115 $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt'));
1112 if ($oldhash!= $conf->get('credentials.hash')) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; } 1116 if ($oldhash!= $conf->get('credentials.hash')) {
1117 echo '<script>alert("'. t('The old password is not correct.') .'");document.location=\'?do=changepasswd\';</script>';
1118 exit;
1119 }
1113 // Save new password 1120 // Save new password
1114 // Salt renders rainbow-tables attacks useless. 1121 // Salt renders rainbow-tables attacks useless.
1115 $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); 1122 $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
@@ -1127,7 +1134,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1127 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>'; 1134 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>';
1128 exit; 1135 exit;
1129 } 1136 }
1130 echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>'; 1137 echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>';
1131 exit; 1138 exit;
1132 } 1139 }
1133 else // show the change password form. 1140 else // show the change password form.
@@ -1143,7 +1150,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1143 if (!empty($_POST['title']) ) 1150 if (!empty($_POST['title']) )
1144 { 1151 {
1145 if (!tokenOk($_POST['token'])) { 1152 if (!tokenOk($_POST['token'])) {
1146 die('Wrong token.'); // Go away! 1153 die(t('Wrong token.')); // Go away!
1147 } 1154 }
1148 $tz = 'UTC'; 1155 $tz = 'UTC';
1149 if (!empty($_POST['continent']) && !empty($_POST['city']) 1156 if (!empty($_POST['continent']) && !empty($_POST['city'])
@@ -1178,7 +1185,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1178 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>'; 1185 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>';
1179 exit; 1186 exit;
1180 } 1187 }
1181 echo '<script>alert("Configuration was saved.");document.location=\'?do=configure\';</script>'; 1188 echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>';
1182 exit; 1189 exit;
1183 } 1190 }
1184 else // Show the configuration form. 1191 else // Show the configuration form.
@@ -1215,7 +1222,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1215 } 1222 }
1216 1223
1217 if (!tokenOk($_POST['token'])) { 1224 if (!tokenOk($_POST['token'])) {
1218 die('Wrong token.'); 1225 die(t('Wrong token.'));
1219 } 1226 }
1220 1227
1221 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag'])); 1228 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag']));
@@ -1244,7 +1251,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1244 { 1251 {
1245 // Go away! 1252 // Go away!
1246 if (! tokenOk($_POST['token'])) { 1253 if (! tokenOk($_POST['token'])) {
1247 die('Wrong token.'); 1254 die(t('Wrong token.'));
1248 } 1255 }
1249 1256
1250 // lf_id should only be present if the link exists. 1257 // lf_id should only be present if the link exists.
@@ -1344,7 +1351,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1344 if ($targetPage == Router::$PAGE_DELETELINK) 1351 if ($targetPage == Router::$PAGE_DELETELINK)
1345 { 1352 {
1346 if (! tokenOk($_GET['token'])) { 1353 if (! tokenOk($_GET['token'])) {
1347 die('Wrong token.'); 1354 die(t('Wrong token.'));
1348 } 1355 }
1349 1356
1350 $ids = trim($_GET['lf_linkdate']); 1357 $ids = trim($_GET['lf_linkdate']);
@@ -1550,11 +1557,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1550 // Import bookmarks from an uploaded file 1557 // Import bookmarks from an uploaded file
1551 if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) { 1558 if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
1552 // The file is too big or some form field may be missing. 1559 // The file is too big or some form field may be missing.
1553 echo '<script>alert("The file you are trying to upload is probably' 1560 $msg = sprintf(
1554 .' bigger than what this webserver can accept (' 1561 t(
1555 .get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')).').' 1562 'The file you are trying to upload is probably bigger than what this webserver can accept'
1556 .' Please upload in smaller chunks.");document.location=\'?do=' 1563 .' (%s). Please upload in smaller chunks.'
1557 .Router::$PAGE_IMPORT .'\';</script>'; 1564 ),
1565 get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize'))
1566 );
1567 echo '<script>alert("'. $msg .'");document.location=\'?do='.Router::$PAGE_IMPORT .'\';</script>';
1558 exit; 1568 exit;
1559 } 1569 }
1560 if (! tokenOk($_POST['token'])) { 1570 if (! tokenOk($_POST['token'])) {
@@ -1962,12 +1972,20 @@ function install($conf)
1962 // (Because on some hosts, session.save_path may not be set correctly, 1972 // (Because on some hosts, session.save_path may not be set correctly,
1963 // or we may not have write access to it.) 1973 // or we may not have write access to it.)
1964 if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) 1974 if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working'))
1965 { // Step 2: Check if data in session is correct. 1975 {
1966 echo '<pre>Sessions do not seem to work correctly on your server.<br>'; 1976 // Step 2: Check if data in session is correct.
1967 echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; 1977 $msg = t(
1968 echo 'It currently points to '.session_save_path().'<br>'; 1978 '<pre>Sessions do not seem to work correctly on your server.<br>'.
1969 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>'; 1979 'Make sure the variable "session.save_path" is set correctly in your PHP config, '.
1970 echo '<br><a href="?">Click to try again.</a></pre>'; 1980 'and that you have write access to it.<br>'.
1981 'It currently points to %s.<br>'.
1982 'On some browsers, accessing your server via a hostname like \'localhost\' '.
1983 'or any custom hostname without a dot causes cookie storage to fail. '.
1984 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'
1985 );
1986 $msg = sprintf($msg, session_save_path());
1987 echo $msg;
1988 echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>';
1971 die; 1989 die;
1972 } 1990 }
1973 if (!isset($_SESSION['session_tested'])) 1991 if (!isset($_SESSION['session_tested']))
diff --git a/plugins/TODO.md b/plugins/TODO.md
deleted file mode 100644
index e3313d67..00000000
--- a/plugins/TODO.md
+++ /dev/null
@@ -1,28 +0,0 @@
1https://github.com/shaarli/Shaarli/issues/181 - Add Disqus or Isso comments box on a permalink page
2
3 * http://posativ.org/isso/
4 * install debian package https://packages.debian.org/sid/isso
5 * configure server http://posativ.org/isso/docs/configuration/server/
6 * configure client http://posativ.org/isso/docs/configuration/client/
7 * 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)
8
9
10Problem: by default, Isso thread ID is guessed from the current url (only one thread per page).
11if we want multiple threads on a single page (shaarli linklist), we must use : the `data-isso-id` client config,
12with data-isso-id being the permalink of an item.
13
14`<section data-isso-id="aH7klxW" id="isso-thread"></section>`
15`data-isso-id: Set a custom thread id, defaults to current URI.`
16
17Problem: feature is currently broken https://github.com/posativ/isso/issues/27
18
19Another option, only display isso threads when current URL is a permalink (`\?(A-Z|a-z|0-9|-){7}`) (only show thread
20when displaying only this link), and just display a "comments" button on each linklist item. Optionally show the comment
21count on each item using the API (http://posativ.org/isso/docs/extras/api/#get-comment-count). API requests can be done
22by raintpl `{function` or client-side with js. The former should be faster if isso and shaarli are on ther same server.
23
24Showing all full isso threads in the linklist would destroy layout
25
26-----------------------------------------------------------
27
28http://www.git-attitude.fr/2014/11/04/git-rerere/ for the merge
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php
index ddf50aaf..8c05a231 100644
--- a/plugins/addlink_toolbar/addlink_toolbar.php
+++ b/plugins/addlink_toolbar/addlink_toolbar.php
@@ -26,11 +26,11 @@ function hook_addlink_toolbar_render_header($data)
26 array( 26 array(
27 'type' => 'text', 27 'type' => 'text',
28 'name' => 'post', 28 'name' => 'post',
29 'placeholder' => 'URI', 29 'placeholder' => t('URI'),
30 ), 30 ),
31 array( 31 array(
32 'type' => 'submit', 32 'type' => 'submit',
33 'value' => 'Add link', 33 'value' => t('Add link'),
34 'class' => 'bigbutton', 34 'class' => 'bigbutton',
35 ), 35 ),
36 ), 36 ),
@@ -40,3 +40,12 @@ function hook_addlink_toolbar_render_header($data)
40 40
41 return $data; 41 return $data;
42} 42}
43
44/**
45 * This function is never called, but contains translation calls for GNU gettext extraction.
46 */
47function addlink_toolbar_dummy_translation()
48{
49 // meta
50 t('Adds the addlink input on the linklist page.');
51}
diff --git a/plugins/archiveorg/archiveorg.html b/plugins/archiveorg/archiveorg.html
index 0781fe35..ad501f47 100644
--- a/plugins/archiveorg/archiveorg.html
+++ b/plugins/archiveorg/archiveorg.html
@@ -1 +1,5 @@
1<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> 1<span>
2 <a href="https://web.archive.org/web/%s">
3 <img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
4 </a>
5</span>
diff --git a/plugins/archiveorg/archiveorg.php b/plugins/archiveorg/archiveorg.php
index 03d13d0e..cda35751 100644
--- a/plugins/archiveorg/archiveorg.php
+++ b/plugins/archiveorg/archiveorg.php
@@ -20,9 +20,18 @@ function hook_archiveorg_render_linklist($data)
20 if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) { 20 if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
21 continue; 21 continue;
22 } 22 }
23 $archive = sprintf($archive_html, $value['url']); 23 $archive = sprintf($archive_html, $value['url'], t('View on archive.org'));
24 $value['link_plugin'][] = $archive; 24 $value['link_plugin'][] = $archive;
25 } 25 }
26 26
27 return $data; 27 return $data;
28} 28}
29
30/**
31 * This function is never called, but contains translation calls for GNU gettext extraction.
32 */
33function archiveorg_dummy_translation()
34{
35 // meta
36 t('For each link, add an Archive.org icon.');
37}
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php
index 8fdbf663..3a90ae6a 100644
--- a/plugins/demo_plugin/demo_plugin.php
+++ b/plugins/demo_plugin/demo_plugin.php
@@ -433,3 +433,12 @@ function hook_demo_plugin_render_feed($data)
433 } 433 }
434 return $data; 434 return $data;
435} 435}
436
437/**
438 * This function is never called, but contains translation calls for GNU gettext extraction.
439 */
440function demo_dummy_translation()
441{
442 // meta
443 t('A demo plugin covering all use cases for template designers and plugin developers.');
444}
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php
index ce16645f..5bc1cce2 100644
--- a/plugins/isso/isso.php
+++ b/plugins/isso/isso.php
@@ -4,10 +4,11 @@
4 * Plugin Isso. 4 * Plugin Isso.
5 */ 5 */
6 6
7use Shaarli\Config\ConfigManager;
8
7/** 9/**
8 * Display an error everywhere if the plugin is enabled without configuration. 10 * Display an error everywhere if the plugin is enabled without configuration.
9 * 11 *
10 * @param $data array List of links
11 * @param $conf ConfigManager instance 12 * @param $conf ConfigManager instance
12 * 13 *
13 * @return mixed - linklist data with Isso plugin. 14 * @return mixed - linklist data with Isso plugin.
@@ -16,8 +17,8 @@ function isso_init($conf)
16{ 17{
17 $issoUrl = $conf->get('plugins.ISSO_SERVER'); 18 $issoUrl = $conf->get('plugins.ISSO_SERVER');
18 if (empty($issoUrl)) { 19 if (empty($issoUrl)) {
19 $error = 'Isso plugin error: '. 20 $error = t('Isso plugin error: '.
20 'Please define the "ISSO_SERVER" setting in the plugin administration page.'; 21 'Please define the "ISSO_SERVER" setting in the plugin administration page.');
21 return array($error); 22 return array($error);
22 } 23 }
23} 24}
@@ -52,3 +53,13 @@ function hook_isso_render_linklist($data, $conf)
52 53
53 return $data; 54 return $data;
54} 55}
56
57/**
58 * This function is never called, but contains translation calls for GNU gettext extraction.
59 */
60function isso_dummy_translation()
61{
62 // meta
63 t('Let visitor comment your shaares on permalinks with Isso.');
64 t('Isso server URL (without \'http://\')');
65}
diff --git a/plugins/markdown/help.html b/plugins/markdown/help.html
index 9c4e5ae0..ded3d347 100644
--- a/plugins/markdown/help.html
+++ b/plugins/markdown/help.html
@@ -1,5 +1,5 @@
1<div class="md_help"> 1<div class="md_help">
2 Description will be rendered with 2 %s
3 <a href="http://daringfireball.net/projects/markdown/syntax" title="Markdown syntax documentation"> 3 <a href="http://daringfireball.net/projects/markdown/syntax" title="%s">
4 Markdown syntax</a>. 4 %s</a>.
5</div> 5</div>
diff --git a/plugins/markdown/markdown.php b/plugins/markdown/markdown.php
index 772c56e8..1531549d 100644
--- a/plugins/markdown/markdown.php
+++ b/plugins/markdown/markdown.php
@@ -154,8 +154,13 @@ function hook_markdown_render_includes($data)
154function hook_markdown_render_editlink($data) 154function hook_markdown_render_editlink($data)
155{ 155{
156 // Load help HTML into a string 156 // Load help HTML into a string
157 $data['edit_link_plugin'][] = file_get_contents(PluginManager::$PLUGINS_PATH .'/markdown/help.html'); 157 $txt = file_get_contents(PluginManager::$PLUGINS_PATH .'/markdown/help.html');
158 158 $translations = [
159 t('Description will be rendered with'),
160 t('Markdown syntax documentation'),
161 t('Markdown syntax'),
162 ];
163 $data['edit_link_plugin'][] = vsprintf($txt, $translations);
159 // Add no markdown 'meta-tag' in tag list if it was never used, for autocompletion. 164 // Add no markdown 'meta-tag' in tag list if it was never used, for autocompletion.
160 if (! in_array(NO_MD_TAG, $data['tags'])) { 165 if (! in_array(NO_MD_TAG, $data['tags'])) {
161 $data['tags'][NO_MD_TAG] = 0; 166 $data['tags'][NO_MD_TAG] = 0;
@@ -325,3 +330,15 @@ function process_markdown($description, $escape = true, $allowedProtocols = [])
325 330
326 return $processedDescription; 331 return $processedDescription;
327} 332}
333
334/**
335 * This function is never called, but contains translation calls for GNU gettext extraction.
336 */
337function markdown_dummy_translation()
338{
339 // meta
340 t('Render shaare description with Markdown syntax.<br><strong>Warning</strong>:
341If your shaared descriptions contained HTML tags before enabling the markdown plugin,
342enabling it might break your page.
343See the <a href="https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering">README</a>.');
344}
diff --git a/plugins/piwik/piwik.php b/plugins/piwik/piwik.php
index 4a2b48a1..ca00c2be 100644
--- a/plugins/piwik/piwik.php
+++ b/plugins/piwik/piwik.php
@@ -18,8 +18,8 @@ function piwik_init($conf)
18 $piwikUrl = $conf->get('plugins.PIWIK_URL'); 18 $piwikUrl = $conf->get('plugins.PIWIK_URL');
19 $piwikSiteid = $conf->get('plugins.PIWIK_SITEID'); 19 $piwikSiteid = $conf->get('plugins.PIWIK_SITEID');
20 if (empty($piwikUrl) || empty($piwikSiteid)) { 20 if (empty($piwikUrl) || empty($piwikSiteid)) {
21 $error = 'Piwik plugin error: ' . 21 $error = t('Piwik plugin error: ' .
22 'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.'; 22 'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.');
23 return array($error); 23 return array($error);
24 } 24 }
25} 25}
@@ -60,3 +60,14 @@ function hook_piwik_render_footer($data, $conf)
60 60
61 return $data; 61 return $data;
62} 62}
63
64/**
65 * This function is never called, but contains translation calls for GNU gettext extraction.
66 */
67function piwik_dummy_translation()
68{
69 // meta
70 t('A plugin that adds Piwik tracking code to Shaarli pages.');
71 t('Piwik URL');
72 t('Piwik site ID');
73}
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php
index 64484504..c6d6b0cc 100644
--- a/plugins/playvideos/playvideos.php
+++ b/plugins/playvideos/playvideos.php
@@ -19,10 +19,10 @@ function hook_playvideos_render_header($data)
19 $playvideo = array( 19 $playvideo = array(
20 'attr' => array( 20 'attr' => array(
21 'href' => '#', 21 'href' => '#',
22 'title' => 'Video player', 22 'title' => t('Video player'),
23 'id' => 'playvideos', 23 'id' => 'playvideos',
24 ), 24 ),
25 'html' => 'â–º Play Videos' 25 'html' => 'â–º '. t('Play Videos')
26 ); 26 );
27 $data['buttons_toolbar'][] = $playvideo; 27 $data['buttons_toolbar'][] = $playvideo;
28 } 28 }
@@ -46,3 +46,12 @@ function hook_playvideos_render_footer($data)
46 46
47 return $data; 47 return $data;
48} 48}
49
50/**
51 * This function is never called, but contains translation calls for GNU gettext extraction.
52 */
53function playvideos_dummy_translation()
54{
55 // meta
56 t('Add a button in the toolbar allowing to watch all videos.');
57}
diff --git a/plugins/pubsubhubbub/pubsubhubbub.php b/plugins/pubsubhubbub/pubsubhubbub.php
index 03b6757b..184b588b 100644
--- a/plugins/pubsubhubbub/pubsubhubbub.php
+++ b/plugins/pubsubhubbub/pubsubhubbub.php
@@ -10,6 +10,7 @@
10 */ 10 */
11 11
12use pubsubhubbub\publisher\Publisher; 12use pubsubhubbub\publisher\Publisher;
13use Shaarli\Config\ConfigManager;
13 14
14/** 15/**
15 * Plugin init function - set the hub to the default appspot one. 16 * Plugin init function - set the hub to the default appspot one.
@@ -65,7 +66,7 @@ function hook_pubsubhubbub_save_link($data, $conf)
65 $p = new Publisher($conf->get('plugins.PUBSUBHUB_URL')); 66 $p = new Publisher($conf->get('plugins.PUBSUBHUB_URL'));
66 $p->publish_update($feeds, $httpPost); 67 $p->publish_update($feeds, $httpPost);
67 } catch (Exception $e) { 68 } catch (Exception $e) {
68 error_log('Could not publish to PubSubHubbub: ' . $e->getMessage()); 69 error_log(sprintf(t('Could not publish to PubSubHubbub: %s'), $e->getMessage()));
69 } 70 }
70 71
71 return $data; 72 return $data;
@@ -91,11 +92,20 @@ function nocurl_http_post($url, $postString) {
91 $context = stream_context_create($params); 92 $context = stream_context_create($params);
92 $fp = @fopen($url, 'rb', false, $context); 93 $fp = @fopen($url, 'rb', false, $context);
93 if (!$fp) { 94 if (!$fp) {
94 throw new Exception('Could not post to '. $url); 95 throw new Exception(sprintf(t('Could not post to %s'), $url));
95 } 96 }
96 $response = @stream_get_contents($fp); 97 $response = @stream_get_contents($fp);
97 if ($response === false) { 98 if ($response === false) {
98 throw new Exception('Bad response from the hub '. $url); 99 throw new Exception(sprintf(t('Bad response from the hub %s'), $url));
99 } 100 }
100 return $response; 101 return $response;
101} 102}
103
104/**
105 * This function is never called, but contains translation calls for GNU gettext extraction.
106 */
107function pubsubhubbub_dummy_translation()
108{
109 // meta
110 t('Enable PubSubHubbub feed publishing.');
111}
diff --git a/plugins/qrcode/qrcode.meta b/plugins/qrcode/qrcode.meta
index cbf371ea..1812cd21 100644
--- a/plugins/qrcode/qrcode.meta
+++ b/plugins/qrcode/qrcode.meta
@@ -1 +1 @@
description="For each link, add a QRCode icon ." description="For each link, add a QRCode icon."
diff --git a/plugins/qrcode/qrcode.php b/plugins/qrcode/qrcode.php
index 8bc610d1..0f96a106 100644
--- a/plugins/qrcode/qrcode.php
+++ b/plugins/qrcode/qrcode.php
@@ -59,3 +59,12 @@ function hook_qrcode_render_includes($data)
59 59
60 return $data; 60 return $data;
61} 61}
62
63/**
64 * This function is never called, but contains translation calls for GNU gettext extraction.
65 */
66function qrcode_dummy_translation()
67{
68 // meta
69 t('For each link, add a QRCode icon.');
70}
diff --git a/plugins/wallabag/wallabag.html b/plugins/wallabag/wallabag.html
index e861536d..4c57691d 100644
--- a/plugins/wallabag/wallabag.html
+++ b/plugins/wallabag/wallabag.html
@@ -1 +1,5 @@
1<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> 1<span>
2 <a href="%s%s" target="_blank">
3 <img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="%s" alt="wallabag" />
4 </a>
5</span>
diff --git a/plugins/wallabag/wallabag.php b/plugins/wallabag/wallabag.php
index 641e4cc2..9dfd079e 100644
--- a/plugins/wallabag/wallabag.php
+++ b/plugins/wallabag/wallabag.php
@@ -5,6 +5,7 @@
5 */ 5 */
6 6
7require_once 'WallabagInstance.php'; 7require_once 'WallabagInstance.php';
8use Shaarli\Config\ConfigManager;
8 9
9/** 10/**
10 * Init function, return an error if the server is not set. 11 * Init function, return an error if the server is not set.
@@ -17,8 +18,8 @@ function wallabag_init($conf)
17{ 18{
18 $wallabagUrl = $conf->get('plugins.WALLABAG_URL'); 19 $wallabagUrl = $conf->get('plugins.WALLABAG_URL');
19 if (empty($wallabagUrl)) { 20 if (empty($wallabagUrl)) {
20 $error = 'Wallabag plugin error: '. 21 $error = t('Wallabag plugin error: '.
21 'Please define the "WALLABAG_URL" setting in the plugin administration page.'; 22 'Please define the "WALLABAG_URL" setting in the plugin administration page.');
22 return array($error); 23 return array($error);
23 } 24 }
24} 25}
@@ -43,12 +44,14 @@ function hook_wallabag_render_linklist($data, $conf)
43 44
44 $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html'); 45 $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html');
45 46
47 $linkTitle = t('Save to wallabag');
46 foreach ($data['links'] as &$value) { 48 foreach ($data['links'] as &$value) {
47 $wallabag = sprintf( 49 $wallabag = sprintf(
48 $wallabagHtml, 50 $wallabagHtml,
49 $wallabagInstance->getWallabagUrl(), 51 $wallabagInstance->getWallabagUrl(),
50 urlencode($value['url']), 52 urlencode($value['url']),
51 PluginManager::$PLUGINS_PATH 53 PluginManager::$PLUGINS_PATH,
54 $linkTitle
52 ); 55 );
53 $value['link_plugin'][] = $wallabag; 56 $value['link_plugin'][] = $wallabag;
54 } 57 }
@@ -56,3 +59,14 @@ function hook_wallabag_render_linklist($data, $conf)
56 return $data; 59 return $data;
57} 60}
58 61
62/**
63 * This function is never called, but contains translation calls for GNU gettext extraction.
64 */
65function wallabag_dummy_translation()
66{
67 // meta
68 t('For each link, add a QRCode icon.');
69 t('Wallabag API URL');
70 t('Wallabag API version (1 or 2)');
71}
72
diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php
index 79c136c8..46bfcd72 100644
--- a/tests/LanguagesTest.php
+++ b/tests/LanguagesTest.php
@@ -1,41 +1,201 @@
1<?php 1<?php
2 2
3require_once 'application/Languages.php'; 3namespace Shaarli;
4
5use Shaarli\Config\ConfigManager;
4 6
5/** 7/**
6 * Class LanguagesTest. 8 * Class LanguagesTest.
7 */ 9 */
8class LanguagesTest extends PHPUnit_Framework_TestCase 10class LanguagesTest extends \PHPUnit_Framework_TestCase
9{ 11{
10 /** 12 /**
13 * @var string Config file path (without extension).
14 */
15 protected static $configFile = 'tests/utils/config/configJson';
16
17 /**
18 * @var ConfigManager
19 */
20 protected $conf;
21
22 /**
23 *
24 */
25 public function setUp()
26 {
27 $this->conf = new ConfigManager(self::$configFile);
28 }
29
30 /**
31 * Test t() with a simple non identified value.
32 */
33 public function testTranslateSingleNotIDGettext()
34 {
35 $this->conf->set('translation.mode', 'gettext');
36 new Languages('en', $this->conf);
37 $text = 'abcdé 564 fgK';
38 $this->assertEquals($text, t($text));
39 }
40
41 /**
42 * Test t() with a simple identified value in gettext mode.
43 */
44 public function testTranslateSingleIDGettext()
45 {
46 $this->conf->set('translation.mode', 'gettext');
47 new Languages('en', $this->conf);
48 $text = 'permalink';
49 $this->assertEquals($text, t($text));
50 }
51
52 /**
53 * Test t() with a non identified plural form in gettext mode.
54 */
55 public function testTranslatePluralNotIDGettext()
56 {
57 $this->conf->set('translation.mode', 'gettext');
58 new Languages('en', $this->conf);
59 $text = 'sandwich';
60 $nText = 'sandwiches';
61 $this->assertEquals('sandwiches', t($text, $nText, 0));
62 $this->assertEquals('sandwich', t($text, $nText, 1));
63 $this->assertEquals('sandwiches', t($text, $nText, 2));
64 }
65
66 /**
67 * Test t() with an identified plural form in gettext mode.
68 */
69 public function testTranslatePluralIDGettext()
70 {
71 $this->conf->set('translation.mode', 'gettext');
72 new Languages('en', $this->conf);
73 $text = 'shaare';
74 $nText = 'shaares';
75 // In english, zero is followed by plural form
76 $this->assertEquals('shaares', t($text, $nText, 0));
77 $this->assertEquals('shaare', t($text, $nText, 1));
78 $this->assertEquals('shaares', t($text, $nText, 2));
79 }
80
81 /**
11 * Test t() with a simple non identified value. 82 * Test t() with a simple non identified value.
12 */ 83 */
13 public function testTranslateSingleNotID() 84 public function testTranslateSingleNotIDPhp()
14 { 85 {
86 $this->conf->set('translation.mode', 'php');
87 new Languages('en', $this->conf);
15 $text = 'abcdé 564 fgK'; 88 $text = 'abcdé 564 fgK';
16 $this->assertEquals($text, t($text)); 89 $this->assertEquals($text, t($text));
17 } 90 }
18 91
19 /** 92 /**
20 * Test t() with a non identified plural form. 93 * Test t() with a simple identified value in PHP mode.
21 */ 94 */
22 public function testTranslatePluralNotID() 95 public function testTranslateSingleIDPhp()
23 { 96 {
24 $text = '%s sandwich'; 97 $this->conf->set('translation.mode', 'php');
25 $nText = '%s sandwiches'; 98 new Languages('en', $this->conf);
26 $this->assertEquals('0 sandwich', t($text, $nText)); 99 $text = 'permalink';
27 $this->assertEquals('1 sandwich', t($text, $nText, 1)); 100 $this->assertEquals($text, t($text));
28 $this->assertEquals('2 sandwiches', t($text, $nText, 2));
29 } 101 }
30 102
31 /** 103 /**
32 * Test t() with a non identified invalid plural form. 104 * Test t() with a non identified plural form in PHP mode.
33 */ 105 */
34 public function testTranslatePluralNotIDInvalid() 106 public function testTranslatePluralNotIDPhp()
35 { 107 {
108 $this->conf->set('translation.mode', 'php');
109 new Languages('en', $this->conf);
36 $text = 'sandwich'; 110 $text = 'sandwich';
37 $nText = 'sandwiches'; 111 $nText = 'sandwiches';
112 $this->assertEquals('sandwiches', t($text, $nText, 0));
38 $this->assertEquals('sandwich', t($text, $nText, 1)); 113 $this->assertEquals('sandwich', t($text, $nText, 1));
39 $this->assertEquals('sandwiches', t($text, $nText, 2)); 114 $this->assertEquals('sandwiches', t($text, $nText, 2));
40 } 115 }
116
117 /**
118 * Test t() with an identified plural form in PHP mode.
119 */
120 public function testTranslatePluralIDPhp()
121 {
122 $this->conf->set('translation.mode', 'php');
123 new Languages('en', $this->conf);
124 $text = 'shaare';
125 $nText = 'shaares';
126 // In english, zero is followed by plural form
127 $this->assertEquals('shaares', t($text, $nText, 0));
128 $this->assertEquals('shaare', t($text, $nText, 1));
129 $this->assertEquals('shaares', t($text, $nText, 2));
130 }
131
132 /**
133 * Test t() with an invalid language set in the configuration in gettext mode.
134 */
135 public function testTranslateWithInvalidConfLanguageGettext()
136 {
137 $this->conf->set('translation.mode', 'gettext');
138 $this->conf->set('translation.language', 'nope');
139 new Languages('fr', $this->conf);
140 $text = 'grumble';
141 $this->assertEquals($text, t($text));
142 }
143
144 /**
145 * Test t() with an invalid language set in the configuration in PHP mode.
146 */
147 public function testTranslateWithInvalidConfLanguagePhp()
148 {
149 $this->conf->set('translation.mode', 'php');
150 $this->conf->set('translation.language', 'nope');
151 new Languages('fr', $this->conf);
152 $text = 'grumble';
153 $this->assertEquals($text, t($text));
154 }
155
156 /**
157 * Test t() with an invalid language set with auto language in gettext mode.
158 */
159 public function testTranslateWithInvalidAutoLanguageGettext()
160 {
161 $this->conf->set('translation.mode', 'gettext');
162 new Languages('nope', $this->conf);
163 $text = 'grumble';
164 $this->assertEquals($text, t($text));
165 }
166
167 /**
168 * Test t() with an invalid language set with auto language in PHP mode.
169 */
170 public function testTranslateWithInvalidAutoLanguagePhp()
171 {
172 $this->conf->set('translation.mode', 'php');
173 new Languages('nope', $this->conf);
174 $text = 'grumble';
175 $this->assertEquals($text, t($text));
176 }
177
178 /**
179 * Test t() with an extension language file in gettext mode
180 */
181 public function testTranslationExtensionGettext()
182 {
183 $this->conf->set('translation.mode', 'gettext');
184 $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
185 new Languages('en', $this->conf);
186 $this->assertEquals('car', t('car', 'car', 1, 'test'));
187 $this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
188 }
189
190 /**
191 * Test t() with an extension language file in PHP mode
192 */
193 public function testTranslationExtensionPhp()
194 {
195 $this->conf->set('translation.mode', 'php');
196 $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
197 new Languages('en', $this->conf);
198 $this->assertEquals('car', t('car', 'car', 1, 'test'));
199 $this->assertEquals('Search', t('Search', 'Search', 1, 'test'));
200 }
41} 201}
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index 3d1aa653..840eaf21 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -384,18 +384,18 @@ class UtilsTest extends PHPUnit_Framework_TestCase
384 */ 384 */
385 public function testHumanBytes() 385 public function testHumanBytes()
386 { 386 {
387 $this->assertEquals('2kiB', human_bytes(2 * 1024)); 387 $this->assertEquals('2'. t('kiB'), human_bytes(2 * 1024));
388 $this->assertEquals('2kiB', human_bytes(strval(2 * 1024))); 388 $this->assertEquals('2'. t('kiB'), human_bytes(strval(2 * 1024)));
389 $this->assertEquals('2MiB', human_bytes(2 * (pow(1024, 2)))); 389 $this->assertEquals('2'. t('MiB'), human_bytes(2 * (pow(1024, 2))));
390 $this->assertEquals('2MiB', human_bytes(strval(2 * (pow(1024, 2))))); 390 $this->assertEquals('2'. t('MiB'), human_bytes(strval(2 * (pow(1024, 2)))));
391 $this->assertEquals('2GiB', human_bytes(2 * (pow(1024, 3)))); 391 $this->assertEquals('2'. t('GiB'), human_bytes(2 * (pow(1024, 3))));
392 $this->assertEquals('2GiB', human_bytes(strval(2 * (pow(1024, 3))))); 392 $this->assertEquals('2'. t('GiB'), human_bytes(strval(2 * (pow(1024, 3)))));
393 $this->assertEquals('374B', human_bytes(374)); 393 $this->assertEquals('374'. t('B'), human_bytes(374));
394 $this->assertEquals('374B', human_bytes('374')); 394 $this->assertEquals('374'. t('B'), human_bytes('374'));
395 $this->assertEquals('232kiB', human_bytes(237481)); 395 $this->assertEquals('232'. t('kiB'), human_bytes(237481));
396 $this->assertEquals('Unlimited', human_bytes('0')); 396 $this->assertEquals(t('Unlimited'), human_bytes('0'));
397 $this->assertEquals('Unlimited', human_bytes(0)); 397 $this->assertEquals(t('Unlimited'), human_bytes(0));
398 $this->assertEquals('Setting not set', human_bytes('')); 398 $this->assertEquals(t('Setting not set'), human_bytes(''));
399 } 399 }
400 400
401 /** 401 /**
@@ -403,9 +403,9 @@ class UtilsTest extends PHPUnit_Framework_TestCase
403 */ 403 */
404 public function testGetMaxUploadSize() 404 public function testGetMaxUploadSize()
405 { 405 {
406 $this->assertEquals('1MiB', get_max_upload_size(2097152, '1024k')); 406 $this->assertEquals('1'. t('MiB'), get_max_upload_size(2097152, '1024k'));
407 $this->assertEquals('1MiB', get_max_upload_size('1m', '2m')); 407 $this->assertEquals('1'. t('MiB'), get_max_upload_size('1m', '2m'));
408 $this->assertEquals('100B', get_max_upload_size(100, 100)); 408 $this->assertEquals('100'. t('B'), get_max_upload_size(100, 100));
409 } 409 }
410 410
411 /** 411 /**
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 00000000..d36d73cd
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,6 @@
1<?php
2
3require_once 'vendor/autoload.php';
4
5$conf = new \Shaarli\Config\ConfigManager('tests/utils/config/configJson');
6new \Shaarli\Languages('en', $conf);
diff --git a/tests/languages/bootstrap.php b/tests/languages/bootstrap.php
index 95609210..da6ac2e4 100644
--- a/tests/languages/bootstrap.php
+++ b/tests/languages/bootstrap.php
@@ -1,7 +1,6 @@
1<?php 1<?php
2if (! empty('UT_LOCALE')) { 2require_once 'tests/bootstrap.php';
3
4if (! empty(getenv('UT_LOCALE'))) {
3 setlocale(LC_ALL, getenv('UT_LOCALE')); 5 setlocale(LC_ALL, getenv('UT_LOCALE'));
4} 6}
5
6require_once 'vendor/autoload.php';
7
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php
new file mode 100644
index 00000000..c05a0f98
--- /dev/null
+++ b/tests/languages/fr/LanguagesFrTest.php
@@ -0,0 +1,173 @@
1<?php
2
3
4namespace Shaarli;
5
6
7use Shaarli\Config\ConfigManager;
8
9/**
10 * Class LanguagesFrTest
11 *
12 * Test the translation system in PHP and gettext mode with French language.
13 *
14 * @package Shaarli
15 */
16class LanguagesFrTest extends \PHPUnit_Framework_TestCase
17{
18 /**
19 * @var string Config file path (without extension).
20 */
21 protected static $configFile = 'tests/utils/config/configJson';
22
23 /**
24 * @var ConfigManager
25 */
26 protected $conf;
27
28 /**
29 * Init: force French
30 */
31 public function setUp()
32 {
33 $this->conf = new ConfigManager(self::$configFile);
34 $this->conf->set('translation.language', 'fr');
35 }
36
37 /**
38 * Reset the locale since gettext seems to mess with it, making it too long
39 */
40 public static function tearDownAfterClass()
41 {
42 if (! empty(getenv('UT_LOCALE'))) {
43 setlocale(LC_ALL, getenv('UT_LOCALE'));
44 }
45 }
46
47 /**
48 * Test t() with a simple non identified value.
49 */
50 public function testTranslateSingleNotIDGettext()
51 {
52 $this->conf->set('translation.mode', 'gettext');
53 new Languages('en', $this->conf);
54 $text = 'abcdé 564 fgK';
55 $this->assertEquals($text, t($text));
56 }
57
58 /**
59 * Test t() with a simple identified value in gettext mode.
60 */
61 public function testTranslateSingleIDGettext()
62 {
63 $this->conf->set('translation.mode', 'gettext');
64 new Languages('en', $this->conf);
65 $text = 'permalink';
66 $this->assertEquals('permalien', t($text));
67 }
68
69 /**
70 * Test t() with a non identified plural form in gettext mode.
71 */
72 public function testTranslatePluralNotIDGettext()
73 {
74 $this->conf->set('translation.mode', 'gettext');
75 new Languages('en', $this->conf);
76 $text = 'sandwich';
77 $nText = 'sandwiches';
78 // Not ID, so English fallback, and in english, plural 0
79 $this->assertEquals('sandwiches', t($text, $nText, 0));
80 $this->assertEquals('sandwich', t($text, $nText, 1));
81 $this->assertEquals('sandwiches', t($text, $nText, 2));
82 }
83
84 /**
85 * Test t() with an identified plural form in gettext mode.
86 */
87 public function testTranslatePluralIDGettext()
88 {
89 $this->conf->set('translation.mode', 'gettext');
90 new Languages('en', $this->conf);
91 $text = 'shaare';
92 $nText = 'shaares';
93 $this->assertEquals('shaare', t($text, $nText, 0));
94 $this->assertEquals('shaare', t($text, $nText, 1));
95 $this->assertEquals('shaares', t($text, $nText, 2));
96 }
97
98 /**
99 * Test t() with a simple non identified value.
100 */
101 public function testTranslateSingleNotIDPhp()
102 {
103 $this->conf->set('translation.mode', 'php');
104 new Languages('en', $this->conf);
105 $text = 'abcdé 564 fgK';
106 $this->assertEquals($text, t($text));
107 }
108
109 /**
110 * Test t() with a simple identified value in PHP mode.
111 */
112 public function testTranslateSingleIDPhp()
113 {
114 $this->conf->set('translation.mode', 'php');
115 new Languages('en', $this->conf);
116 $text = 'permalink';
117 $this->assertEquals('permalien', t($text));
118 }
119
120 /**
121 * Test t() with a non identified plural form in PHP mode.
122 */
123 public function testTranslatePluralNotIDPhp()
124 {
125 $this->conf->set('translation.mode', 'php');
126 new Languages('en', $this->conf);
127 $text = 'sandwich';
128 $nText = 'sandwiches';
129 // Not ID, so English fallback, and in english, plural 0
130 $this->assertEquals('sandwiches', t($text, $nText, 0));
131 $this->assertEquals('sandwich', t($text, $nText, 1));
132 $this->assertEquals('sandwiches', t($text, $nText, 2));
133 }
134
135 /**
136 * Test t() with an identified plural form in PHP mode.
137 */
138 public function testTranslatePluralIDPhp()
139 {
140 $this->conf->set('translation.mode', 'php');
141 new Languages('en', $this->conf);
142 $text = 'shaare';
143 $nText = 'shaares';
144 // In english, zero is followed by plural form
145 $this->assertEquals('shaare', t($text, $nText, 0));
146 $this->assertEquals('shaare', t($text, $nText, 1));
147 $this->assertEquals('shaares', t($text, $nText, 2));
148 }
149
150 /**
151 * Test t() with an extension language file in gettext mode
152 */
153 public function testTranslationExtensionGettext()
154 {
155 $this->conf->set('translation.mode', 'gettext');
156 $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
157 new Languages('en', $this->conf);
158 $this->assertEquals('voiture', t('car', 'car', 1, 'test'));
159 $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
160 }
161
162 /**
163 * Test t() with an extension language file in PHP mode
164 */
165 public function testTranslationExtensionPhp()
166 {
167 $this->conf->set('translation.mode', 'php');
168 $this->conf->set('translation.extensions.test', 'tests/utils/languages/');
169 new Languages('en', $this->conf);
170 $this->assertEquals('voiture', t('car', 'car', 1, 'test'));
171 $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test'));
172 }
173}
diff --git a/tests/utils/languages/fr/LC_MESSAGES/test.mo b/tests/utils/languages/fr/LC_MESSAGES/test.mo
new file mode 100644
index 00000000..416c7831
--- /dev/null
+++ b/tests/utils/languages/fr/LC_MESSAGES/test.mo
Binary files 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
index 00000000..89a4fd9b
--- /dev/null
+++ b/tests/utils/languages/fr/LC_MESSAGES/test.po
@@ -0,0 +1,19 @@
1msgid ""
2msgstr ""
3"Project-Id-Version: Extension test\n"
4"POT-Creation-Date: 2017-05-20 13:54+0200\n"
5"PO-Revision-Date: 2017-05-20 14:16+0200\n"
6"Last-Translator: \n"
7"Language-Team: Shaarli\n"
8"Language: fr_FR\n"
9"MIME-Version: 1.0\n"
10"Content-Type: text/plain; charset=UTF-8\n"
11"Content-Transfer-Encoding: 8bit\n"
12"Plural-Forms: nplurals=2; plural=(n > 1);\n"
13"X-Generator: Poedit 2.0.1\n"
14
15msgid "car"
16msgstr "voiture"
17
18msgid "Search"
19msgstr "Fouille"
diff --git a/tpl/default/import.html b/tpl/default/import.html
index 1f040685..000a50ac 100644
--- a/tpl/default/import.html
+++ b/tpl/default/import.html
@@ -18,7 +18,7 @@
18 <div class="center" id="import-field"> 18 <div class="center" id="import-field">
19 <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}"> 19 <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}">
20 <input type="file" name="filetoupload"> 20 <input type="file" name="filetoupload">
21 <p><br>Maximum size allowed: <strong>{$maxfilesizeHuman}</strong></p> 21 <p><br>{'Maximum size allowed:'|t} <strong>{$maxfilesizeHuman}</strong></p>
22 </div> 22 </div>
23 23
24 <div class="pure-g"> 24 <div class="pure-g">
@@ -31,15 +31,15 @@
31 <div class="radio-buttons"> 31 <div class="radio-buttons">
32 <div> 32 <div>
33 <input type="radio" name="privacy" value="default" checked="checked"> 33 <input type="radio" name="privacy" value="default" checked="checked">
34 Use values from the imported file, default to public 34 {'Use values from the imported file, default to public'|t}
35 </div> 35 </div>
36 <div> 36 <div>
37 <input type="radio" name="privacy" value="private"> 37 <input type="radio" name="privacy" value="private">
38 Import all bookmarks as private 38 {'Import all bookmarks as private'|t}
39 </div> 39 </div>
40 <div> 40 <div>
41 <input type="radio" name="privacy" value="public"> 41 <input type="radio" name="privacy" value="public">
42 Import all bookmarks as public 42 {'Import all bookmarks as public'|t}
43 </div> 43 </div>
44 </div> 44 </div>
45 </div> 45 </div>
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 685821e3..5dab8e9a 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -86,7 +86,7 @@
86 <div class="pure-g pure-alert pure-alert-success search-result"> 86 <div class="pure-g pure-alert pure-alert-success search-result">
87 <div class="pure-u-2-24"></div> 87 <div class="pure-u-2-24"></div>
88 <div class="pure-u-20-24"> 88 <div class="pure-u-20-24">
89 {function="t('%s result', '%s results', $result_count)"} 89 {function="sprintf(t('%s result', '%s results', $result_count), $result_count)"}
90 {if="!empty($search_term)"} 90 {if="!empty($search_term)"}
91 {'for'|t} <em><strong>{$search_term}</strong></em> 91 {'for'|t} <em><strong>{$search_term}</strong></em>
92 {/if} 92 {/if}
@@ -117,6 +117,16 @@
117 <div class="pure-g"> 117 <div class="pure-g">
118 <div class="pure-u-lg-2-24 pure-u-1-24"></div> 118 <div class="pure-u-lg-2-24 pure-u-1-24"></div>
119 <div class="pure-u-lg-20-24 pure-u-22-24"> 119 <div class="pure-u-lg-20-24 pure-u-22-24">
120 {ignore}Set translation here, for performances{/ignore}
121 {$strPrivate=t('Private')}
122 {$strEdit=t('Edit')}
123 {$strDelete=t('Delete')}
124 {$strFold=t('Fold')}
125 {$strEdited=t('Edited: ')}
126 {$strPermalink=t('Permalink')}
127 {$strPermalinkLc=t('permalink')}
128 {$strAddTag=t('Add tag')}
129 {ignore}End of translations{/ignore}
120 {loop="links"} 130 {loop="links"}
121 <div class="anchor" id="{$value.shorturl}"></div> 131 <div class="anchor" id="{$value.shorturl}"></div>
122 <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}"> 132 <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
@@ -125,12 +135,12 @@
125 {if="isLoggedIn()"} 135 {if="isLoggedIn()"}
126 <div class="linklist-item-editbuttons"> 136 <div class="linklist-item-editbuttons">
127 {if="$value.private"} 137 {if="$value.private"}
128 <span class="label label-private">{'Private'|t}</span> 138 <span class="label label-private">{$strPrivate}</span>
129 {/if} 139 {/if}
130 <input type="checkbox" class="delete-checkbox" value="{$value.id}"> 140 <input type="checkbox" class="delete-checkbox" value="{$value.id}">
131 <!-- FIXME! JS translation --> 141 <!-- FIXME! JS translation -->
132 <a href="?edit_link={$value.id}" title="{'Edit'|t}"><i class="fa fa-pencil-square-o edit-link"></i></a> 142 <a href="?edit_link={$value.id}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link"></i></a>
133 <a href="#" title="{'Fold'|t}" class="fold-button"><i class="fa fa-chevron-up"></i></a> 143 <a href="#" title="{$strFold}" class="fold-button"><i class="fa fa-chevron-up"></i></a>
134 </div> 144 </div>
135 {/if} 145 {/if}
136 146
@@ -164,7 +174,7 @@
164 <i class="fa fa-tags"></i> 174 <i class="fa fa-tags"></i>
165 {$tag_counter=count($value.taglist)} 175 {$tag_counter=count($value.taglist)}
166 {loop="value.taglist"} 176 {loop="value.taglist"}
167 <span class="label label-tag" title="Add tag"> 177 <span class="label label-tag" title="{$strAddTag}">
168 <a href="?addtag={$value|urlencode}">{$value}</a> 178 <a href="?addtag={$value|urlencode}">{$value}</a>
169 </span> 179 </span>
170 {if="$tag_counter - 1 != $counter"}&middot;{/if} 180 {if="$tag_counter - 1 != $counter"}&middot;{/if}
@@ -174,9 +184,9 @@
174 184
175 <div class="pure-g"> 185 <div class="pure-g">
176 <div class="linklist-item-infos-dateblock pure-u-lg-3-8 pure-u-1"> 186 <div class="linklist-item-infos-dateblock pure-u-lg-3-8 pure-u-1">
177 <a href="?{$value.shorturl}" title="{'Permalink'|t}"> 187 <a href="?{$value.shorturl}" title="{$strPermalink}">
178 {if="!$hide_timestamps || isLoggedIn()"} 188 {if="!$hide_timestamps || isLoggedIn()"}
179 {$updated=$value.updated_timestamp ? 'Edited: '. format_date($value.updated) : 'Permalink'} 189 {$updated=$value.updated_timestamp ? $strEdited. format_date($value.updated) : $strPermalink}
180 <span class="linkdate" title="{$updated}"> 190 <span class="linkdate" title="{$updated}">
181 <i class="fa fa-clock-o"></i> 191 <i class="fa fa-clock-o"></i>
182 {$value.created|format_date} 192 {$value.created|format_date}
@@ -184,7 +194,7 @@
184 &middot; 194 &middot;
185 </span> 195 </span>
186 {/if} 196 {/if}
187 {'permalink'|t} 197 {$strPermalinkLc}
188 </a> 198 </a>
189 199
190 <div class="pure-u-0 pure-u-lg-visible"> 200 <div class="pure-u-0 pure-u-lg-visible">
@@ -205,7 +215,7 @@
205 </a> 215 </a>
206 {if="isLoggedIn()"} 216 {if="isLoggedIn()"}
207 <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}" 217 <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}"
208 title="{'Delete'|t}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete"> 218 title="{$strDelete}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete">
209 <i class="fa fa-trash"></i> 219 <i class="fa fa-trash"></i>
210 </a> 220 </a>
211 {/if} 221 {/if}
@@ -221,7 +231,7 @@
221 {if="isLoggedIn()"} 231 {if="isLoggedIn()"}
222 &middot; 232 &middot;
223 <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}" 233 <a href="?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}"
224 title="{'Delete'|t}" class="delete-link confirm-delete"> 234 title="{$strDelete}" class="delete-link confirm-delete">
225 <i class="fa fa-trash"></i> 235 <i class="fa fa-trash"></i>
226 </a> 236 </a>
227 {/if} 237 {/if}
diff --git a/tpl/default/page.footer.html b/tpl/default/page.footer.html
index 54b16e8a..2c788e2f 100644
--- a/tpl/default/page.footer.html
+++ b/tpl/default/page.footer.html
@@ -8,8 +8,8 @@
8 {$version} 8 {$version}
9 {/if} 9 {/if}
10 &middot; 10 &middot;
11 The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community &middot; 11 {'The personal, minimalist, super-fast, database free, bookmarking service'|t} {'by the Shaarli community'|t} &middot;
12 <a href="doc/html/index.html" rel="nofollow">Documentation</a> 12 <a href="doc/html/index.html" rel="nofollow">{'Documentation'|t}</a>
13 {loop="$plugins_footer.text"} 13 {loop="$plugins_footer.text"}
14 {$value} 14 {$value}
15 {/loop} 15 {/loop}
diff --git a/tpl/default/pluginsadmin.html b/tpl/default/pluginsadmin.html
index 5cc1802f..717cb517 100644
--- a/tpl/default/pluginsadmin.html
+++ b/tpl/default/pluginsadmin.html
@@ -116,8 +116,8 @@
116 </section> 116 </section>
117 117
118 <div class="center more"> 118 <div class="center more">
119 More plugins available 119 {"More plugins available"|t}
120 <a href="doc/Community-&-Related-software.html#third-party-plugins">in the documentation</a>. 120 <a href="doc/Community-&-Related-software.html#third-party-plugins">{"in the documentation"|t}</a>.
121 </div> 121 </div>
122 <div class="center"> 122 <div class="center">
123 <input type="submit" value="{'Save'|t}" name="save"> 123 <input type="submit" value="{'Save'|t}" name="save">