12

bash 中有一个很好的特性,关于本地化(语言翻译):

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

注意:对于这项工作,fr_CH.utf8已经在您的系统上生成...否则您可以尝试使用自己的语言环境...或安装locales并生成一个。)

问题:

但是如果这适用于简单的字符串,当字符串包含 a \n(或更糟糕的是:反引号时`,事情会更复杂:

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

这不是出席的答案。

注意2:对于这项工作,必须在消息文件中准备确切.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的部分运行没有问题,但我希望有一种方法可以将这个 eval 排除在外,并以更自然可读的方式编写这种部分。

无论如何@techno的回答让我看到我的第一个想法是危险的,好像WRITTERS包含一些;ls,例如......

编辑:所以问题是:

我怎么能把它藏在eval外面和/或用更性感的方式写这个

注意:

$ 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
4

5 回答 5

6

我已经对这个功能进行了一些尝试,这就是我想出的:您可以将换行符逐字包含为:

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

在脚本中:

#!/bin/bash

message=$"Written by %s.
"

printf "$message" Gniourf

该脚本将输出:

Écrit par Gniourf.

好的,这不是一个真正的答案,但它可能会有所帮助(至少,我们没有使用 evil eval)。

个人评论:我觉得这个功能真的很笨拙!

于 2012-12-25T13:56:42.410 回答
4

如果eval对任意变量使用不好,则有一种方法可以仅在调用/需要时eval执行此操作,仅在消息部分上运行:

function lPrintf() {
    local sFormat="$(
        eval 'echo $"'"${1}"'"'.
    )"
    shift
    printf "${sFormat%.}" $@
}

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%.}

于 2012-12-27T09:05:43.323 回答
2

好吧,我想终于做对了。

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

使用示例:

LANG=fr_CH.utf8 iprintf coreutils "If FILE is not specified, use %s.  %s as FILE is common.\n\n" foo bar
于 2012-12-25T21:22:36.790 回答
1

构建翻译功能的简单解决方案:

f() {
    eval 'local msg=$"'"${1//[\"\$\`]}"\"
    shift
    printf "${msg}" "$@"
}

测试:

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

f $'Written by %s, %s, %s,\nand %s.\n' Athos Portos Aramis Shreck
Écrit par Athos, Portos, Aramis
et Shreck.

但是因为我更喜欢设置变量而不是分叉函数:

f() {
    eval 'local msg=$"'"${1//[\"\$\`]}"\"
    local -n variable=$2
    shift 2
    printf -v variable "$msg" "$@"
}

然后

f $'Written by %s, %s, %s,\nand %s.\n' string Huey Dewey Louie Batman
echo ${string@Q}
$'Écrit par Huey, Dewey, Louie\net Batman.\n'

echo "$string"
Écrit par Huey, Dewey, Louie
et Batman.

或者作为一个完整的翻译功能更好:

f() {
    local store=false OPTIND OPTARG OPTERR varname
    while getopts 'd:v:' opt ;do
        case $opt in
            d ) local TEXTDOMAIN=$OPTARG ;;
            v ) varname=$OPTARG ;;
        esac
    done
    shift $((OPTIND-1))
    eval 'local msg=$"'"${1//[\"\$\`]}"\"
    shift
    printf ${varname+-v} $varname "$msg" "$@"
}

然后

f -d libc -v string "Permission denied"
echo $string
Permission non accordée

f -d coreutils $'Written by %s, %s, %s,\nand %s.\n' Riri Fifi Loulou Georges
Écrit par Riri, Fifi, Loulou
et Georges.

旧答案(2013 年 1 月)

好吧,这是我自己的答案:

这现在似乎没有很好地实施。在许多情况下工作,但是,虽然

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

工作简单,使用此bashism似乎无法翻译相同的字符串:

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

虽然翻译这个包含两个反引号的字符串可以正常工作

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

有一个脚本,您可能会在其中看到我的一些不成功的尝试。

#!/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%.}" "$@"
}

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.

如果有人可以帮助我删除此脚本中的评论和/或错误消息!?...(不到 8 小时?!)

总之,谢谢大家。(除非在 8 小时内获得最佳答案,否则我的赏金将转到@gniourf_gniourf。但也感谢@techno,我喜欢你的 lPrintf!)

于 2013-01-03T21:13:55.830 回答
0

让你摆脱它

从根本上讲,您可能不应该担心这个问题,因为 C 和 Bash 在printf工作方式上是不同的:Cprintf不翻译反斜杠转义,而 Bash 可以。所以在一个理想的世界里,你真的应该做的只是printf $"%s, %s,\n%s" some thing more让模板字符串保留原始反斜杠转义(所以它可能看起来像msgid "%s, %s,\\n%s"在 po-file 中)。

正如您已经意识到的那样,该$""结构还不允许使用对 bash 的双引号语法无效的 msgid。根本没有办法使用这个条目:

msgid "`"
msgstr "« "

通过剥离这些有问题的字符,它只会掩盖问题。(同样,这对 bash 来说很好,因为你会一直在写echo $"\`"and msgid "\\`")。

另一方面,确实有充分的理由不使用该$""结构。该结构允许翻译人员运行任意命令,与eval. 使用gettext.sh 函数没有问题,因为任何变量替换都由单独的envsubst程序处理。它还可以让您随心所欲$''地使用。

于 2021-03-29T11:37:19.280 回答