|
1 <?xml version="1.0"?> |
|
2 <!-- |
|
3 Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. |
|
4 |
|
5 CDDL HEADER START |
|
6 |
|
7 The contents of this file are subject to the terms of the |
|
8 Common Development and Distribution License (the "License"). |
|
9 You may not use this file except in compliance with the License. |
|
10 |
|
11 You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
12 or http://www.opensolaris.org/os/licensing. |
|
13 See the License for the specific language governing permissions |
|
14 and limitations under the License. |
|
15 |
|
16 When distributing Covered Code, include this CDDL HEADER in each |
|
17 file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
18 If applicable, add the following below this CDDL HEADER, with the |
|
19 fields enclosed by brackets "[]" replaced with your own identifying |
|
20 information: Portions Copyright [yyyy] [name of copyright owner] |
|
21 |
|
22 CDDL HEADER END |
|
23 --> |
|
24 |
|
25 <!-- |
|
26 * This stylesheet provides generalized localization support for xslt |
|
27 * stylesheets. |
|
28 * |
|
29 * To use: |
|
30 * |
|
31 * 1. Include this snippet in your stylesheet: |
|
32 * |
|
33 * <xsl:import href="<path>/<to>/locale.xsl"/> |
|
34 * <xsl:variable name="LOCALE_PKG_PREFIX" select="'someprefix-'"/> |
|
35 * |
|
36 * ...where "someprefix" is a unique prefix that identifies the package or |
|
37 * project containing your stylesheets. |
|
38 * |
|
39 * 2. Set the LANG param when processing your stylesheet. This parameter takes |
|
40 * the same form as the LANG environment variable (see environ(5)): |
|
41 * |
|
42 * <language>[_<territory>][.<codeset>][@<modifier>] |
|
43 * |
|
44 * For example, using xsltproc: |
|
45 * |
|
46 * % /usr/bin/xsltproc -stringparam LANG "${LC_ALL:-${LANG}}" ... |
|
47 * |
|
48 * 3. Create a resource file: |
|
49 * |
|
50 * <?xml version="1.0" encoding="utf-8"?> |
|
51 * <resources> |
|
52 * <string name="my.resource.property.greeting"> |
|
53 * Hello, {1}! Have a good {2}! |
|
54 * </string> |
|
55 * <string name="my.resource.property.foo"> |
|
56 * Yadda yadda |
|
57 * </string> |
|
58 * ... |
|
59 * </resources> |
|
60 * |
|
61 * Save the resource file, relative to your stylesheet, as: |
|
62 * |
|
63 * locale/${LOCALE_PKG_PREFIX}strings_<language>_<territory>@<modifier>.xml |
|
64 * locale/${LOCALE_PKG_PREFIX}strings_<language>_<territory>.xml |
|
65 * locale/${LOCALE_PKG_PREFIX}strings_<language>.xml |
|
66 * locale/${LOCALE_PKG_PREFIX}strings.xml |
|
67 * |
|
68 * ...where <language>, <territory>, and <modifier> are pulled from the $LANG |
|
69 * setting above. All such files are loaded if they exist. Resources from |
|
70 * more-specifically named files are preferred if there is more than one |
|
71 * match. |
|
72 * |
|
73 * 4. From within your stylesheet, call the getString template to retrieve the |
|
74 * resource: |
|
75 * |
|
76 * <xsl:call-template name="getString"> |
|
77 * <xsl:with-param name="name" select="'my.resource.property.greeting'"/> |
|
78 * <xsl:with-param name="params"> |
|
79 * <param> |
|
80 * world |
|
81 * </param> |
|
82 * <param> |
|
83 * <bold>day</bold> |
|
84 * </param> |
|
85 * </xsl:with-param> |
|
86 * </xsl:call-template> |
|
87 * |
|
88 * Note that a <param> can hold any XML result tree fragment (RTF), not just |
|
89 * text. |
|
90 * |
|
91 * 5. Whitespace in each string resource is normalized (see the XSLT |
|
92 * normalize-space() function) by default before being parameterized and |
|
93 * returned by getString. This can be overridden by setting the |
|
94 * preserve-space attribute to "true" or "1" in the <string> or <resources> |
|
95 * element of the resource file: |
|
96 * |
|
97 * <?xml version="1.0" encoding="utf-8"?> |
|
98 * <resources> |
|
99 * <string name="my.resource.property.label" |
|
100 * preserve-space="1">Name: </string> |
|
101 * ... |
|
102 * </resources> |
|
103 * |
|
104 * or: |
|
105 * |
|
106 * <?xml version="1.0" encoding="utf-8"?> |
|
107 * <resources preserve-space="1"> |
|
108 * <string name="my.resource.property.label">Name: </string> |
|
109 * <string name="my.resource.property.indent"> </string> |
|
110 * ... |
|
111 * </resources> |
|
112 --> |
|
113 |
|
114 <xsl:stylesheet version="1.0" |
|
115 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
|
116 xmlns:exsl="http://exslt.org/common"> |
|
117 |
|
118 <!-- |
|
119 * Parse a locale string of the form: |
|
120 * <language>[_<territory>][.<codeset>][@<modifier>] |
|
121 * into its constituent parts. |
|
122 *--> |
|
123 <xsl:param name="LANG"/> |
|
124 |
|
125 <xsl:template name="langparts" mode="locale"> |
|
126 <xsl:param name="langstr" select="$LANG"/> |
|
127 <xsl:param name="searchchars" select="'_.@'"/> |
|
128 |
|
129 <xsl:choose> |
|
130 <xsl:when test="string-length($searchchars) = 0"> |
|
131 <langpart type="^"> |
|
132 <xsl:value-of select="$langstr"/> |
|
133 </langpart> |
|
134 </xsl:when> |
|
135 |
|
136 <xsl:otherwise> |
|
137 <xsl:variable name="c" |
|
138 select='substring($searchchars, string-length($searchchars), 1)'/> |
|
139 |
|
140 <xsl:variable name="newlangstr"> |
|
141 <xsl:choose> |
|
142 <xsl:when test="contains($langstr, $c)"> |
|
143 <xsl:value-of select="substring-before($langstr, $c)"/> |
|
144 </xsl:when> |
|
145 <xsl:otherwise> |
|
146 <xsl:value-of select="$langstr"/> |
|
147 </xsl:otherwise> |
|
148 </xsl:choose> |
|
149 </xsl:variable> |
|
150 |
|
151 <xsl:call-template name="langparts" mode="locale"> |
|
152 <xsl:with-param name="langstr" select="$newlangstr"/> |
|
153 <xsl:with-param name="searchchars" |
|
154 select="substring($searchchars, 1, |
|
155 string-length($searchchars) - 1)"/> |
|
156 </xsl:call-template> |
|
157 |
|
158 <!-- Ignore codeset, if any --> |
|
159 <xsl:if test="$c != '.' and contains($langstr, $c)"> |
|
160 <langpart type="{$c}"> |
|
161 <xsl:value-of select="substring-after($langstr, $c)"/> |
|
162 </langpart> |
|
163 </xsl:if> |
|
164 |
|
165 </xsl:otherwise> |
|
166 </xsl:choose> |
|
167 </xsl:template> |
|
168 |
|
169 <!-- This should be overridden by the stylesheets that include this file --> |
|
170 <xsl:variable name="LOCALE_PKG_PREFIX"/> |
|
171 |
|
172 <!-- |
|
173 * Based on the current node's <lang> element hierarchy (created in |
|
174 * the langparts template), load and combine all existing external resource |
|
175 * files using a ResourceBundle.getBundle-like algorithm: |
|
176 * |
|
177 * locale/${LOCALE_PKG_PREFIX}strings_<language>_<territory>@<modifier>.xml |
|
178 * locale/${LOCALE_PKG_PREFIX}strings_<language>_<territory>.xml |
|
179 * locale/${LOCALE_PKG_PREFIX}strings_<language>.xml |
|
180 * locale/${LOCALE_PKG_PREFIX}strings.xml |
|
181 --> |
|
182 <xsl:template match="lang" mode="locale"> |
|
183 <xsl:param name="prefix" |
|
184 select="concat('locale/', $LOCALE_PKG_PREFIX, 'strings')"/> |
|
185 <xsl:param name="suffix" select="'.xml'"/> |
|
186 |
|
187 <!-- Language --> |
|
188 <xsl:if test="langpart[@type = '^']"> |
|
189 |
|
190 <xsl:variable name="prefix.l"> |
|
191 <xsl:value-of select="$prefix"/> |
|
192 <xsl:text>_</xsl:text> |
|
193 <xsl:value-of select="langpart[@type = '^']"/> |
|
194 </xsl:variable> |
|
195 |
|
196 <!-- Language + territory --> |
|
197 <xsl:if test="langpart[@type = '_']"> |
|
198 |
|
199 <xsl:variable name="prefix.l.t"> |
|
200 <xsl:value-of select="$prefix.l"/> |
|
201 <xsl:text>_</xsl:text> |
|
202 <xsl:value-of select="langpart[@type = '_']"/> |
|
203 </xsl:variable> |
|
204 |
|
205 <!-- Language + territory + modifier --> |
|
206 <xsl:if test="langpart[@type = '@']"> |
|
207 <xsl:variable name="prefix.l.t.m"> |
|
208 <xsl:value-of select="$prefix.l.t"/> |
|
209 <xsl:text>@</xsl:text> |
|
210 <xsl:value-of select="langpart[@type = '@']"/> |
|
211 </xsl:variable> |
|
212 |
|
213 <xsl:copy-of select="document(concat($prefix.l.t.m, $suffix))"/> |
|
214 </xsl:if> |
|
215 |
|
216 <xsl:copy-of select="document(concat($prefix.l.t, $suffix))"/> |
|
217 </xsl:if> |
|
218 |
|
219 <xsl:copy-of select="document(concat($prefix.l, $suffix))"/> |
|
220 </xsl:if> |
|
221 |
|
222 <xsl:copy-of select="document(concat($prefix, $suffix))"/> |
|
223 </xsl:template> |
|
224 |
|
225 <xsl:variable name="stringsrtf"> |
|
226 <!-- Disect $LANG into its constituent parts --> |
|
227 <xsl:variable name="langparts"> |
|
228 <lang> |
|
229 <xsl:call-template name="langparts" mode="locale"/> |
|
230 </lang> |
|
231 </xsl:variable> |
|
232 |
|
233 <!-- |
|
234 * Create a node-set from the above RTF, then load appropriate external |
|
235 * localized files |
|
236 --> |
|
237 <xsl:apply-templates select="exsl:node-set($langparts)/lang" mode="locale"/> |
|
238 </xsl:variable> |
|
239 |
|
240 <xsl:variable name="strings" select="exsl:node-set($stringsrtf)"/> |
|
241 |
|
242 <!-- |
|
243 * Locate the given string from the resource files discovered above. |
|
244 * |
|
245 * Arguments: |
|
246 * |
|
247 * name - the name of the <string> resource |
|
248 * params - an RTF of parameters to substitute for {N} within the found |
|
249 * string: |
|
250 * |
|
251 * <param> |
|
252 * foo |
|
253 * <param> |
|
254 * <param> |
|
255 * bar |
|
256 * <param> |
|
257 * ... |
|
258 --> |
|
259 <xsl:template name="getString"> |
|
260 <xsl:param name="name"/> |
|
261 <xsl:param name="params"/> |
|
262 |
|
263 <xsl:variable name="string" |
|
264 select="($strings/resources/string[@name = $name])[1]"/> |
|
265 |
|
266 <!-- |
|
267 * Look for a preserve-space attribute in the <string> or its parent |
|
268 * <resource> for guidance on whether space should be preserved. If not |
|
269 * found in either element, default to false. |
|
270 * |
|
271 * Note: XSLT 1.0 forces variables to be RTFs when using child nodes |
|
272 * instead of the select attributes. So add a non-empty string ("1") to |
|
273 * preserve space, then convert it to a boolean later. |
|
274 --> |
|
275 <xsl:variable name="preserve-space"> |
|
276 <xsl:choose> |
|
277 <xsl:when test="$string/@preserve-space"> |
|
278 <xsl:if test="$string/@preserve-space = 'true' or |
|
279 $string/@preserve-space = '1'"> |
|
280 1 |
|
281 </xsl:if> |
|
282 </xsl:when> |
|
283 <xsl:when test="$string/../@preserve-space"> |
|
284 <xsl:if test="$string/../@preserve-space = 'true' or |
|
285 $string/../@preserve-space = '1'"> |
|
286 1 |
|
287 </xsl:if> |
|
288 </xsl:when> |
|
289 </xsl:choose> |
|
290 </xsl:variable> |
|
291 |
|
292 <xsl:variable name="text"> |
|
293 <xsl:choose> |
|
294 <!-- |
|
295 * Convert $preserve-space from an RTF to a string to a boolean, since |
|
296 * RTF-to-boolean conversion of a non-empty RTF always equals true |
|
297 --> |
|
298 <xsl:when test="not(boolean(string($preserve-space)))"> |
|
299 <xsl:value-of select="normalize-space($string/text())"/> |
|
300 </xsl:when> |
|
301 <xsl:otherwise> |
|
302 <xsl:value-of select="$string/text()"/> |
|
303 </xsl:otherwise> |
|
304 </xsl:choose> |
|
305 </xsl:variable> |
|
306 |
|
307 <xsl:call-template name="parameterize" mode="locale"> |
|
308 <xsl:with-param name="text" select="$text"/> |
|
309 <xsl:with-param name="params" select="exsl:node-set($params)"/> |
|
310 </xsl:call-template> |
|
311 </xsl:template> |
|
312 |
|
313 <xsl:template name="parameterize" mode="locale"> |
|
314 <xsl:param name="text"/> |
|
315 <xsl:param name="params"/> |
|
316 <xsl:param name="level" select="1"/> |
|
317 |
|
318 <xsl:choose> |
|
319 <xsl:when test="$level > count($params/param)"> |
|
320 <xsl:value-of select="$text"/> |
|
321 </xsl:when> |
|
322 <xsl:otherwise> |
|
323 <xsl:variable name="search" select="concat('{', $level, '}')"/> |
|
324 <xsl:choose> |
|
325 <xsl:when test="contains($text, $search)"> |
|
326 <xsl:call-template name="parameterize" mode="locale"> |
|
327 <xsl:with-param name="text" |
|
328 select="substring-before($text, $search)"/> |
|
329 <xsl:with-param name="params" select="$params"/> |
|
330 <xsl:with-param name="level" select="$level + 1"/> |
|
331 </xsl:call-template> |
|
332 <xsl:copy-of select="$params/param[$level]/node()"/> |
|
333 <xsl:call-template name="parameterize" mode="locale"> |
|
334 <xsl:with-param name="text" |
|
335 select="substring-after($text, $search)"/> |
|
336 <xsl:with-param name="params" select="$params"/> |
|
337 <xsl:with-param name="level" select="$level"/> |
|
338 </xsl:call-template> |
|
339 </xsl:when> |
|
340 <xsl:otherwise> |
|
341 <xsl:call-template name="parameterize" mode="locale"> |
|
342 <xsl:with-param name="text" select="$text"/> |
|
343 <xsl:with-param name="params" select="$params"/> |
|
344 <xsl:with-param name="level" select="$level + 1"/> |
|
345 </xsl:call-template> |
|
346 </xsl:otherwise> |
|
347 </xsl:choose> |
|
348 </xsl:otherwise> |
|
349 </xsl:choose> |
|
350 </xsl:template> |
|
351 </xsl:stylesheet> |