From 4825c25f630b3c54d01c2357c68df0fa0b1b6746 Mon Sep 17 00:00:00 2001 From: Cyrille L Date: Fri, 21 Apr 2023 09:47:21 +0200 Subject: [PATCH] [0.10.3] --- CHANGELOG.md | 3 + README.md | 10 +- debian/control | 2 +- src/usr/bin/tyto | 2 +- src/var/lib/tyto/help/styles.css | 23 +- src/var/lib/tyto/program/check.py | 74 +++-- src/var/lib/tyto/program/dom.py | 331 ++++++++++++----------- src/var/lib/tyto/program/form.py | 73 ++--- src/var/lib/tyto/program/html.py | 61 ++--- src/var/lib/tyto/program/logs.py | 6 + src/var/lib/tyto/program/new.py | 38 ++- src/var/lib/tyto/program/status.py | 129 ++++++--- src/var/lib/tyto/program/tyto.py | 253 ++++++++++------- src/var/lib/tyto/program/wip.py | 106 ++++---- src/var/lib/tyto/translations/logs_en.py | 5 +- src/var/lib/tyto/translations/logs_fr.py | 5 +- src/var/lib/tyto/translations/site_en.py | 1 + src/var/lib/tyto/translations/site_fr.py | 1 + 18 files changed, 675 insertions(+), 448 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ccc2f3..4625c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Tyto - Littérateur - Changelog: https://git.a-lec.org/echolib/tyto-litterateur/-/blob/master/CHANGELOG.md - License: https://git.a-lec.org/echolib/tyto-litterateur/-/blob/master/LICENSE +## {0.10.3] +- pre 1.0 + ## [0.10.2] - Citer dans un texte > `[_` + `_]` - Italique `` > `;_` + `_;` diff --git a/README.md b/README.md index c4660b0..8af7217 100644 --- a/README.md +++ b/README.md @@ -54,19 +54,17 @@ abbr: abbrev # Les titres HTML vont de 1 à 6. # Utiliser #N, où N est entre 1 et 6. # Si du contenu existe entre les titres, une
est ajoutée -# Astuce: on commence en général par #2 dans l'article le titre du site -# étant en #1 # Source -#2 Titre 1 +#1 Titre 1 Contenu 1 -#3 Titre 2 +#2 Titre 2 -#4 Titre 3 +#3 Titre 3 contenu 2 -#5 Titre 4 +#4 Titre 4 ``` ### Balise div diff --git a/debian/control b/debian/control index 77c6364..574a0e7 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Package: tyto -Version: 0.10.2 +Version: 0.10.3 Section: custom Priority: optional Architecture: all diff --git a/src/usr/bin/tyto b/src/usr/bin/tyto index e4e7998..41ef166 100755 --- a/src/usr/bin/tyto +++ b/src/usr/bin/tyto @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Version: 0.10.2 +# Version: 0.10.3 # Tyto - Littérateur # # Copyright (C) 2023 Cyrille Louarn diff --git a/src/var/lib/tyto/help/styles.css b/src/var/lib/tyto/help/styles.css index 2dacdf7..7da4d9b 100644 --- a/src/var/lib/tyto/help/styles.css +++ b/src/var/lib/tyto/help/styles.css @@ -52,10 +52,11 @@ a.site_menu_link { article#article_main { } -h1#main_title { -} -h2.title_2 { +/* article title */ +h2#main_title { } + +/* Writer titles*/ h3.title_3 { } h4.title_4 { @@ -65,9 +66,17 @@ h5.title_5 { h6.title_6 { } -/* Between every IF CONTENTS */ +/* Between every IF CONTENTS */ div.contents { } +div.contents_3 { +} +div.contents_4 { +} +div.contents_5 { +} +div.contents_6 { +} /* Default if not set in post */ p.DOMAIN { @@ -138,7 +147,7 @@ a#article_code_link { aside#sidebar { } -h1#sidebar_title { +h2#sidebar_title { } ul#sidebar_list { } @@ -146,7 +155,7 @@ li.sidebar_item { } a.sidebar_item_link { } -h2.sidebar_item_title { +h3.sidebar_item_title { } p.sidebar_item_about { } @@ -165,7 +174,7 @@ div#footer_container { /* Block*/ div#footer_infos { } -h1#footer_site_title { +h2#footer_site_title { } p#footer_about { } diff --git a/src/var/lib/tyto/program/check.py b/src/var/lib/tyto/program/check.py index 144d08b..7696f0c 100644 --- a/src/var/lib/tyto/program/check.py +++ b/src/var/lib/tyto/program/check.py @@ -166,15 +166,25 @@ def check_all(option): # Check articles process # #------------------------# def check_process(target): + global post_err + if not db.post: - logs.out("1", db.post, True) + logs.out("1", db.post, False) + post_err = True + return + + # Start checking processes + #------------------------- + # Convert file to string + # Also check for separator and empty article + file_to_string() + if post_err: + return global post_bottom, article_bottom global date_wip, hash_wip, date_www, hash_www, post_bottom - global post_err date_wip = hash_wip = date_www = hash_www = '' - post_err = False # Set values for wip and www from DB if db.exists: @@ -213,12 +223,6 @@ def check_process(target): http_www = "%s/%s"%(dom.www_url, srv_post_short_uri) http_wip = '%s/%s'%(dom.wip_url, srv_post_short_uri) - # Start checking processes - #------------------------- - # Convert file to string - # Also check for separator and empty article - file_to_string() - # Check for icodes, bcodes, quotes # check icodes marks on same line if_icodes_bcodes_quotes(post_bottom) @@ -269,14 +273,21 @@ def check_process(target): # Check if separator or exit # #---------------------------------# def file_to_string(): + global post_err global article, post_header, post_bottom global ln_header, ln_bottom, ln_article + post_err = False post_header = post_bottom = '' sep = content = False article = open(db.uri_file, 'r').read() for line in article.rsplit('\n'): + if line.startswith(logs.shebang): + logs.out("82", db.uri_file, False) + post_err = True + return + if line.startswith('-----'): if not sep: sep = True @@ -292,12 +303,16 @@ def file_to_string(): # Check if separator or exit if not sep: - logs.out("6", '"-----" > %s'%db.uri_file, True) + logs.out("6", '"-----" > %s'%db.uri_file, False) + post_err = True + return if not content: if db.exists and tyto.exists(db.config): os.remove(db.config) - logs.out("18", db.uri_file, True) + logs.out("18", db.uri_file, False) + post_err = True + return # +2 > (start at 0 + separator line) ln_article = len(article.splitlines()) + 1 @@ -313,11 +328,11 @@ def file_to_string(): def count_words(post_bottom): global post_words post_words = 0 + for line in post_bottom: if not line \ or line.startswith(tyto.nolinewords) \ - or line.startswith("#") and \ - not line.startswith(tyto.titles_tags): + or line.startswith("#") and not line.startswith(tyto.titles_user): continue post_words = post_words + len(line.strip().split(" ")) @@ -330,11 +345,11 @@ def count_words(post_bottom): #---------------------------------------------# def if_icodes_bcodes_quotes(post_bottom): global icode, quote, bcode, post_err - global post_titles, nbr_quotes, nbr_bcodes, nbr_ancs, post_comments + global nbr_quotes, nbr_bcodes, nbr_ancs, post_comments global post_images, post_raws, fcodes icode = quote = in_quote = bcode = in_bcode = in_bq = False - post_titles = nbr_quotes = nbr_bcodes = nbr_ancs = 0 + nbr_quotes = nbr_bcodes = nbr_ancs = 0 post_images = post_comments = post_raws = fcodes = 0 for ln, line in enumerate(post_bottom.rsplit('\n'), 1): @@ -363,10 +378,7 @@ def if_icodes_bcodes_quotes(post_bottom): if in_bq: continue - elif line.startswith(tyto.titles_tags): - post_titles += 1 - continue - elif line.startswith('#'): + elif line.startswith('#') and not line.startswith(tyto.titles_user): post_comments += 1 continue elif line.startswith(tyto.single_tags[1][0]): @@ -769,12 +781,36 @@ def check_anchors(): post_err = True +#==========================# +# Check titles and content # +#--------------------------# +def check_titles(): + global post_err, post_titles + post_titles = 0 + + for ln, line in enumerate(post_bottom.rsplit('\n'), 1): + ln = ln + ln_header + 1 + + if line.startswith(tyto.titles_user): + title_name = line[2: len(line)].lstrip() + if not title_name: + post_err = True + logs.out("84", 'L=%s > %s'%(ln, db.uri_file), False) + continue + + post_titles += 1 + + + + #===========================# # Check tags in post_bottom # #---------------------------# def check_content(post_bottom): global post_err + check_titles() + # Check tags for words (strongs, italics...) # Set stats for each one #------------------------------------------- diff --git a/src/var/lib/tyto/program/dom.py b/src/var/lib/tyto/program/dom.py index df5182d..f0dd064 100644 --- a/src/var/lib/tyto/program/dom.py +++ b/src/var/lib/tyto/program/dom.py @@ -50,83 +50,93 @@ except: except: hole = True +# Set current user name +try: + user +except: + try: + user = os.environ.get('USER') + except: + user = '' + + # Settings for domain, check if db is not corrupted dom_values = \ -( -'directory', -'database', -'local_user', -'lang_sys', -'lang_logs', -'articles_db_d', -'articles_d', -'files_d', -'images_d', -'modules_d', -'navbar_f', -'sidebar_f', -'metas_f', -'footer_f', -'footer_about_f', -'shortname', -'www_url', -'wip_url', -'srv_root', -'srv_domain', -'srv_wip', -'srv_wip_tpl_d', -'srv_wip_images_d', -'srv_wip_files_d', -'srv_www', -'srv_www_tpl_d', -'srv_www_images_d', -'srv_www_files_d', -'wip_css_f', -'wip_logo_f', -'wip_navbar_f', -'wip_sidebar_f', -'wip_metas_f', -'wip_footer_f', -'wip_stats_f', -'www_css_f', -'www_navbar_f', -'www_sidebar_f', -'www_metas_f', -'www_footer_f', -'www_stats_f', -'www_logo_f', -'www_rss_f', -'logo', -'styles', -'rss', -'rss_items', -'title', -'date', -'about', -'lang_site', -'mail', -'tags', -'license', -'license_url', -'legal_url', -'terms_url', -'css', -'sep', -'article_code', -'relme', -'sidebar_title', -'sidebar_items', -'activated' -) + ( + 'directory', + 'database', + 'local_user', + 'lang_sys', + 'lang_logs', + 'articles_db_d', + 'articles_d', + 'files_d', + 'images_d', + 'modules_d', + 'navbar_f', + 'sidebar_f', + 'metas_f', + 'footer_f', + 'footer_about_f', + 'shortname', + 'www_url', + 'wip_url', + 'srv_root', + 'srv_domain', + 'srv_wip', + 'srv_wip_tpl_d', + 'srv_wip_images_d', + 'srv_wip_files_d', + 'srv_www', + 'srv_www_tpl_d', + 'srv_www_images_d', + 'srv_www_files_d', + 'wip_css_f', + 'wip_logo_f', + 'wip_navbar_f', + 'wip_sidebar_f', + 'wip_metas_f', + 'wip_footer_f', + 'wip_stats_f', + 'www_css_f', + 'www_navbar_f', + 'www_sidebar_f', + 'www_metas_f', + 'www_footer_f', + 'www_stats_f', + 'www_logo_f', + 'www_rss_f', + 'logo', + 'styles', + 'rss', + 'rss_items', + 'title', + 'date', + 'about', + 'lang_site', + 'mail', + 'tags', + 'license', + 'license_url', + 'legal_url', + 'terms_url', + 'css', + 'sep', + 'article_code', + 'relme', + 'sidebar_title', + 'sidebar_items', + 'activated' + ) create_files = \ -( -'navbar_f', -'sidebar_f', -'metas_f', -'footer_f', -'footer_about_f' -) + ( + 'navbar_f', + 'sidebar_f', + 'metas_f', + 'footer_f', + 'footer_about_f' + ) wip_html_mods = () err_val = (()) # Make a list from values error @@ -186,18 +196,8 @@ if not hole: value_set = False incomplete = True active = False - - - if value.endswith('_d'): - if value_set: - if not os.path.exists(eval(str(value))): - try: - os.makedirs(eval(str(value)), exist_ok=True) - dir_new = dir_new + ((eval(str(value))),) - except: - dir_unu = dir_unu + ((eval(str(value))),) - - elif value.endswith('_f'): + + if value.endswith('_f'): if value_set: if not os.path.exists(eval(str(value))): if value in create_files: @@ -210,84 +210,99 @@ if not hole: # When an active and complete domain is needed # #----------------------------------------------# if exists and not incomplete and not corrupt: - wip_html_mods = \ - ( - eval(str('wip_navbar_f')), - eval(str('wip_sidebar_f')), - eval(str('wip_metas_f')), - eval(str('wip_footer_f')) - ) if active: ready = True - metas = ( - eval(str('metas_f')), - eval(str('wip_metas_f')), - eval(str('www_metas_f')) - ) - navbars = ( - eval(str('navbar_f')), - eval(str('wip_navbar_f')), - eval(str('www_navbar_f')) - ) - sidebars = ( - eval(str('sidebar_f')), - eval(str('wip_sidebar_f')), - eval(str('www_sidebar_f')) - ) - footers = ( - eval(str('footer_f')), - eval(str('wip_footer_f')), - eval(str('www_footer_f')), - eval(str('footer_about_f')) - ) + wip_html_mods = \ + ( + eval(str('wip_navbar_f')), + eval(str('wip_sidebar_f')), + eval(str('wip_metas_f')), + eval(str('wip_footer_f')) + ) + www_html_mods = \ + ( + eval(str('www_navbar_f')), + eval(str('www_sidebar_f')), + eval(str('www_metas_f')), + eval(str('www_footer_f')) + ) + metas = \ + ( + eval(str('metas_f')), + eval(str('wip_metas_f')), + eval(str('www_metas_f')) + ) + navbars = \ + ( + eval(str('navbar_f')), + eval(str('wip_navbar_f')), + eval(str('www_navbar_f')) + ) + sidebars = \ + ( + eval(str('sidebar_f')), + eval(str('wip_sidebar_f')), + eval(str('www_sidebar_f')) + ) + footers = \ + ( + eval(str('footer_f')), + eval(str('wip_footer_f')), + eval(str('www_footer_f')), + eval(str('footer_about_f')) + ) - templates = ( - eval(str('wip_logo_f')), - eval(str('wip_css_f')), - eval(str('wip_navbar_f')), - eval(str('wip_sidebar_f')), - eval(str('wip_metas_f')), - eval(str('wip_footer_f')), - eval(str('wip_stats_f')), - eval(str('www_logo_f')), - eval(str('www_css_f')), - eval(str('www_navbar_f')), - eval(str('www_sidebar_f')), - eval(str('www_metas_f')), - eval(str('www_footer_f')), - eval(str('www_stats_f')), - eval(str('www_rss_f')), - ) - - modules = { - "metas" : metas, - "navbar" : navbars, - "sidebar" : sidebars, - "footer" : footers, - "template": templates, - } + templates = \ + ( + eval(str('wip_logo_f')), + eval(str('wip_css_f')), + eval(str('wip_navbar_f')), + eval(str('wip_sidebar_f')), + eval(str('wip_metas_f')), + eval(str('wip_footer_f')), + eval(str('wip_stats_f')), + eval(str('www_logo_f')), + eval(str('www_css_f')), + eval(str('www_navbar_f')), + eval(str('www_sidebar_f')), + eval(str('www_metas_f')), + eval(str('www_footer_f')), + eval(str('www_stats_f')), + eval(str('www_rss_f')), + ) + + modules = \ + { + "metas" : metas, + "navbar" : navbars, + "sidebar" : sidebars, + "footer" : footers, + "template": templates, + } + + templates_files_wip = \ + ( + eval(str('wip_logo_f')), + eval(str('wip_css_f')), + eval(str('wip_navbar_f')), + eval(str('wip_sidebar_f')), + eval(str('wip_metas_f')), + eval(str('wip_footer_f')), + eval(str('wip_stats_f')), + ) - templates_wip = { - eval(str('wip_logo_f')), - eval(str('wip_css_f')), - eval(str('wip_navbar_f')), - eval(str('wip_sidebar_f')), - eval(str('wip_metas_f')), - eval(str('wip_footer_f')), - eval(str('wip_stats_f')), - } - - templates_www = { - eval(str('www_logo_f')), - eval(str('www_css_f')), - eval(str('www_navbar_f')), - eval(str('www_sidebar_f')), - eval(str('www_metas_f')), - eval(str('www_footer_f')), - eval(str('www_stats_f')), - eval(str('www_rss_f')) - } + templates_files_www = \ + ( + eval(str('www_logo_f')), + eval(str('www_css_f')), + eval(str('www_navbar_f')), + eval(str('www_sidebar_f')), + eval(str('www_metas_f')), + eval(str('www_footer_f')), + eval(str('www_stats_f')), + eval(str('www_rss_f')) + ) #====================================# # Check if domain is ready and ready # diff --git a/src/var/lib/tyto/program/form.py b/src/var/lib/tyto/program/form.py index b11665e..58e0e58 100644 --- a/src/var/lib/tyto/program/form.py +++ b/src/var/lib/tyto/program/form.py @@ -681,11 +681,20 @@ def create_domain(target): # Create in _configs/ modules files #---------------------------------- + + html.create_user_metass('new') + html.create_navbar('new') + html.create_sidebar('new') + html.create_user_footer('new') + create_footer_about('new') + + ''' create_metas('form') create_navbar('form') create_sidebar('form') create_footer('form') create_footer_about('form') + ''' print(langs.site.form_ready) @@ -700,16 +709,18 @@ def create_domain(target): #------------------------# def create_metas(option): if not dom.ready: dom.valid() - + + print(' │\n ├ %s'%langs.site.metas_inf) + if option != "new": return + # Create new default config file, or ask if exists if tyto.exists(dom.metas_f): - answer = asking(' ├ %s. %s%s '%( - langs.site.metas, langs.site.form_reset, - langs.site.q + answer = asking(' ├ %s%s '%( + langs.site.form_reset, langs.site.q ), False) if not answer in answer_yes: - if option == "form": return + if option == "new": return logs.out("255", '', True) # Set default content for @@ -766,10 +777,6 @@ def create_metas(option): '' tyto.set_file(dom.metas_f, 'New', metas_tags) - - # Create an empty html file in wip/www server if not exists - if not tyto.exists(dom.wip_metas_f): - html.create_user_metas('wip') #=============================# @@ -778,15 +785,17 @@ def create_metas(option): def create_navbar(option): if not dom.ready: dom.valid() + print(' │\n ├ %s'%langs.site.navbar_inf) + if option != "new": return + # Create new config file, or ask if exists if tyto.exists(dom.navbar_f): - answer = asking(' ├ %s. %s%s '%( - langs.site.navbar, langs.site.form_reset, - langs.site.q + answer = asking(' ├ %s%s '%( + langs.site.form_reset, langs.site.q ), False) if not answer in answer_yes: - if option == "form": return + if option == "new": return logs.out("255", '', True) @@ -827,9 +836,6 @@ def create_navbar(option): navbar_lang = navbar_lang%(tyto.Tyto, dom.navbar_f) tyto.set_file(dom.navbar_f, 'New', navbar_lang) - - # Create wip navbar file - html.create_navbar('wip') #==============================# @@ -838,19 +844,21 @@ def create_navbar(option): def create_sidebar(option): if not dom.ready: dom.valid() + print(' │\n ├ %s'%langs.site.sidebar_inf) + if option != "new": return + # Create an empty html file in wip/www server if not exists if not tyto.exists(dom.wip_sidebar_f): tyto.set_file(dom.wip_sidebar_f, 'new', '') # Create new config file, or ask if exists with option = 'reset' if tyto.exists(dom.sidebar_f): - answer = asking(' ├ %s. %s%s '%( - langs.site.sidebar, langs.site.form_reset, - langs.site.q + answer = asking(' ├ %s%s '%( + langs.site.form_reset, langs.site.q ), False) if not answer in answer_yes: - if option == "form": return + if option == "new": return logs.out("255", '', True) @@ -900,6 +908,10 @@ def create_sidebar(option): def create_footer(option): if not dom.ready: dom.valid() + print(' │\n ├ %s'%langs.site.footer_inf) + create_footer_about(option) + if option != "new": return + # Default footer contents #------------------------ Tytosrc = \ @@ -999,15 +1011,15 @@ def create_footer(option): # Final HTML footer code footer = \ '%s\n'%footer_lang%(tyto.Tyto, dom.footer_f) + \ - '
\n%s'%line + div_close = False + + # Has content after, open div + try: + post_bottom.rsplit('\n')[ln + 1] + div_open = True + except: + continue + + # New div + if div_open: + line = '%s\n
'%(line, hx) + div_close = True + + # Save new article with div and no empty lines + #--------------------------------------------- + if not article_tmp: article_tmp = line + else: article_tmp = "%s\n%s"%(article_tmp, line) - if indiv: - article_temp = '%s\n
'%article_temp + # Close last div (if any) + if div_close: + article_tmp = "%s\n"%(article_tmp) - # Replace article with new contents - post_bottom = article_temp - + post_bottom = article_tmp + #==============================================# # Convert raw file to HTML with
 +  #
diff --git a/src/var/lib/tyto/translations/logs_en.py b/src/var/lib/tyto/translations/logs_en.py
index 584b4d0..cd6e01d 100644
--- a/src/var/lib/tyto/translations/logs_en.py
+++ b/src/var/lib/tyto/translations/logs_en.py
@@ -41,14 +41,18 @@ file_n   = "File changed"
 file_e   = "File exists"
 dir_c    = "Directory created"
 dir_e    = "Directory exists"
+post_exists = "Ab article already exists"
 
 # chk
+shebang_r= 'Remove shebang'
 nycheck  = "Article not yet checked"
 was_chk  = "Article was 'check'"
 st_chk_o = "Old 'check' status"
 post_inc = "Unused in article"
 post_inv = "Article not valid"
 post_val = "Article is valid"
+title_no = "Empty title"
+unused_p = "Empty article"
 
 # wip
 nywip    = "Article not yet wip"
@@ -71,7 +75,6 @@ post_chg = "Article changed: 'check' it first"
 sep_inv  = "Unused separator in article"
 unused_v = "Unused value in article"
 unused_t = "Unused value in header"
-unused_p = "Empty article"
 mark_np  = "Not paired marks"
 symb_np  = "Not paired symbols"
 snpic_d  = "Using default snpic. Not found"
diff --git a/src/var/lib/tyto/translations/logs_fr.py b/src/var/lib/tyto/translations/logs_fr.py
index ec65f5c..28f18f0 100644
--- a/src/var/lib/tyto/translations/logs_fr.py
+++ b/src/var/lib/tyto/translations/logs_fr.py
@@ -40,14 +40,18 @@ file_n   = "Fichier modifié"
 file_e   = "Fichier présent"
 dir_c    = "Dossier créé"
 dir_e    = "Dossier présent"
+post_exists = "Un article existe déjà"
 
 # chk
+shebang_r= 'Enlever le shebang'
 nycheck  = "Article pas encore 'check'"
 was_chk  = "Article déjà vérifié"
 st_chk_o = "Statut 'check' Ancien"
 post_inc = "Donnée manquante dans l'article"
 post_inv = "Article non valide"
 post_val = "Article valide"
+title_no = "Titre vide"
+unused_p = "Article vide"
 
 # Wip
 nywip    = "Article pas encore 'wip'"
@@ -72,7 +76,6 @@ post_chg = "Article modifié : commencer par 'check'"
 sep_inv  = "Séparateur manquant dans l'article"
 unused_v = "Valeur manquante dans l'article"
 unused_t = "Valeur manquante dans l'entête"
-unused_p = "L'article est vide"
 mark_np  = "Marqueurs non jumelés"
 symb_np  = "Symboles non jumelés"
 snpic_d  = "snpic utilisé par défaut. Manquant"
diff --git a/src/var/lib/tyto/translations/site_en.py b/src/var/lib/tyto/translations/site_en.py
index 113d9a6..e488d86 100644
--- a/src/var/lib/tyto/translations/site_en.py
+++ b/src/var/lib/tyto/translations/site_en.py
@@ -73,6 +73,7 @@ generator   = "Generator"
 # Check
 check_a     = "Check again this article"
 post_chg    = "Article was edited. Check it"
+new_post    = "Create article"
 
 # Wip
 wip_new     = "Create a new HTML page in 'wip' server again"
diff --git a/src/var/lib/tyto/translations/site_fr.py b/src/var/lib/tyto/translations/site_fr.py
index db92258..dcb8b0b 100644
--- a/src/var/lib/tyto/translations/site_fr.py
+++ b/src/var/lib/tyto/translations/site_fr.py
@@ -73,6 +73,7 @@ generator   = "Generateur"
 # Check
 check_a     = "Vérifier encore l'article"
 post_chg    = "Article édité. Le vérifier"
+new_post    = "Créer l'article"
 
 # Wip
 wip_new     = "Créer encore une page HTML dans le serveur 'wip'"