meso-web

Sources of the |Méso|Star> website
git clone git://git.meso-star.fr/meso-web.git
Log | Files | Refs | README | LICENSE

commit b9123708f07d2b6278425a22d9ded3529c1f5114
parent 3a0016f5e8be9b065722e59be7740d3790d9a3fd
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 20 Aug 2025 19:21:08 +0200

Add multilingual support to the generate_header program

Add support for content translated into multiple languages so that
indexed content provides links to its translations.

The index format is updated to provide the @LANG@ macro to the URI,
which is replaced by one of the languages listed in the third field
under a character string where languages are separated by colons (:).
Thus, the URI becomes language-generic. The first language listed for
multilingual content is the default language, i.e. the default content
displayed once this entry is selected.

Diffstat:
Mgenerate_header.sh | 173++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 118 insertions(+), 55 deletions(-)

diff --git a/generate_header.sh b/generate_header.sh @@ -27,42 +27,72 @@ absdir() # file cd "${OLDPWD}" } +# Delete irrelevant data +strip_dummy() # stdin +{ + # - Remove comments + # - Remove empty lines + # - Remove heading an trailing spaces + sed 's/#.\{0,\}$//g' \ + | sed '/^[[:space:]]\{0,\}$/d' \ + | sed -e 's/^[[:space:]]\{1,\}//g' -e 's/[[:space:]]\{1,\}$//g' +} + +# List the indexed content in TSV format. Each line contains the index +# label, followed by the indexed content, and possibly an indicator of +# the language used. +# +# The input data is the content of an index.tsv file, submitted on +# standard input. +indexed_content() +( + strip_dummy | while read -r _line; do + _langs="$(echo "${_line}" | cut -d' ' -f3)" + + if [ -z "${_langs}" ]; then + # There is no lang field defined: print the line as it + printf '%s\n' "${_line}" + + else + # The lang field is defined. Duplicate the line as many times as + # there are languages listed. Define the content URI by replacing + # the @LANG@ string with the language value. + echo "${_langs}" \ + | sed 's/:/\n/g' \ + | while read -r _translation; do + echo "${_line}" \ + | sed -e "s/\t[^\t]\{1,\}$/\t${_translation}/g" \ + -e "s/@LANG@/${_translation}/g" + done + fi + done +) + # Print relative path from input file to worktree relpath_to_worktree() # file -{ +( # Build directory from worktree to path - dir__="$(absdir "$1")" - dir__="$(printf '%s\n' "${dir__}" | sed "s;^${worktree}[/]\{0,\};;g")" + _dir="$(absdir "$1")" + _dir="$(printf '%s\n' "${_dir}" | sed "s;^${worktree}[/]\{0,\};;g")" # Ensure that the directory if a subpath of the worktree - if ! [ -d "${dir__}" ]; then + if ! [ -d "${_dir}" ]; then return 1 fi - printf '%s\n' "${dir__}" | sed 's/\//\n/g' | while read -r i; do + echo "${_dir}" | sed 's/\//\n/g' | while read -r _i; do printf "../" done -} - -# Delete irrelevant data -strip_dummy() -{ - # - Remove comments - # - Remove empty lines - # - Remove heading an trailing spaces - sed 's/#.\{0,\}$//g' \ - | sed '/^[[:space:]]\{0,\}$/d' \ - | sed -e 's/^[[:space:]]\{1,\}//g' -e 's/[[:space:]]\{1,\}$//g' -} +) print_head() { printf '<!DOCTYPE html>\n' - printf '<html lang="%s">\n' "${lang:-en}" + printf '<html lang="%s">\n' "${lang}" printf '<head>\n' printf ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n' printf ' <meta name="viewport" content="width=device-width, initial-scale=1">\n' - printf ' <title>|M|S> %s</title>\n' "${section}" + printf ' <title>|M|S> %s</title>\n' "${label}" printf ' <link rel="stylesheet" title="default" href="%smeso.css">\n' "${root}" printf '</head>\n' printf '<body>\n' @@ -70,72 +100,102 @@ print_head() # Print top-level menu print_menu1() -{ +( printf '<div id="menu">\n' - n__="0" - - strip_dummy < "${worktree}/menu.tsv" | while read -r i; do + _separator="" + strip_dummy < "${worktree}/menu.tsv" | while read -r _i; do # Retrieve the menu label and its associated directory - label__="$(echo "${i}" | cut -d' ' -f1)" - directory__="$(echo "${i}" | cut -d' ' -f2)" + _label="$(echo "${_i}" | cut -d' ' -f1)" + _directory="$(echo "${_i}" | cut -d' ' -f2)" - if [ "${n__}" -gt 0 ]; then - printf ' &emsp13;|&emsp13;\n' - fi - n__="$((n__+1))" + echo "${_separator}" && _separator=' &emsp13;|&emsp13;\n' - if [ "${directory__}" = "${section}" ]; then - printf ' %s\n' "${label__}" + if [ "${_directory}" = "${section}" ]; then + printf ' %s\n' "${_label}" else # Get the default page of the section, i.e., the first entry in # its index. The menu is a link to it. - index__="$(strip_dummy < "${worktree}/${directory__}/index.tsv" \ + _uri="$(indexed_content < "${worktree}/${_directory}/index.tsv" \ | head -1 | cut -d' ' -f2)" + printf ' <a href="%s%s/%s">%s</a>\n' \ - "${root}" "${directory__}" "${index__}" "${label__}" + "${root}" "${_directory}" "${_uri}" "${_label}" fi done printf '</div>\n<hr>\n' -} +) + +# Print the list of translation choices +# Available langs are submitted on standard input +print_translations() +( + _uri_template="$1" + + printf ' <span style="float: right;">\n' + + _separator="" + while read -r _lang; do + + _translation="$(echo "${_uri_template}" | sed "s/@LANG@/${_lang}/g")" + + echo "${_separator}" && _separator=' &emsp13;.&emsp13;\n' + + if [ "${content}" = "${_translation}" ]; then + printf ' <span class="cur">%s</span>\n' "${_lang}" + else + printf ' <a href="%s%s/%s>%s</a>\n' \ + "${root}" "${section}" "${_translation}" "${_lang}" + fi + done + + printf ' </span>\n' +) # Print second-level menu print_menu2() -{ +( printf '<div id="sub-menu">\n' - n__="0" + _separator="" + strip_dummy < "${worktree}/${section}/index.tsv" | while read -r _i; do - strip_dummy < "${worktree}/${section}/index.tsv" | while read -r i; do - label__="$(echo "${i}" | cut -d' ' -f1)" - uri__="$(echo "${i}" | cut -d' ' -f2)" + _label="$(echo "${_i}" | cut -d' ' -f1)" + _uri_template="$(echo "${_i}" | cut -d' ' -f2)" + _langs="$(echo "${_i}" | cut -d' ' -f3)" - if [ "${n__}" -gt 0 ]; then - printf ' &emsp13;.&emsp13;\n' - fi - n__="$((n__+1))" + _uri="$(echo "${_uri_template}" | sed "s/@LANG@/${lang}/g")" + + echo "${_separator}" && _separator=' &emsp13;.&emsp13;\n' - if [ "${uri__}" = "${html}" ]; then + if [ "${_uri}" = "${content}" ]; then # This is the current web page - printf ' <span class="cur">%s</span>\n' "${label__}" + printf ' <span class="cur">%s</span>\n' "${_label}" - elif echo "${uri__}" | grep -qe "^http[s]\{0,1\}://"; then + # Print links to translations if available + if [ -n "${_langs}" ]; then + echo "${_langs}" \ + | sed 's/:/\n/g' \ + | print_translations "${_uri_template}" + fi + + elif echo "${_uri}" | grep -qe "^http[s]\{0,1\}://"; then # The entry links to an http[s] URL - printf ' <a href="%s">%s</a>\n' "${uri__}" "${label__}" + printf ' <a href="%s">%s</a>\n' "${_uri}" "${_label}" else - # The entry links to a local web page for the section. + # The entry links to a local web page for the section printf ' <a href="%s%s/%s">%s</a>\n' \ - "${root}" "${section}" "${uri__}" "${label__}" + "${root}" "${section}" "${_uri}" "${_label}" fi done printf '</div>\n' -} +) ######################################################################## # The script @@ -174,6 +234,8 @@ if [ -z "${entry}" ]; then exit 1 fi +label="$(echo "${entry}" | cut -d' ' -f1)" + if ! [ -e "${section}/index.tsv" ]; then >&2 printf \ '%s: unable to find the index.tsv file in the directory %s\n' \ @@ -183,16 +245,17 @@ fi # Define whether the entry corresponds to a subsection index, i.e., # whether it is the file to be displayed when entering the subsection. -html="$(basename "$1")" -subentry="$(strip_dummy < "${worktree}/${section}/index.tsv" \ - | sed -n "/\t${html}/p")" +content="$(basename "$1")" +subentry="$(indexed_content < "${worktree}/${section}/index.tsv" \ + | sed -n "/\t${content}/p")" +lang="en" if [ -n "${subentry}" ]; then - lang="$(echo "${subentry}" | cut -d' ' -f3)" + lang="$(echo "${subentry}" | cut -d' ' -f3 | cut -d':' -f1)" else # Indicate that the file is not accessible via the menu >&2 printf '%s: %s is not an index file of the section %s\n' \ - "${0##*/}" "${html}" "${section}" + "${0##*/}" "${content}" "${section}" fi root="$(relpath_to_worktree "$1")"