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:
| M | generate_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 '  | \n'
- fi
- n__="$((n__+1))"
+ echo "${_separator}" && _separator='  | \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='  . \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 '  . \n'
- fi
- n__="$((n__+1))"
+ _uri="$(echo "${_uri_template}" | sed "s/@LANG@/${lang}/g")"
+
+ echo "${_separator}" && _separator='  . \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")"