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", name = "gemwriter",
exec_name = "gemwriter", exec_name = "gemwriter",
version = "0.3", version = "0.3",
last_updated = "2022-08-05", last_updated = "2022-08-06",
} }
env.defaults = { env.defaults = {
@ -18,6 +18,7 @@ env.defaults = {
page = "page.gmi", page = "page.gmi",
post = "post.gmi", post = "post.gmi",
}, },
lang_opts = { "en_US", "fr_FR" },
post_slug = "untitled", post_slug = "untitled",
post_slug_date_format = "%Y-%m-%d", post_slug_date_format = "%Y-%m-%d",
-- Atom feed date format -- Atom feed date format
@ -27,13 +28,24 @@ env.defaults = {
page_slug = os.date("%Y%m%d-%H%M%S"), 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 -- Configurable settings
env.toml_vars = { env.toml_vars = {
general = "general", general = "general",
capsules = "capsules", capsules = "capsules",
} }
env.general = { env.general = {
app_lang = "en", app_lang = "en_US",
main_capsule = "main", main_capsule = "main",
} }
env.capsules = { env.capsules = {

View File

@ -1,5 +1,5 @@
local env = require("env") local env = require("env")
local lang = require("lang.en") local lang = require("lang.en_US")
local util = require("util") local util = require("util")
@ -8,11 +8,32 @@ writer.docs = {}
writer.conf = {} writer.conf = {}
writer.posts = {} 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 = [[ writer.docs.gen_config = [[
Generate a default config directory. Generate a default config directory.
]] ]]
function writer.gen_config() function writer.gen_config(lang_pref)
writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir) -- 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) util.make_dir(writer.conf.config_dir)
for name, file in pairs(env.defaults.config_files) do for name, file in pairs(env.defaults.config_files) do
-- Check each file individually anyway to avoid overwriting existing -- Check each file individually anyway to avoid overwriting existing
@ -106,7 +127,9 @@ function writer.parse_config(config_file)
end end
-- Set lang -- Set lang
if util.module_exists("lang." .. writer.conf.app_lang) then
lang = require("lang." .. writer.conf.app_lang) lang = require("lang." .. writer.conf.app_lang)
end
-- Custom templates override lang defaults (ignore config) -- Custom templates override lang defaults (ignore config)
for name, file in pairs(env.defaults.config_files) do for name, file in pairs(env.defaults.config_files) do
@ -119,24 +142,9 @@ end
writer.docs.load_config = [[ writer.docs.load_config = [[
Check whether there is an existing config and load it if found. Exit if a Load an existing config. Exit if a capsule is not found.
capsule is not found.
]] ]]
function writer.load_config(cap_id) 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
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 -- Check capsule id exists, abort if no valid capsule found
if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then
writer.conf.cap_id = env.general.main_capsule writer.conf.cap_id = env.general.main_capsule
@ -145,14 +153,12 @@ function writer.load_config(cap_id)
end end
local cap_label = "%[" .. env.toml_vars.capsules .. "%." .. local cap_label = "%[" .. env.toml_vars.capsules .. "%." ..
tostring(cap_id) .. "%]" tostring(cap_id) .. "%]"
if (string.find(config_file, cap_label) == nil) and if (string.find(writer.conf.config_file, cap_label) == nil) and
(writer.conf.cap_id ~= env.general.main_capsule) then (writer.conf.cap_id ~= env.general.main_capsule) then
print(lang.errs.invalid_cap_id) print(lang.errs.invalid_cap_id)
os.exit() os.exit()
end end
writer.parse_config(writer.conf.config_file)
writer.parse_config(config_file)
end
end end
@ -348,40 +354,58 @@ cli.docs.handle_args = [[
any additional arguments to the functions. any additional arguments to the functions.
]] ]]
function cli.handle_args(args) function cli.handle_args(args)
if (args[1] ~= lang.opts.help) and (args[1] ~= lang.opts.version) then 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]) writer.load_config(args[2])
end 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() writer.posts = writer.get_posts_meta()
if writer.conf.cap.gen_index_page then writer.gen_index_page() end 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 if writer.conf.cap.gen_atom_feed then writer.gen_atom_feed() end
end end
if args[1] == lang.opts.config then if args[1] == env.cli_opts.config then
writer.load_config() writer.gen_config(args[2])
print(lang.msgs.load_config .. writer.conf.config_dir) 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 = "" local file_name = ""
if args[3] ~= nil then if args[3] ~= nil then
file_name = writer.add_gemtext(args[3], args[1]) file_name = writer.add_gemtext(args[3], args[1])
else else
file_name = writer.add_gemtext(args[2], args[1]) file_name = writer.add_gemtext(args[2], args[1])
end 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) print(lang.msgs.index)
elseif args[1] == lang.opts.publish then elseif args[1] == env.cli_opts.publish then
writer.publish() writer.publish()
print(lang.msgs.publish) print(lang.msgs.publish)
elseif args[1] == lang.opts.help then elseif args[1] == env.cli_opts.help then
print(env.app.exec_name .. lang.msgs.help) 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 .. print(env.app.name .. " " .. env.app.version ..
" (" .. env.app.last_updated .. ")") " (" .. env.app.last_updated .. ")")
end 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 ## Build
- Install Lua and [luastatic]. - Install Lua (including development libraries and headers) and [luastatic].
- Clone this repository and change into the directory. Run: - Clone this repository and change into the directory. Run:
``` ```
luastatic gemwriter.lua env.lua util.lua lang/en.lua \ luastatic gemwriter.lua env.lua util.lua lang/*.lua \
/usr/lib/liblua.so -I/usr/include -o gemwriter /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. Replace `[arch]` with the OS architecture, e.g. `x86_64-linux-gnu`, and
`/usr/include/lua.h`) may need to be adjusted for your distribution. `[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`. - Move the `gemwriter` executable to a location in your `$PATH`.
@ -52,6 +55,13 @@ version Print version info
4. Publish your capsule: `gemwriter publish` 4. Publish your capsule: `gemwriter publish`
## Credits
Special thanks to the following contributors:
- [lucidiot](https://tilde.town/~lucidiot/) — French translation
## License ## License
BSD-3-Clause BSD-3-Clause

View File

@ -139,6 +139,16 @@ function util.extract_file_date(dir, file)
end 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) function util.read_file(file)
local fh = io.open(file, "r") local fh = io.open(file, "r")
local text = "" local text = ""