KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > gettext > GettextModule


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Nam Nguyen
28  */

29
30 package com.caucho.quercus.lib.gettext;
31
32 import com.caucho.quercus.annotation.Optional;
33 import com.caucho.quercus.env.*;
34 import com.caucho.quercus.lib.string.StringModule;
35 import com.caucho.quercus.module.AbstractQuercusModule;
36 import com.caucho.util.L10N;
37 import com.caucho.util.LruCache;
38 import com.caucho.vfs.Path;
39
40 import java.util.ArrayList JavaDoc;
41 import java.util.HashMap JavaDoc;
42 import java.util.Locale JavaDoc;
43 import java.util.logging.Logger JavaDoc;
44
45
46 /**
47  * Module to find translated strings and return them in desired charset.
48  * Translations are LRU cached.
49  */

50 public class GettextModule
51     extends AbstractQuercusModule
52 {
53   private LruCache<Object JavaDoc,GettextResource> _cache =
54       new LruCache<Object JavaDoc,GettextResource>(16);
55
56   private final Logger JavaDoc log
57     = Logger.getLogger(GettextModule.class.getName());
58   private final L10N L = new L10N(GettextModule.class);
59
60   public String JavaDoc []getLoadedExtensions()
61   {
62     return new String JavaDoc[] { "gettext" };
63   }
64
65   /**
66    * Sets charset of translated strings that are returned from this domain.
67    *
68    * @param env
69    * @param domain
70    * @param codeset
71    * @return codeset
72    */

73   public StringValue bind_textdomain_codeset(Env env,
74                               StringValue domain,
75                               StringValue codeset)
76   {
77     return new StringValueImpl("UTF-16");
78   }
79
80   /**
81    * Changes root directory of domain.
82    *
83    * @param env
84    * @param domain
85    * @param directory
86    * @return directory
87    */

88   public Value bindtextdomain(Env env,
89                               StringValue domain,
90                               StringValue directory)
91   {
92     return setPath(env, domain, directory);
93   }
94
95   /**
96    * Same as gettext, but allows overriding of domain and category.
97    *
98    * @param env
99    * @param domain
100    * @param message
101    * @param category
102    */

103   public StringValue dcgettext(Env env,
104                               StringValue domain,
105                               StringValue message,
106                               int category,
107                               Value args[])
108   {
109     return translate(env,
110                      domain,
111                      getCategory(env, category),
112                      message,
113                      args);
114   }
115
116   /**
117    * Same as ngettext, but allows overriding of domain and category.
118    *
119    * @param env
120    * @param domain
121    * @param msgid1
122    * @param msgid2
123    * @param n
124    * @param category
125    */

126   public StringValue dcngettext(Env env,
127                               StringValue domain,
128                               StringValue msgid1,
129                               StringValue msgid2,
130                               int n,
131                               int category,
132                               Value args[])
133   {
134     return translate(env,
135                      domain,
136                      getCategory(env, category),
137                      msgid1,
138                      msgid2,
139                      n,
140                      args);
141   }
142
143   /**
144    * Same as gettext, but allows overriding of current domain.
145    *
146    * @param env
147    * @param domain
148    * @param message
149    */

150   public StringValue dgettext(Env env,
151                               StringValue domain,
152                               StringValue message,
153                               Value args[])
154   {
155     return translate(env,
156                      domain,
157                      "LC_MESSAGES",
158                      message,
159                      args);
160   }
161
162   /**
163    * Same as ngettext, but allows overriding of current domain.
164    *
165    * @param env
166    * @param domain
167    * @param msgid1
168    * @param msgid2
169    * @param n
170    */

171   public StringValue dngettext(Env env,
172                               StringValue domain,
173                               StringValue msgid1,
174                               StringValue msgid2,
175                               int n,
176                               Value args[])
177   {
178     return translate(env,
179                      domain,
180                      "LC_MESSAGES",
181                      msgid1,
182                      msgid2,
183                      n,
184                      args);
185   }
186
187   /**
188    * Alias of gettext().
189    *
190    * @param env
191    * @param message
192    */

193   public StringValue _(Env env, StringValue message, Value []args)
194   {
195     return gettext(env, message, args);
196   }
197
198   /**
199    * Returns translated string from current domain and default category.
200    *
201    * @param env
202    * @param message
203    */

204   public StringValue gettext(Env env, StringValue message, Value []args)
205   {
206     return translate(env,
207                      getCurrentDomain(env),
208                      "LC_MESSAGES",
209                      message,
210                      args);
211   }
212
213   /**
214    * Returns translated plural string form from current domain and default
215    * category.
216    *
217    * @param env
218    * @param msgid1
219    * @param msgid2
220    * @param n
221    * @return translated string, or original plural string if n == 1,
222    * else return original singular string
223    */

224   public StringValue ngettext(Env env,
225                               StringValue msgid1,
226                               StringValue msgid2,
227                               int n,
228                               Value args[])
229   {
230     return translate(env,
231                      getCurrentDomain(env),
232                      "LC_MESSAGES",
233                      msgid1,
234                      msgid2,
235                      n,
236                      args);
237   }
238
239   /**
240    * Changes the current domain.
241    *
242    * @param env
243    * @param domain
244    * @return name of current domain after change.
245    */

246   public StringValue textdomain(Env env,
247                               @Optional Value domain)
248   {
249     if (! domain.isNull())
250       return setCurrentDomain(env, domain.toStringValue());
251
252     return getCurrentDomain(env);
253   }
254
255   /**
256    * Retrieves the translation for message.
257    *
258    * @param env
259    * @param domain
260    * @param category
261    * @param message
262    *
263    * @return translation found, else message
264    */

265   private StringValue translate(Env env,
266                               StringValue domain,
267                               CharSequence JavaDoc category,
268                               StringValue message,
269                               Value []args)
270   {
271     Locale JavaDoc locale = env.getLocaleInfo().getMessages();
272
273     GettextResource resource = getResource(env,
274                                            getPath(env, domain),
275                                            locale,
276                                            category,
277                                            domain);
278
279     StringValue translation = resource.getTranslation(message);
280
281     if (translation == null)
282       translation = message;
283
284     return format(env, translation, args);
285   }
286
287   /**
288    * Retrieves the plural translation for msgid1.
289    *
290    * @param env
291    * @param domain
292    * @param category
293    * @param msgid1
294    * @param msgid2
295    *
296    * @return translation found, else msgid1 if n == 1, else msgid2
297    */

298   private StringValue translate(Env env,
299                               StringValue domain,
300                               CharSequence JavaDoc category,
301                               StringValue msgid1,
302                               StringValue msgid2,
303                               int quantity,
304                               Value []args)
305   {
306     Locale JavaDoc locale = env.getLocaleInfo().getMessages();
307
308     GettextResource resource = getResource(env,
309                                            getPath(env, domain),
310                                            locale,
311                                            category,
312                                            domain);
313
314     StringValue translation = resource.getTranslation(msgid1, quantity);
315
316     if (translation == null)
317       translation = errorReturn(msgid1, msgid2, quantity);
318
319     return format(env, translation, args);
320   }
321
322   private GettextResource getResource(Env env,
323                               Path path,
324                               Locale JavaDoc locale,
325                               CharSequence JavaDoc category,
326                               StringValue domain)
327   {
328     ArrayList JavaDoc<Object JavaDoc> key = new ArrayList JavaDoc<Object JavaDoc>();
329
330     key.add(path.getFullPath());
331     key.add(locale);
332     key.add(category);
333     key.add(domain);
334
335     GettextResource resource = _cache.get(key);
336
337     if (resource == null) {
338       resource = new GettextResource(env, path, locale, category, domain);
339       _cache.put(key, resource);
340     }
341
342     return resource;
343   }
344
345   private Path getPath(Env env, StringValue domain)
346   {
347     Object JavaDoc val = env.getSpecialValue("caucho.gettext_paths");
348
349     if (val == null) {
350       val = new HashMap JavaDoc<StringValue,Path>();
351
352       env.setSpecialValue("caucho.gettext_paths", val);
353     }
354
355     Path path = ((HashMap JavaDoc<StringValue,Path>)val).get(domain);
356
357     if (path == null)
358       return env.getPwd();
359
360     return path;
361   }
362
363   private Value setPath(Env env,
364                               StringValue domain,
365                               StringValue directory)
366   {
367     Object JavaDoc val = env.getSpecialValue("caucho.gettext_paths");
368
369     if (val == null) {
370       val = new HashMap JavaDoc<StringValue,Path>();
371
372       env.setSpecialValue("caucho.gettext_paths", val);
373     }
374
375     Path path = env.lookupPwd(directory);
376
377     if (path == null)
378       return BooleanValue.FALSE;
379
380     ((HashMap JavaDoc<StringValue,Path>)val).put(domain, path);
381
382     return directory;
383   }
384
385   private StringValue getCurrentDomain(Env env)
386   {
387     Object JavaDoc val = env.getSpecialValue("caucho.gettext_current");
388
389     if (val == null)
390       return setCurrentDomain(env, new StringValueImpl("messages"));
391
392     return (StringValue)val;
393   }
394
395   private StringValue setCurrentDomain(Env env, StringValue currentDomain)
396   {
397     env.setSpecialValue("caucho.gettext_current", currentDomain);
398
399     return currentDomain;
400   }
401
402   /**
403    * Gets the name for this category.
404    */

405   private String JavaDoc getCategory(Env env, int category)
406   {
407     if (category == StringModule.LC_MESSAGES)
408       return "LC_MESSAGES";
409     else if (category == StringModule.LC_ALL)
410       return "LC_ALL";
411     else if (category == StringModule.LC_CTYPE)
412       return "LC_CTYPE";
413     else if (category == StringModule.LC_NUMERIC)
414       return "LC_NUMERIC";
415     else if (category == StringModule.LC_TIME)
416       return "LC_TIME";
417     else if (category == StringModule.LC_COLLATE)
418       return "LC_COLLATE";
419     else if (category == StringModule.LC_MONETARY)
420       return "LC_MONETARY";
421     else {
422       env.warning(L.l("Invalid category. Please use named constants"));
423       return "LC_MESSAGES";
424     }
425   }
426
427   private static StringValue errorReturn(StringValue msgid1,
428                               StringValue msgid2,
429                               int n)
430   {
431     if (n == 1)
432       return msgid1;
433     else
434       return msgid2;
435   }
436
437   private static StringValue format(Env env,
438                               StringValue msg,
439                               Value []args)
440   {
441     if (args.length == 0)
442       return msg;
443     else if (msg.isUnicode())
444       return formatUnicode(env, msg, args);
445     else
446       return formatBinary(env, msg, args);
447   }
448
449   private static BinaryValue formatBinary(Env env,
450                               StringValue msg,
451                               Value []args)
452   {
453     BinaryBuilderValue sb = new BinaryBuilderValue();
454
455     int i = 0;
456     int length = msg.length();
457
458     while (i < length) {
459       char ch = msg.charAt(i);
460
461       if (ch != '[' || i + 4 > length) {
462         sb.appendByte(ch);
463         i++;
464       }
465       else if (msg.charAt(i + 1) != '_') {
466         sb.appendByte('[');
467         i++;
468       }
469       else if (msg.charAt(i + 3) != ']') {
470         sb.appendByte('[');
471         sb.appendByte('_');
472         i += 2;
473       }
474       else {
475         ch = msg.charAt(i + 2);
476         int argIndex = ch - '0';
477
478         if (0 <= argIndex && argIndex < args.length) {
479           args[argIndex].appendTo(sb);
480           i += 4;
481         }
482         else {
483           sb.appendByte('{');
484           i++;
485         }
486       }
487     }
488
489     return sb;
490   }
491
492   private static UnicodeValue formatUnicode(Env env,
493                               StringValue msg,
494                               Value []args)
495   {
496     StringBuilderValue sb = new StringBuilderValue();
497
498     int i = 0;
499     int length = msg.length();
500
501     while (i < length) {
502       char ch = msg.charAt(i);
503
504       if (ch != '[' || i + 4 > length) {
505         sb.append(ch);
506         i++;
507       }
508       else if (msg.charAt(i + 1) != '_') {
509         sb.append(ch);
510         i++;
511       }
512       else if (msg.charAt(i + 3) != ']') {
513         sb.append(ch);
514         i++;
515       }
516       else {
517         ch = msg.charAt(i + 2);
518         int argIndex = ch - '0';
519
520         if (0 <= argIndex && argIndex < args.length) {
521           args[argIndex].appendTo(sb);
522           i += 4;
523         }
524         else {
525           sb.append('[');
526           i++;
527         }
528       }
529     }
530
531     return sb;
532   }
533 }
534
Popular Tags