summaryrefslogtreecommitdiffstats
path: root/lib/locale.sh
blob: f3ebac53915e608c172cd42a2c7628b4bd600b5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# Locale functions
#
# Copyright (C) 2012, 2018, 2019 Patrick McDermott
#
# This file is part of opkbuild.
#
# opkbuild is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# opkbuild is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with opkbuild.  If not, see <http://www.gnu.org/licenses/>.

_OB_DEFAULT_LOCALE='en_US'
_OB_INTERNAL_TEXT_DOMAIN="libopkbuild_${LIBOPKBUILD_SHSOVERSION}"
_ob_text_domain=
_ob_builddir=
_ob_msgs_loaded=false

## @brief Get the current message domain
## @details \fBob_get_text_domain\fP() prints the currently loaded message
##          domain.
## @return Returns 0 on success.
## @stdout Prints the name of the currently loaded message domain.
## @pure yes This function has no side effects.
ob_get_text_domain()
{
	printf '%s' "${_ob_text_domain}"
	return 0
}

_ob_try_load_messages()
{
	local locale="${1}"
	shift 1 || _ob_abort
	local ms=

	if [ x"${_ob_builddir}" != x'' ]; then  # Env var set and not null
		ms="${_ob_builddir}"
		ms="${ms}/${locale}/${_ob_text_domain}.ms"
	else
		ms="${LOCALEDIR}"
		ms="${ms}/${locale}/LC_MESSAGES/${_ob_text_domain}.ms"
	fi

	# POSIX on the dot utility:
	# "If no readable file is found, a non-interactive shell shall abort"
	# So to survive an ENOENT or other error and display a more informative
	# error message before aborting, we need this eval/cat command.  This is
	# more resilient against race conditions than `[ -f "${ms}" ]` is.
	eval "$(cat "${ms}" 2>/dev/null)" || return 1
	return 0
}

## @brief Set and load the message domain
## @details \fBob_set_text_domain\fP() sets and loads the message domain for
##          future \fBob_get_msg\fP() calls.  A message domain is a catalog of
##          localized messages identified by message IDs.  Each application
##          should have its own message domain.  libopkbuild also has its own
##          message domain and temporarily switches to it when needed.
## @operand text_domain req The message domain to load.
## @return Returns 0 on success or 1 if \fItext_domain\fP is missing or invalid
##         or if the message domain cannot be loaded.
## @pure no This function sets an internal global variable and loads a message
##          catalog that sets numerous message variables.
ob_set_text_domain()
{
	local text_domain="${1}"
	shift 1 || _ob_abort

	case "${text_domain}" in *[!A-Za-z0-9_]* | '')
		return 1
	esac
	_ob_text_domain="${text_domain}"
	_ob_builddir="${OB_EXE_BUILDDIR-}"

	# Exit early if domain's messages have already been loaded.
	if eval "\${_ob_text_domain_${_ob_text_domain}_loaded:-false}"; then
		return
	fi
	eval "_ob_text_domain_${_ob_text_domain}_loaded=true"

	# Try to load the messages.
	if ! _ob_try_load_messages "${LC_MESSAGES%.*}"; then
		if ! _ob_try_load_messages "${LC_MESSAGES%_*}"; then
			if ! _ob_try_load_messages "${_OB_DEFAULT_LOCALE}"; then
				return 1
			fi
		fi
	fi

	_ob_msgs_loaded=true
	return 0
}

## @brief Get a message from the current message domain
## @details \fBob_get_msg\fP() prints a message, identified by a message ID,
##          from the currently loaded message catalog. 
##          \fBob_set_text_domain\fP() must be called first.
## @operand msgid req The ID of the message to print
## @return Returns 0 on success.
## @stdout Prints the requested message from the current message domain.
## @pure yes This function has no side effects.
ob_get_msg()
{
	local msgid="${1}"
	shift 1 || _ob_abort

	if ! ${_ob_msgs_loaded}; then
		printf '%s: Error: No text domain set\n' "${0##*/}" >&2
		_ob_abort
	fi
	eval "printf '%s' \"\${msg_${_ob_text_domain}_${msgid}}\""

	return 0
}

_ob_get_msg()
{
	local msgid="${1}"
	shift 1 || _ob_abort

	eval "printf '%s' \"\${msg_${_OB_INTERNAL_TEXT_DOMAIN}_${msgid}}\""

	return 0
}

_ob_load_internal_text_domain()
{
	# Make sure LC_MESSAGES is set.
	if [ x"${LC_MESSAGES:+set}" != x'set' ]; then
		if [ x"${LC_ALL:+set}" = x'set' ]; then
			LC_MESSAGES="${LC_ALL}"
		elif [ x"${LANG:+set}" = x'set' ]; then
			LC_MESSAGES="${LANG}"
		else
			LC_MESSAGES="${_OB_DEFAULT_LOCALE}"
		fi
	fi

	# Try to load the messages.
	_ob_text_domain="${_OB_INTERNAL_TEXT_DOMAIN}"
	_ob_builddir="${OB_LIB_BUILDDIR-}"
	if ! _ob_try_load_messages "${LC_MESSAGES%.*}"; then
		printf '%s: Error: Failed to load locale messages\n' \
			"${0##*/}" >&2
		kill 0
		exit 1  # Should never be reached, but just in case
	fi
}
__init _ob_load_internal_text_domain