Update lang files and readme

- Revise lang files: use `string.format()`, clean up template and
  message placeholders, include comments for translated lines
  (thanks to lucidiot for FR translation and suggestions)
- Clarify dependencies in build instructions
0.3
mio 2022-08-07 00:14:10 +00:00
parent 9450de7fc3
commit 4e1afaa621
8 changed files with 427 additions and 356 deletions

16
env.lua
View File

@ -5,7 +5,7 @@ env.app = {
name = "gemwriter",
exec_name = "gemwriter",
version = "0.3",
last_updated = "2022-08-05",
last_updated = "2022-08-06",
}
env.defaults = {
@ -18,6 +18,7 @@ env.defaults = {
page = "page.gmi",
post = "post.gmi",
},
lang_opts = { "en_US", "fr_FR" },
post_slug = "untitled",
post_slug_date_format = "%Y-%m-%d",
-- Atom feed date format
@ -27,13 +28,24 @@ env.defaults = {
page_slug = os.date("%Y%m%d-%H%M%S"),
}
-- Command-line keywords
env.cli_opts = {
config = "config",
page = "page",
post = "post",
index = "index",
publish = "publish",
help = "help",
version = "version",
}
-- Configurable settings
env.toml_vars = {
general = "general",
capsules = "capsules",
}
env.general = {
app_lang = "en",
app_lang = "en_US",
main_capsule = "main",
}
env.capsules = {

View File

@ -1,5 +1,5 @@
local env = require("env")
local lang = require("lang.en")
local lang = require("lang.en_US")
local util = require("util")
@ -8,11 +8,32 @@ writer.docs = {}
writer.conf = {}
writer.posts = {}
writer.docs.get_lang_opts = [[
Return a table of available language translations for the app interface.
]]
function writer.get_lang_opts()
local lang_opts = {}
for l = 1, #env.defaults.lang_opts do
if util.module_exists("lang." .. env.defaults.lang_opts[l]) then
table.insert(lang_opts, env.defaults.lang_opts[l])
end
end
return lang_opts
end
writer.docs.gen_config = [[
Generate a default config directory.
]]
function writer.gen_config()
writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
function writer.gen_config(lang_pref)
-- Set lang
if lang_pref ~= nil and util.module_exists("lang." .. lang_pref) then
lang = require("lang." .. lang_pref)
env.defaults_toml = string.gsub(env.defaults_toml, env.general.app_lang,
lang_pref)
end
util.make_dir(writer.conf.config_dir)
for name, file in pairs(env.defaults.config_files) do
-- Check each file individually anyway to avoid overwriting existing
@ -106,7 +127,9 @@ function writer.parse_config(config_file)
end
-- Set lang
lang = require("lang." .. writer.conf.app_lang)
if util.module_exists("lang." .. writer.conf.app_lang) then
lang = require("lang." .. writer.conf.app_lang)
end
-- Custom templates override lang defaults (ignore config)
for name, file in pairs(env.defaults.config_files) do
@ -119,40 +142,23 @@ end
writer.docs.load_config = [[
Check whether there is an existing config and load it if found. Exit if a
capsule is not found.
Load an existing config. Exit if a capsule is not found.
]]
function writer.load_config(cap_id)
writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
local config_file = util.read_file(writer.conf.config_dir .. "/" ..
env.defaults.config_files.config)
local config_new = false
-- No config found
if (config_file == "") and (not config_new) then
writer.gen_config()
config_new = true
-- Check capsule id exists, abort if no valid capsule found
if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then
writer.conf.cap_id = env.general.main_capsule
else
writer.conf.cap_id = cap_id
end
-- Config found, read the configuration and load the values to an
-- associative table, writer.conf.
if (config_file ~= "") and (not config_new) then
-- Check capsule id exists, abort if no valid capsule found
if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then
writer.conf.cap_id = env.general.main_capsule
else
writer.conf.cap_id = cap_id
end
local cap_label = "%[" .. env.toml_vars.capsules .. "%." ..
tostring(cap_id) .. "%]"
if (string.find(config_file, cap_label) == nil) and
(writer.conf.cap_id ~= env.general.main_capsule) then
print(lang.errs.invalid_cap_id)
os.exit()
end
writer.parse_config(config_file)
local cap_label = "%[" .. env.toml_vars.capsules .. "%." ..
tostring(cap_id) .. "%]"
if (string.find(writer.conf.config_file, cap_label) == nil) and
(writer.conf.cap_id ~= env.general.main_capsule) then
print(lang.errs.invalid_cap_id)
os.exit()
end
writer.parse_config(writer.conf.config_file)
end
@ -348,40 +354,58 @@ cli.docs.handle_args = [[
any additional arguments to the functions.
]]
function cli.handle_args(args)
if (args[1] ~= lang.opts.help) and (args[1] ~= lang.opts.version) then
writer.load_config(args[2])
if (args[1] ~= env.cli_opts.help) or (args[1] ~= env.cli_opts.version) then
local config_new = false
writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
writer.conf.config_file = util.read_file(writer.conf.config_dir .. "/" ..
env.defaults.config_files.config)
-- No config found
if (writer.conf.config_file == "") and (not config_new) then
writer.gen_config(args[2])
config_new = true
end
-- Config found, read the configuration and load the values to an
-- associative table, writer.conf.
if (writer.conf.config_file ~= "") and (not config_new) then
writer.load_config(args[2])
end
end
if (args[1] == lang.opts.index) or (args[1] == lang.opts.publish) then
if (args[1] == env.cli_opts.index) or (args[1] == env.cli_opts.publish) then
writer.posts = writer.get_posts_meta()
if writer.conf.cap.gen_index_page then writer.gen_index_page() end
if writer.conf.cap.gen_atom_feed then writer.gen_atom_feed() end
end
if args[1] == lang.opts.config then
writer.load_config()
print(lang.msgs.load_config .. writer.conf.config_dir)
if args[1] == env.cli_opts.config then
writer.gen_config(args[2])
print(string.format(lang.msgs.load_config, writer.conf.config_dir))
elseif (args[1] == lang.opts.post) or (args[1] == lang.opts.page) then
elseif (args[1] == env.cli_opts.post) or (args[1] == env.cli_opts.page) then
local file_name = ""
if args[3] ~= nil then
file_name = writer.add_gemtext(args[3], args[1])
else
file_name = writer.add_gemtext(args[2], args[1])
end
print(lang.msgs.add_gemtext .. file_name)
print(string.format(lang.msgs.add_gemtext, file_name))
elseif args[1] == lang.opts.index then
elseif args[1] == env.cli_opts.index then
print(lang.msgs.index)
elseif args[1] == lang.opts.publish then
elseif args[1] == env.cli_opts.publish then
writer.publish()
print(lang.msgs.publish)
elseif args[1] == lang.opts.help then
print(env.app.exec_name .. lang.msgs.help)
elseif args[1] == env.cli_opts.help then
print(string.format(lang.msgs.help_usage, env.app.exec_name) ..
string.format(lang.msgs.help_opts, env.cli_opts.config,
env.cli_opts.page, env.cli_opts.post, env.cli_opts.index,
env.cli_opts.publish, env.cli_opts.help, env.cli_opts.version) ..
string.format(lang.msgs.help_lang_opts,
table.concat(writer.get_lang_opts(), ", ")))
elseif args[1] == lang.opts.version then
elseif args[1] == env.cli_opts.version then
print(env.app.name .. " " .. env.app.version ..
" (" .. env.app.last_updated .. ")")
end

View File

@ -1,145 +0,0 @@
local en = {}
-- Templates
en.tpl_vars = {
post = {
author = "{{ post_author }}",
content = "{{ post_content }}",
date = "{{ post_date }}",
summary = "{{ post_summary }}",
tags = "{{ post_tags }}",
title = "{{ post_title }}",
url = "{{ post_url }}",
},
feed = {
date = "{{ feed_date }}",
url = "{{ feed_url }}",
},
index = {
posts = "{{ posts }}",
},
log = {
author = "{{ log_author }}",
subtitle = "{{ log_subtitle }}",
title = "{{ log_title }}",
log_url = "{{ log_url }}",
},
}
en.atom_header = [[<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ log_title }}</title>
<subtitle>{{ log_subtitle }}</subtitle>
<updated>{{ feed_date }}</updated>
<author>
<name>{{ log_author }}</name>
</author>
<id>{{ feed_url }}</id>
<link href="{{ log_url }}" rel="alternate"/>
<link href="{{ feed_url }}" rel="self" type="application/atom+xml"/>
]]
en.atom_entry = [[
<entry>
<id>{{ post_url }}</id>
<title>
<![CDATA[{{ post_title }}]] .. "]]>" .. [[
</title>
<updated>{{ post_date }}</updated>
<author>
<name>{{ post_author }}</name>
</author>
<link href="{{ post_url }}" rel="alternate"/>
<summary>
<![CDATA[{{ post_summary }}]] .. "]]>" .. [[
</summary>
<content>
<![CDATA[{{ post_content }}]] .. "]]>" .. [[
</content>
</entry>
]]
en.atom_footer = [[</feed>]]
en.post = [[---
date: {{ post_date }}
---
# {{ post_title }}
## Links
=> gemini:// link
=> gemini:// link (img)
=> https:// link (https)]]
en.index = [[
# {{ log_title }}
{{ posts }}
]]
en.page = [[
# {{ post_title }}
## Heading 2
### Heading 3
List:
*
*
*
```
Preformatted text
```
## Links
=> gemini:// link
=> gemini:// link (img)
=> https:// link (https)]]
-- App command options and messages output
en.opts = {
config = "config",
page = "page",
post = "post",
index = "index",
publish = "publish",
help = "help",
version = "version",
}
en.msgs = {
add_gemtext = "Created ",
help = [[ [options] [capsule] [title]
Options:
config Generate a config directory
page [capsule] [title] Add a new page with the given title
post [capsule] [title] Add a new gemlog post with the given title
index Generate an index page and feed of posts
publish Index and copy posts remotely using scp
help Show this help message
version Print version info]],
index = "Created index page and feed.",
load_config = [[Created config files. Please edit them with the correct
details before proceeding. The config files can be found at:
]],
publish = "Published capsule.",
}
en.errs = {
invalid_cap_id = "Error: unknown capsule id.",
}
return en

157
lang/en_US.lua 100644
View File

@ -0,0 +1,157 @@
local en = {}
-- Templates
en.tpl_vars = {
post = {
author = "{{ author }}",
content = "{{ content }}",
date = "{{ date }}",
summary = "{{ summary }}",
-- This is not used in the templates yet.
tags = "{{ tags }}",
title = "{{ title }}",
url = "{{ url }}",
},
feed = {
date = "{{ date }}",
url = "{{ feed_url }}",
},
index = {
posts = "{{ posts }}",
},
log = {
author = "{{ author }}",
subtitle = "{{ subtitle }}",
title = "{{ title }}",
url = "{{ log_url }}",
},
}
en.atom_header = [[<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ title }}</title>
<subtitle>{{ subtitle }}</subtitle>
<updated>{{ date }}</updated>
<author>
<name>{{ author }}</name>
</author>
<id>{{ feed_url }}</id>
<link href="{{ log_url }}" rel="alternate"/>
<link href="{{ feed_url }}" rel="self" type="application/atom+xml"/>
]]
en.atom_entry = [[
<entry>
<id>{{ url }}</id>
<title>
<![CDATA[{{ title }}]] .. "]]>" .. [[
</title>
<updated>{{ date }}</updated>
<author>
<name>{{ author }}</name>
</author>
<link href="{{ url }}" rel="alternate"/>
<summary>
<![CDATA[{{ summary }}]] .. "]]>" .. [[
</summary>
<content>
<![CDATA[{{ content }}]] .. "]]>" .. [[
</content>
</entry>
]]
en.atom_footer = [[</feed>]]
en.post = [[---
date: {{ date }}
---
# {{ title }}
## Links
=> gemini:// link
=> gemini:// link (img)
=> https:// link (https)]]
en.index = [[
# {{ title }}
{{ posts }}
]]
en.page = [[
# {{ title }}
## Heading 2
### Heading 3
List:
*
*
*
```
Preformatted text
```
## Links
=> gemini:// link
=> gemini:// link (img)
=> https:// link (https)]]
en.msgs = {}
-- %s: gemtext filename
en.msgs.add_gemtext = "Created %s."
-- %s: app executable name
en.msgs.help_usage = [[%s [options] [capsule] [title]
]]
-- %s: command-line options
-- (see env.lua env.cli_opts for the full list)
en.msgs.help_opts = [[
Options:
%s [lang] Generate a config directory
%s [capsule] [title] Add a new page with the given title
%s [capsule] [title] Add a new gemlog post with the given title
%s Generate an index page and feed of posts
%s Index and copy posts remotely using scp
%s Show this help message
%s Print version info
]]
-- %s: list of language codes
en.msgs.help_lang_opts = [[
Config language options:
%s]]
en.msgs.index = "Created index page and feed."
-- %s: config directory path
en.msgs.load_config = [[Created config files. Please edit them with the correct
details before proceeding. The config files can be found at:
%s]]
en.msgs.publish = "Published capsule."
en.errs = {
invalid_cap_id = "Error: unknown capsule id.",
}
return en

View File

@ -1,157 +0,0 @@
local fr = {}
-- Templates
fr.tpl_vars = {
post = {
-- It might not be really necessary to mention post/feed/log each time
-- since those occur in different templates?
-- This would make the french translated tags much cleaner, because the
-- proper way to do this translation would be "auteur_de_l'article"
-- (author_of_the_post).
author = "{{ auteur_article }}",
content = "{{ contenu_article }}",
date = "{{ date_article }}",
summary = "{{ résumé_article }}",
-- This is not used in the templates.
tags = "{{ tags_article }}",
title = "{{ titre_article }}",
url = "{{ url_article }}",
},
feed = {
date = "{{ date_flux }}",
url = "{{ url_flux }}",
},
index = {
posts = "{{ articles }}",
},
log = {
-- The concept of a "gemlog" does not have a French translation
-- as far as I know, so I used 'capsule' instead of 'log'.
author = "{{ auteur_capsule }}",
subtitle = "{{ sous_titre_capsule }}",
title = "{{ titre_capsule }}",
log_url = "{{ url_capsule }}",
},
}
fr.atom_header = [[<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ titre_capsule }}</title>
<subtitle>{{ sous_titre_capsule }}</subtitle>
<updated>{{ date_flux }}</updated>
<author>
<name>{{ auteur_capsule }}</name>
</author>
<id>{{ url_flux }}</id>
<link href="{{ url_capsule }}" rel="alternate"/>
<link href="{{ url_flux }}" rel="self" type="application/atom+xml"/>
]]
fr.atom_entry = [[
<entry>
<id>{{ url_article }}</id>
<title>
<![CDATA[{{ titre_article }}]] .. "]]>" .. [[
</title>
<updated>{{ date_article }}</updated>
<author>
<name>{{ auteur_article }}</name>
</author>
<link href="{{ url_article }}" rel="alternate"/>
<summary>
<![CDATA[{{ résumé_article }}]] .. "]]>" .. [[
</summary>
<content>
<![CDATA[{{ contenu_article }}]] .. "]]>" .. [[
</content>
</entry>
]]
fr.atom_footer = [[</feed>]]
fr.post = [[---
date: {{ date_article }}
---
# {{ titre_article }}
## Liens
=> gemini:// lien
=> gemini:// lien (image)
=> https:// lien (https)]]
fr.index = [[
# {{ titre_capsule }}
{{ articles }}
]]
fr.page = [[
# {{ titre_article }}
## Titre de niveau 2
### Titre de niveau 3
Liste :
*
*
*
```
Texte préformaté
```
## Liens
=> gemini:// lien
=> gemini:// lien (image)
=> https:// lien (https)]]
-- App command options and messages output
-- These probably should always stay untranslated, because having a CLI's
-- entire command line change depending on the language would be a mess.
-- Imagine writing bash in french…
fr.opts = {
config = "config",
page = "page",
post = "post",
index = "index",
publish = "publish",
help = "help",
version = "version",
}
fr.msgs = {
add_gemtext = "Créé ",
help = [[ [options] [capsule] [titre]
Options :
config Générer un dossier de configuration
page [capsule] [titre] Ajouter une nouvelle page avec le titre fourni
post [capsule] [titre] Ajouter un nouvel article avec le titre fourni
index Générer une page d'accueil et un flux d'articles
publish Générer les pages puis les publier via scp
help Afficher ce message d'aide
version Afficher les informations de version]],
index = "Flux et page d'accueil créés.",
load_config = [[Les fichiers de configuration ont été créés. Veuillez y
inscrire les valeurs correctes avant de continuer.
Les fichiers peuvent être trouvés dans :
]],
publish = "Capsule publiée.",
}
fr.errs = {
invalid_cap_id = "Erreur: Identifiant de capsule inconnu.",
}
return fr

160
lang/fr_FR.lua 100644
View File

@ -0,0 +1,160 @@
local fr = {}
-- Templates
fr.tpl_vars = {
post = {
author = "{{ auteur }}",
content = "{{ contenu }}",
date = "{{ date }}",
summary = "{{ résumé }}",
-- This is not used in the templates yet.
tags = "{{ tags }}",
title = "{{ titre }}",
url = "{{ url }}",
},
feed = {
date = "{{ date }}",
url = "{{ url_flux }}",
},
index = {
posts = "{{ articles }}",
},
log = {
-- The concept of a "gemlog" does not have a French translation,
-- the closest term after some discussion might be "journal".
author = "{{ auteur }}",
subtitle = "{{ sous_titre }}",
title = "{{ titre }}",
log_url = "{{ url_journal }}",
},
}
fr.atom_header = [[<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ titre }}</title>
<subtitle>{{ sous_titre }}</subtitle>
<updated>{{ date }}</updated>
<author>
<name>{{ auteur }}</name>
</author>
<id>{{ url_flux }}</id>
<link href="{{ url_journal }}" rel="alternate"/>
<link href="{{ url_flux }}" rel="self" type="application/atom+xml"/>
]]
fr.atom_entry = [[
<entry>
<id>{{ url }}</id>
<title>
<![CDATA[{{ titre }}]] .. "]]>" .. [[
</title>
<updated>{{ date }}</updated>
<author>
<name>{{ auteur }}</name>
</author>
<link href="{{ url }}" rel="alternate"/>
<summary>
<![CDATA[{{ résumé }}]] .. "]]>" .. [[
</summary>
<content>
<![CDATA[{{ contenu }}]] .. "]]>" .. [[
</content>
</entry>
]]
fr.atom_footer = [[</feed>]]
fr.post = [[---
date: {{ date }}
---
# {{ titre }}
## Liens
=> gemini:// lien
=> gemini:// lien (image)
=> https:// lien (https)]]
fr.index = [[
# {{ titre }}
{{ articles }}
]]
fr.page = [[
# {{ titre }}
## Titre de niveau 2
### Titre de niveau 3
Liste :
*
*
*
```
Texte préformaté
```
## Liens
=> gemini:// lien
=> gemini:// lien (image)
=> https:// lien (https)]]
fr.msgs = {}
-- %s: gemtext filename
fr.msgs.add_gemtext = "%s créé."
-- %s: app executable name
fr.msgs.help_usage = [[%s [options] [capsule] [titre]
]]
-- %s: command-line options
-- (see env.lua env.cli_opts for the full list)
fr.msgs.help_opts = [[
Options :
%s [lang] Générer un dossier de configuration
%s [capsule] [titre] Ajouter une nouvelle page avec le titre fourni
%s [capsule] [titre] Ajouter un nouvel article avec le titre fourni
%s Générer une page d'accueil et un flux d'articles
%s Générer les pages puis les publier via scp
%s Afficher ce message d'aide
%s Afficher les informations de version
]]
-- %s: list of language codes
fr.msgs.help_lang_opts = [[
Langues disponibles :
%s]]
fr.msgs.index = "Flux et page d'accueil créés."
-- %s: config directory path
fr.msgs.load_config = [[Les fichiers de configuration ont été créés. Veuillez y
inscrire les valeurs correctes avant de continuer.
Les fichiers peuvent être trouvés dans :
%s]]
fr.msgs.publish = "Capsule publiée."
fr.errs = {
invalid_cap_id = "Erreur: Identifiant de capsule inconnu.",
}
return fr

View File

@ -25,17 +25,20 @@ version Print version info
## Build
- Install Lua and [luastatic].
- Install Lua (including development libraries and headers) and [luastatic].
- Clone this repository and change into the directory. Run:
```
luastatic gemwriter.lua env.lua util.lua lang/en.lua \
/usr/lib/liblua.so -I/usr/include -o gemwriter
luastatic gemwriter.lua env.lua util.lua lang/*.lua \
/usr/lib/[arch]/liblua[5.x].a -I/usr/include/lua[5.x] -static \
-o gemwriter
```
The paths to `liblua.so` and the development headers (i.e.
`/usr/include/lua.h`) may need to be adjusted for your distribution.
Replace `[arch]` with the OS architecture, e.g. `x86_64-linux-gnu`, and
`[5.x]` with the Lua version. The paths to `liblua.a` and the development
headers (i.e. `/usr/include/lua[5.x]/lua.h`) may need to be adjusted for
your distribution.
- Move the `gemwriter` executable to a location in your `$PATH`.
@ -52,6 +55,13 @@ version Print version info
4. Publish your capsule: `gemwriter publish`
## Credits
Special thanks to the following contributors:
- [lucidiot](https://tilde.town/~lucidiot/) — French translation
## License
BSD-3-Clause

View File

@ -139,6 +139,16 @@ function util.extract_file_date(dir, file)
end
function util.module_exists(mod)
local check, err = pcall(require, mod)
if check then do
return true end
else
return false
end
end
function util.read_file(file)
local fh = io.open(file, "r")
local text = ""