Локализация bash не будет работать с многостроковыми строками (с сильным синтаксисом или с помощью `eval`)

В bash есть хорошая функция, о локализации (перевод языка):

TEXTDOMAIN=coreutils
LANG=fr_CH.utf8
echo $"system boot"
démarrage système

( Nota:. Для этой работы уже был сгенерирован в вашей системе... Иначе вы можете попробовать создать свой собственный язык... или установить locales и сгенерировать его.)

Проблема:

Но если это отлично работает с простыми строками, когда строка содержит \n (или худшее: backtick `, вещи сложнее:

echo $"Written by %s, %s, %s,\nand %s.\n"
Written by %s, %s, %s,\nand %s.\n

Это не ответ.

( Nota2:. Для этой работы точное сообщение должно быть подготовлено в файле сообщений .mo, в этом примере/тесте я использую существующие файлы coreutils.mo, которые могут быть неформованы с помощью команда msgunfmt.)

В общем, единственный способ, которым я нашел перевод:

eval echo \$\"$'Written by %s, %s, %s,\nand %s.\n'\"
Écrit par %s, %s, %s,
et %s.

или

msg=$'Written by %s, %s, %s,\nand %s.\n'
eval echo \$\""$msg"\"
Écrit par %s, %s, %s,
et %s.

(Вы могли видеть две двойные кавычки... не очень сексуально...)

И, наконец, я мог:

WRITTERS=(Hans Pierre Jackob Heliott)
eval printf \$\""$msg"\" ${WRITTERS[@]}
Écrit par Hans, Pierre, Jackob,
et Heliott.

Но, как я недавно слышал, что eval - зло...; -)

На самом деле, у меня нет проблем с eval, которые выполняются только с жестко закодированной частью, но я был бы признателен за то, чтобы сохранить эту оценку и написать эту часть более естественным или читаемым образом.

Во всех ответах @techno дайте мне понять, что моя первая идея - это что-то опасное, если WRITTERS содержит некоторые ;ls, для образца...

Изменить: Итак, вопрос:

Как я могу сохранить этот eval и/или записать это более сексуально.

Nota:

$ printf "I use bash %s on Debian %s\n" $BASH_VERSION $(</etc/debian_version)
I use bash 4.1.5(1)-release on Debian 6.0.6

Ответы

Ответ 1

Если использование eval плохо с произвольными переменными, существует способ сделать это только при вызове/необходимом при запуске eval только в части сообщения:

function lPrintf() {
    local sFormat="$(
        eval 'echo $"'"${1}"'"'.
    )"
    shift
    printf "${sFormat%.}" [email protected]
}

lPrintf "system boot"
démarrage système

lPrintf  $'Written by %s, %s, %s,\nand %s.\n' techno moi lui-même bibi
Écrit par techno, moi, lui-même,
et bibi.

(Точка в конце переведенной строки гарантирует, что вся строка, включая ведущий разрыв строки, передается переменной sFormat. Они будут отброшены с помощью ${sFormat%.})

Ответ 2

Я немного поиграл с этой функцией, и это то, к чему я придумал: вы можете включить новую строку дословно:

$ echo $"Written by %s.
> "
Écrit par %s.
$ 

В script:

#!/bin/bash

message=$"Written by %s.
"

printf "$message" Gniourf

Этот script выведет:

Écrit par Gniourf.

Хорошо, на самом деле это не ответ, но это может немного помочь (по крайней мере, мы не используем зло eval).

Личное замечание: я считаю эту функцию очень неуклюжей!

Ответ 3

ОК. Думаю, наконец, все получилось.

iprintf() {
    msg="$2"
    domain="$1"
    shift
    shift
    imsg=$(gettext -ed "$domain" "$msg" ; echo EOF)
    imsg="${imsg%EOF}"
    printf "$imsg" "[email protected]"
}

Пример использования:

LANG=fr_CH.utf8 iprintf coreutils "If FILE is not specified, use %s.  %s as FILE is common.\n\n" foo bar

Ответ 4

Ну, есть мой ответ:

Теперь это не так хорошо реализовано. Работайте во многих ситуациях, но, пока

echo "$(gettext 'missing character class name `[::]'\')"
caractère de nom de classe « [::] » manquant

работают просто, одна и та же строка кажется невозможной для перевода с использованием этого башизма:

echo $"missing character class name `[::]'"
> 

консоль остается заблокированной (ожидая такой конец строки) добавление `" будет погружать bash в сложный процесс интерпретации: → >

> `"
bash: command substitution: line 1: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « ' » correspondant
bash: command substitution: line 2: Erreur de syntaxe : fin de fichier prématurée
missing character class name 

И, конечно же:

echo $"missing character class name \`[::]'"
missing character class name `[::]'

не делайте перевод.:-p

При переводе этой строки, содержащей два backticks работают мелко:

echo $"%s}: integer required between `{' and `}'"
%s} : entier requis entre « { » et « } »

Существует script, где вы можете увидеть некоторые мои неудачные попытки.

#!/bin/bash

echo "Localized tests"
export TEXTDOMAIN=coreutils
export LANG=fr_CH.UTF-8
export WRITTERS=(Athos Portos Aramis Dartagnan\ Le\ Beau)

echo '#First method# whitout eval'

declare -A MyMessages;
MyMessages[sysReboot]=$"system boot"
MyMessages[writtenBy]=$"Written by %s, %s, %s,
and %s.
"
MyMessages[intReq]=$"%s}: integer required between `{' and `}'"
MyMessages[trClass]=$"when translating, the only character classes that may appear in
string2 are `upper' and `lower'"
# MyMessages[missClass]=$"missing character class name `[::]'" 

for msgIdx in ${!MyMessages[@]} ;do
    printf "\n--- Test chain '%s' ---\n" $msgIdx
    case $msgIdx in
    writ* )
        printf "${MyMessages[$msgIdx]}\n" "${WRITTERS[@]}"
        ;;
    intReq )
        printf "ARRAY{${MyMessages[$msgIdx]}\n" NaN
        ;;
    * )
        printf "${MyMessages[$msgIdx]}\n"
        ;;
    esac
  done

echo $'###\n#Second method# whith limited eval'
unset MyMessages;

declare -A MyMessages;

lPrintf() {
    local sFormat="$(
        eval 'echo $"'"${1}"'"'.
    )"
    shift
    printf "${sFormat%.}" "[email protected]"
}

MyMessages[sysReboot]="system boot"
MyMessages[writtenBy]=$'Written by %s, %s, %s,\nand %s.\n'
MyMessages[intReq]="%s}: integer required between \`{' and \`}'"
MyMessages[trClass]="when translating, the only character classes that "
MyMessages[trClass]+=$'may appear in\nstring2 '
MyMessages[trClass]+="are \`upper' and \`lower'"
MyMessages[missClass]="missing character class name \`[::]'"

for msgIdx in ${!MyMessages[@]} ;do
    printf "\n--- Test chain '%s' ---\n" $msgIdx
    case $msgIdx in
    writ* )
        lPrintf "${MyMessages[$msgIdx]}" "${WRITTERS[@]}"
        ;;
    intReq )
        lPrintf "${MyMessages[$msgIdx]}" NaN
        ;;
    * )
        lPrintf "${MyMessages[$msgIdx]}"
        ;;
    esac
  done

и его вывод:

Localized tests
#First method# whitout eval

--- Test chain 'trClass' ---
à la traduction, les seules classes de caractères qui peuvent apparaître
dans string2 sont « upper » ou « lower »

--- Test chain 'intReq' ---
ARRAY{NaN} : entier requis entre « { » et « } »

--- Test chain 'sysReboot' ---
démarrage système

--- Test chain 'writtenBy' ---
Écrit par Athos, Portos, Aramis,
et Dartagnan Le Beau.

###
#Second method# whith limited eval

--- Test chain 'trClass' ---
à la traduction, les seules classes de caractères qui peuvent apparaître
dans string2 sont « upper » ou « lower »
--- Test chain 'missClass' ---
./localized.sh: eval: line 44: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « ` » correspondant
./localized.sh: eval: line 45: Erreur de syntaxe : fin de fichier prématurée

--- Test chain 'intReq' ---
NaN} : entier requis entre « { » et « } »
--- Test chain 'sysReboot' ---
démarrage système
--- Test chain 'writtenBy' ---
Écrit par Athos, Portos, Aramis,
et Dartagnan Le Beau.

Если кто-нибудь может помочь мне удалить комментарии и/или сообщение об ошибке в этом script!?... (менее 8 часов?!)

Всем, спасибо всем. (Моя щедрость пойдет на @gniourf_gniourf, если лучший ответ за 8 часов. Но благодаря @techno тоже мне нравится ваш lPrintf!)