1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """
19 Egg Translations Management Package
20
21 @author: Brian Kirsch - bkirsch@osafoundation.org
22 @author: Markku Mielityinen - mmmm@osafoundation.org
23 @contact: Grant Baillie - grant@osafoundation.org
24 @copyright: Copyright (c) 2006-2009 Open Source Applications Foundation
25 @license: Apache License, Version 2.0
26 """
27
28 __all__ = ["DefaultTranslations", "EggTranslations", "hasCountryCode",
29 "stripCountryCode", "stripEncodingCode", "logger"]
30
31 import pkg_resources
32 from cStringIO import StringIO
33 import types
34 import sys
35 from gettext import GNUTranslations
36 import logging
37
38 logger = logging.getLogger(__name__)
39
42
44 - def ugettext(self, message, *args):
45 """
46 Extends C{GNUTranslations} ugettext
47 method adding the ability to pass
48 a default argument which will be
49 returned if no localization is
50 found for the message key.
51
52 @param message: C{unicode} or {str} message key
53 used to lookup a localization
54
55 @type message: C{unicode} or ASCII {str}
56
57 @param args: An optional argument which if passed
58 will be returned if no localzation
59 found for message. The type of the
60 return value in the args list
61 has no limitations.
62
63 @type args: C{list}
64
65 @return: C{unicode} localized message or
66 either the original message argument
67 or the default value in args if no
68 localization found.
69
70 """
71
72 if not args:
73 return GNUTranslations.ugettext(self, message)
74
75 missing = object()
76 tmsg = self._catalog.get(message, missing)
77
78 if tmsg is missing:
79 if self._fallback:
80 return self._fallback.ugettext(message, args[0])
81
82
83
84 return args[0]
85
86 return tmsg
87
88 """
89 TO DO:
90 =================
91 1. Overwrite of a key ie domain, name, locale
92 should print a warning.
93
94 2. Only one definition of a locale in a
95 resource file per project can't have
96 [myproject::fr_CA, fr] then later [myproject::fr].
97 Need to error or print warning.
98
99 3. Clean up debug logging messages to be less
100 verbose and more useful.
101
102 4. Make sure at least one locale passed in
103 locale set.
104 """
105
107
108 __slots__ = ["_init", "_iniFileName", "_localeSet", "_fallback",
109 "_iniCache", "_gtCache", "_moCache", "_localeCache",
110 "_iniEncoding"]
111
112 _NAME = "EggTranslations"
113
148
149
150
151 - def initialize(self, localeSet, iniFileName="resources.ini",
152 encoding="UTF-8", fallback=True):
153 """
154 The initialize method performs the following operations:
155
156 1. Calls the C{pkg_resources.add_activation_listener}
157 method passing the L{EggTranslations._parseINIFiles}
158 method as the callback. See the parseINIFiles method
159 for more info on loading resources and
160 gettext translation files.
161
162
163 2. Calls the EggTranslations setLocaleSet method
164 passing the localeSet param. See the setLocaleSet
165 method documentation for more info.
166
167 The initialize method sets the locale set and loads the
168 resource and translation caches.
169
170 It must be called before using the EggTranslations API.
171
172 The initialize method can only be called once per
173 EggTranslations instance.
174
175 @raise INIParsingException:
176 @raise LookupError:
177 @raise UnicodeDecodeError:
178 @raise UnicodeEncodeError:
179
180 @param localeSet: A C{unicode} or C{str} or C{list}
181 containing locale country and language
182 codes. The value(s) must be able to
183 be converted to ASCII.
184
185 @type localeSet: ASCII C{str} or C{unicode} or
186 C{list} containing ASCII
187 C{str} or C{unicode} values
188
189 @param iniFileName: The name of the resource ini file
190 in the egg's info directory.
191
192 This file contains the location of
193 localized and non-localized resources
194 as well as translation files in gettext
195 mo format. The default value for this
196 file is "resources.ini". If a C{str}
197 is passed it must be able to be
198 converted to C{unicode} using the
199 encoding passed to the initialize
200 method. The default encoding
201 is "UTF-8".
202
203 @type iniFileName: ASCII C{str} or C{unicode}
204
205 @param encoding: The character set encoding of the
206 iniFileName. This encoding will be
207 used to convert C{str} values contained
208 in the iniFileName to C{unicode}.
209 The default encoding is "UTF-8".
210
211 @type encoding: ASCII C{str} or C{unicode}
212
213 @param fallback: Indicates whether locale set fallback should
214 take place. If set to True, the
215 EggTranslations will search all locales
216 in the locale set till a resource or
217 gettext mo translation file is found. If
218 set to False the EggTranslations will
219 only try to locate a resource or
220 gettext mo translation file for the
221 current locale which is the first locale
222 in the locale set.
223
224
225 @type fallback: C{boolean}
226
227 """
228 assert(self._init, False,
229 "EggTranslations already initialized")
230
231 if type(iniFileName) == types.StringType:
232 iniFileName = unicode(iniFileName,
233 sys.getfilesystemencoding())
234
235 if type(encoding) == types.StringType:
236
237
238 encoding = unicode(encoding)
239
240 assert(type(iniFileName) == types.UnicodeType)
241 assert(type(encoding) == types.UnicodeType)
242 assert(type(fallback) == types.BooleanType)
243
244 self._init = True
245 self._fallback = fallback
246
247 self._iniFileName = iniFileName
248 self._iniEncoding = encoding
249
250 pkg_resources.add_activation_listener(self._parseINIFiles)
251 self.setLocaleSet(localeSet, fallback)
252
253 - def hasKey(self, project, name, locale=None):
254 """
255 returns True if a key was specified in one or more
256 eggs resource ini files (default is "resource.ini")
257 for the given project and name.
258
259 The locale is an optional argument. By default
260 the locale set is searched in fallback order as well
261 as the 'all' default locale (if fallback=True in the
262 initialize or setLocaleSet method) until a
263 key is found. If no key found the method returns False.
264
265 However, if a locale is specified the method will only
266 search for a key in the resource ini files for the
267 given locale.
268
269
270 @raise UnicodeDecodeError:
271 @raise UnicodeEncodeError:
272
273 @param project: A project is a root namespace under which
274 resources and localizations exist.
275
276 A project name matches an egg name.
277 An egg's info file can contain resource
278 and localizations for more than one project.
279
280 The project name must be either an ASCII
281 C{str} or C{unicode}.
282
283 @type project: ASCII C{str} or C{unicode}
284
285 @param name: name is the key to lookup in
286 a resource ini file to retrieve the
287 value specifed. For example,
288 myname = this is my value.
289
290 The name must be either an ASCII
291 C{str} or C{unicode}.
292
293 @type name: ASCII C{str} or C{unicode}
294
295
296 @param locale: Optional argument that if specified
297 tells the method to only return True
298 if key is present in one or more
299 ini files for the given locale.
300 This parameter if specified must contain
301 a valid language / country locale
302 i.e. "en_US" or "en"
303
304 @type locale: C{unicode} or ASCII C{str}
305
306 @return: C{bool} True if the name is found otherwise False
307 """
308
309 assert(self._init, True)
310
311 if type(project) == types.StringType:
312 project = unicode(project)
313
314 if type(name) == types.StringType:
315 name = unicode(name)
316
317 assert(type(project) == types.UnicodeType)
318 assert(type(name) == types.UnicodeType)
319
320 if locale is not None:
321 if type(locale) == types.UnicodeType:
322 locale = str(locale)
323
324 assert(type(locale) == types.StringType)
325 return (project, name, locale) in self._iniCache
326
327 if not self._fallback:
328 loc = self._localeSet[0]
329 return (project, name, loc) in self._iniCache
330
331
332 for locale in self._localeSet:
333 if (project, name, locale) in self._iniCache:
334 return True
335
336 return (project, name, 'all') in self._iniCache
337
338
340 """
341 Returns the unicode string value that was specified
342 in one or more eggs resource ini files (default is
343 "resource.ini") for the given project and name. or
344 None if not found.
345
346 The locale is an optional argument. By default
347 the locale set is searched in fallback order
348 (if fallback=True in the initialize or
349 setLocale method) until a key is found.
350
351 However, if a locale is specified the method will only
352 search for a key in the resource ini files for the
353 given locale.
354
355 B{Example}:
356
357 If you have a C{resource.ini} file containing the following
358 entry::
359
360 [MyProject::fr]
361 myimage = /resources/imgs/myimage.png
362
363 then::
364
365 >>> print eggRMInstance.getValueForKey("MyProject",
366 ... "myimage", "fr")
367 /resource/imgs/myimage.png
368
369 @raise UnicodeDecodeError:
370 @raise UnicodeEncodeError:
371
372 @param project: A project is a root namespace under which
373 resources and localizations exist.
374
375 A project name matches an egg name.
376 An egg's info file can contain resource
377 and localizations for more than one project.
378
379 The project name must be either an ASCII
380 C{str} or C{unicode}.
381
382 @type project: ASCII C{str} or C{unicode}
383
384 @param name: name is the key to lookup in
385 a resource ini file to retrieve the
386 value specifed. For example,
387 myname = this is my value.
388
389 The name must be either an ASCII
390 C{str} or C{unicode}.
391
392 @type name: ASCII C{str} or C{unicode}
393
394
395 @param locale: Optional argument that if specified
396 tells the method to only return True
397 if key is present in one or more
398 ini files for the given locale.
399 This parameter if specified must contain
400 a valid language / country locale
401 i.e. "en_US" or "en"
402
403 @type locale: C{unicode} or ASCII C{str}
404
405 @return: C{unicode} value or C{None} if not found
406 """
407
408 res = self._getTupleForKey(project, name, locale)
409
410 return res and res[1] or None
411
413 """
414 Returns True if:
415
416 1. One or more resource ini files have an
417 entry for the key contained in the name
418 parameter.
419
420 2. The entry is a valid directory path.
421
422 The locale is an optional argument. By default
423 the locale set is searched in fallback order
424 (if fallback=True in the initialize or
425 setLocale method) until a key is found.
426 If no key found the method returns False.
427
428 However, if a locale is specified the method will only
429 search for a key in the resource ini files for the
430 given locale.
431
432 @raise UnicodeDecodeError:
433 @raise UnicodeEncodeError:
434
435 @param project:
436
437 A project is a root namespace under which
438 resources and localizations exist.
439
440 A project name matches an egg name.
441 An egg's info file can contain resource
442 and localizations for more than one project.
443
444 The project name must be either an ASCII
445 C{str} or C{unicode}.
446
447 @type project: ASCII C{str} or C{unicode}
448
449 @param name: name is the key to lookup in
450 a resource ini file to retrieve the
451 value specifed. For example,
452 myname = this is my value.
453
454 The name must be either an ASCII
455 C{str} or C{unicode}.
456
457 @type name: ASCII C{str} or C{unicode}
458
459
460 @param locale: Optional argument that if specified
461 tells the method to only return True
462 if key is present in one or more
463 ini files for the given locale.
464 This parameter if specified must contain
465 a valid language / country locale
466 i.e. "en_US" or "en"
467
468 @type locale: C{unicode} or ASCII C{str}
469
470 @return: C{bool} True if the name is found and
471 the entry for that name is a valid directory
472 path in an egg otherwise False
473 """
474
475 res = self._getTupleForKey(project, name, locale)
476
477 if res is not None:
478 dist, value = res
479
480 return dist.metadata_isdir(
481 value.encode(self._iniEncoding))
482
483 return False
484
485
487 """
488 Returns a C{list} of C{unicode} values containing the
489 names of files in the directory entry for the
490 given project and name. The listDirectory
491 method will not return the names of sub directories
492 only files in the directory for the given project and
493 name.
494
495 The locale is an optional argument. By default
496 the locale set is searched in fallback order
497 (if fallback=True in the initialize or
498 setLocale method) until a key is found.
499 If no key found the method returns False.
500
501 However, if a locale is specified the method will only
502 search for a key in the resource ini files for the
503 given locale.
504
505 @raise UnicodeDecodeError:
506 @raise UnicodeEncodeError:
507 @raise NameError:
508 @raise OSError:
509
510 @param project: A project is a root namespace under which
511 resources and localizations exist.
512
513 A project name matches an egg name.
514 An egg's info file can contain resource
515 and localizations for more than one project.
516
517 The project name must be either an ASCII
518 C{str} or C{unicode}.
519
520 @type project: ASCII C{str} or C{unicode}
521
522 @param name: name is the key to lookup in
523 a resource ini file to retrieve the
524 value specifed. For example,
525 myname = this is my value.
526
527 The name must be either an ASCII
528 C{str} or C{unicode}.
529
530 @type name: ASCII C{str} or C{unicode}
531
532
533 @param locale: Optional argument that if specified
534 tells the method to only return True
535 if key is present in one or more
536 ini files for the given locale.
537 This parameter if specified must contain
538 a valid language / country locale
539 i.e. "en_US" or "en"
540
541 @type locale: C{unicode} or ASCII C{str}
542
543 @return: C{list} of C{unicode} filenames
544 """
545
546 res = self._getTupleForKey(project, name, locale)
547
548 if res is None:
549 self._raiseNameError(project, name, locale)
550
551 dist, value = res
552
553 dir = dist.metadata_listdir(
554 value.encode(self._iniEncoding))
555
556 for i in xrange(0, len(dir)):
557 dir[i] = unicode(dir[i], self._iniEncoding)
558
559 return dir
560
562 """
563 Returns True if:
564 1. one or more resource ini files have an
565 entry for the key contained in the
566 name parameter.
567
568 2. The entry must be a valid path to a file
569 in the same egg as resource ini.
570
571 B{Example}:
572
573 If you have a C{resource.ini} file containing the following
574 entry::
575
576 [MyProject::fr]
577 myResource=/resources/myresource.png
578
579 then::
580
581 >>> print eggRMInstance.hasResource("MyProject",
582 ... "myResource", "fr")
583 True
584
585 @raise UnicodeDecodeError:
586 @raise UnicodeEncodeError:
587
588 The locale is an optional argument. By default
589 the locale set is searched in fallback order
590 (if fallback=True in the initialize or setLocale
591 method) until a key is found. If no key found
592 the method returns False.
593
594 However, if a locale is specified the method will only
595 search for a key in the resource ini files for the
596 given locale.
597
598 @param project: A project is a root namespace under which
599 resources and localizations exist.
600
601 A project name matches an egg name.
602 An egg's info file can contain resource
603 and localizations for more than one project.
604
605 The project name must be either an ASCII
606 C{str} or C{unicode}.
607
608 @type project: ASCII C{str} or C{unicode}
609
610 @param name: name is the key to lookup in
611 a resource ini file to retrieve the
612 value specifed. For example,
613 myname = this is my value.
614
615 The name must be either an ASCII
616 C{str} or C{unicode}.
617
618 @type name: ASCII C{str} or C{unicode}
619
620
621 @param locale: Optional argument that if specified
622 tells the method to only return True
623 if key is present in one or more
624 ini files for the given locale.
625 This parameter if specified must contain
626 a valid language / country locale
627 i.e. "en_US" or "en"
628
629 @type locale: C{unicode} or ASCII C{str}
630
631 @return: C{bool} True if the name key points to at least
632 one resource in an egg otherwise False.
633 """
634
635 res = self._getTupleForKey(project, name, locale)
636
637 if res is not None:
638 dist, value = res
639 return dist.has_metadata(
640 value.encode(self._iniEncoding))
641
642 return False
643
644
646 """
647 Returns a C{file} handle to the resource for
648 the given project and name.
649
650 The locale is an optional argument. By default
651 the locale set is searched in fallback order
652 (if fallback=True in the initialize or
653 setLocale method) until a key is found. If
654 no key found the method returns False.
655
656 However, if a locale is specified the method will only
657 search for a key in the resource ini files for the
658 given locale.
659
660 @raise UnicodeDecodeError:
661 @raise UnicodeEncodeError:
662 @raise NameError:
663 @raise IOError:
664
665 @param project: A project is a root namespace under which
666 resources and localizations exist.
667
668 A project name matches an egg name.
669 An egg's info file can contain resource
670 and localizations for more than one project.
671
672 The project name must be either an ASCII
673 C{str} or C{unicode}.
674
675 @type project: ASCII C{str} or C{unicode}
676
677 @param name: name is the key to lookup in
678 a resource ini file to retrieve the
679 value specifed. For example,
680 myname = this is my value.
681
682 The name must be either an ASCII
683 C{str} or C{unicode}.
684
685 @type name: ASCII C{str} or C{unicode}
686
687
688 @param locale: Optional argument that if specified
689 tells the method to only return True
690 if key is present in one or more
691 ini files for the given locale.
692 This parameter if specified must contain
693 a valid language / country locale
694 i.e. "en_US" or "en"
695
696 @type locale: C{unicode} or ASCII C{str}
697
698 @return: C{file} handle to the resource
699 """
700
701 res = self._getTupleForKey(project, name, locale)
702
703 if res is None:
704 self._raiseNameError(project, name, locale)
705
706 dist, value = res
707
708 return StringIO(dist.get_metadata(
709 value.encode(self._iniEncoding)))
710
711
714 """
715 Returns a C{generator} containing a list of non-blank
716 non-comment lines in a resource file for the given project
717 and name.
718
719 The locale is an optional argument. By default
720 the locale set is searched in fallback order
721 (if fallback=True in the initialize or
722 setLocale method) until a key is found.
723 If no key found the method returns False.
724
725 However, if a locale is specified the method will only
726 search for a key in the resource ini files for the
727 given locale.
728
729 @raise UnicodeDecodeError:
730 @raise UnicodeEncodeError:
731 @raise NameError:
732 @raise IOError:
733 @raise LookupError:
734
735 B{Example}:
736
737 Imagine you have a resource.ini file that contains the
738 following::
739
740 [MyProject::all]
741 myDocument = README.txt
742
743 Then you might retrieve all lines from the ``README.txt``
744 file via::
745
746 >>> lines = eggRMInstance.getResourceAsLines("MyProject",
747 ... "myDocument",
748 ... "all")
749
750 >>> for line in lines:
751 ... print line
752
753 @param project: A project is a root namespace under which
754 resources and localizations exist.
755
756 A project name matches an egg name.
757 An egg's info file can contain resource
758 and localizations for more than one project.
759
760 The project name must be either an ASCII
761 C{str} or C{unicode}.
762
763 @type project: ASCII C{str} or C{unicode}
764
765 @param name: name is the key to lookup in
766 a resource ini file to retrieve the
767 value specifed. For example,
768 myname = this is my value.
769
770 The name must be either an ASCII
771 C{str} or C{unicode}.
772
773 @type name: ASCII C{str} or C{unicode}
774
775 @param locale: Optional argument that if specified
776 tells the method to only return True
777 if key is present in one or more
778 ini files for the given locale.
779 This parameter if specified must contain
780 a valid language / country locale
781 i.e. "en_US" or "en"
782
783 @type locale: C{unicode} or ASCII C{str}
784
785
786 @param encoding: The character set encoding of the
787 resource file. This encoding will be
788 used to convert C{str} values contained
789 in the file to C{unicode}.
790 The default encoding is "UTF-8".
791
792 @type encoding: ASCII C{str} or C{unicode}
793
794 @return: C{generator} list of non-blank non-comment
795 C{unicode} lines.
796 """
797
798 return pkg_resources.yield_lines(self.getResourceAsString(
799 project, name, locale, encoding))
800
801
804 """
805 Returns a C{unicode} string containing the contents of
806 the resource file for the given project and name.
807
808 The locale is an optional argument. By default
809 the locale set is searched in fallback order
810 (if fallback=True in the initialize or
811 setLocale method) until a key is found.
812 If no key found the method returns False.
813
814 However, if a locale is specified the method will only
815 search for a key in the resource ini files for the
816 given locale.
817
818
819 B{Example}:
820
821 If you have a C{resource.ini} file containing the following
822 entry::
823
824 [MyProject::all]
825 myDocument = README.txt
826
827 Then you could retrieve the entire contents of C{README.txt}
828 as a C{unicode} string via::
829
830 >>> f = eggRMInstance.getResourceAsString("MyProject",
831 ... "myDocument",
832 ... "all")
833
834 >>> f
835 u'This is the text contained in the readme file'
836
837 @raise UnicodeDecodeError:
838 @raise UnicodeEncodeError:
839 @raise NameError:
840 @raise IOError:
841 @raise LookupError:
842
843
844 @param project: A project is a root namespace under which
845 resources and localizations exist.
846
847 A project name matches an egg name.
848 An egg's info file can contain resource
849 and localizations for more than one project.
850
851 The project name must be either an ASCII
852 C{str} or C{unicode}.
853
854 @type project: ASCII C{str} or C{unicode}
855
856 @param name: name is the key to lookup in
857 a resource ini file to retrieve the
858 value specifed. For example,
859 myname = this is my value.
860
861 The name must be either an ASCII
862 C{str} or C{unicode}.
863
864 @type name: ASCII C{str} or C{unicode}
865
866
867 @param locale: Optional argument that if specified
868 tells the method to only return True
869 if key is present in one or more
870 ini files for the given locale.
871 This parameter if specified must contain
872 a valid language / country locale
873 i.e. "en_US" or "en"
874
875 @type locale: C{unicode} or ASCII C{str}
876
877 @param encoding: The character set encoding of the
878 resource file. This encoding will be
879 used to convert C{str} values contained
880 in the file to C{unicode}.
881 The default encoding is "UTF-8".
882
883 @type encoding: ASCII C{str} or C{unicode}
884
885 @return: C{unicode} contents of the resource file.
886 """
887
888 res = self._getTupleForKey(project, name, locale)
889
890 if res is None:
891 self._raiseNameError(project, name, locale)
892
893 dist, value = res
894
895 bytes = dist.get_metadata(value.encode(self._iniEncoding))
896 return unicode(bytes, encoding)
897
898 - def getText(self, project, name, txt, *args):
899 """
900 Returns a C{unicode} string containing the localized
901 value for key txt in the given project. The name
902 parameter points to a key in a resource ini file that
903 contain a value entry pointing to a gettext .mo
904 resource in the same egg.
905
906 An optional additional argument can be specified as the
907 default value to return if no localized is found.
908
909 The txt parameter will be returned by
910 L{EggTranslations.getText} if no localized value found
911 for the txt parameter.
912
913 However, if the default value argument is passed,
914 that value will be returned instead of text.
915
916 Example where there in no localized value for
917 txt parameter "Hello World":
918
919 >>> eggRMInstance.getText("MyProject", "catalog",
920 ... "Hello World")
921 u'Hello World'
922 >>>
923 >>> eggRMInstance.getText("MyProject", "catalog",
924 ... "Hello World",
925 ... None)
926 None
927 >>>
928
929 If fallback was set to True in the
930 L{EggTranslations.initialize} method or the
931 L{EggTranslations.setLocaleSet} method, the
932 L{EggTranslations.getText} method will search all
933 locales in the locale set till a gettext mo
934 translation is found for the txt parameter.
935
936 If fallback was set to False in the
937 L{EggTranslations.initialize} method
938 or the L{EggTranslations.setLocaleSet} method, the
939 L{EggTranslations.getText} method will only search
940 the current locale, which is the first locale in the
941 locale set for a gettext mo translation for
942 the txt parameter.
943
944 Note that the "all" default locale can not
945 contain any key value pairs that point to gettext
946 .mo files.
947
948 If a .mo gettext value is found in the "all" default
949 locale, the .mo file will not be loaded by
950 the L{EggTranslations}.
951
952
953 B{Example}:
954
955 A resource.ini file contains the following entry::
956
957 [MyProject::fr]
958 catalog = locale/fr/MyProject.mo
959
960 The locale/fr/myproject.mo file contains a
961 localization of "Hello" to "Bonjour". Then, calling
962 L{EggTranslations.getText} looks this up in the
963 C{.mo} file::
964
965 >>> egRMInstance.initialize("fr")
966 >>> eggRMInstance.getText("MyProject", "catalog",
967 ... "Hello")
968 u'Bonjour'
969
970 @raise UnicodeDecodeError:
971
972 @param project: A project is a root namespace under which
973 resources and localizations exist.
974
975 A project name matches an egg name.
976 An egg's info file can contain resource
977 and localizations for more than one project.
978
979 The project name must be either an ASCII
980 C{str} or C{unicode}.
981
982 @type project: ASCII C{str} or C{unicode}
983
984 @param name: name is the key to lookup in
985 a resource ini file to retrieve the
986 value specifed. For example,
987 myname = this is my value.
988
989 The name must be either an ASCII
990 C{str} or C{unicode}.
991
992 @type name: ASCII C{str} or C{unicode}
993
994 @param txt: The default text string which is used
995 as look up key to retrieve a localized
996 value from a gettext .mo file.
997
998 The default text string is usual
999 the English version of the text. The
1000 .mo gettext files will contain
1001 localizations of the English version
1002 with the English version as the key.
1003
1004 @type txt: ASCII C{str} or C{unicode}
1005
1006 @param args: An optional argument which if passed
1007 will be returned if no localzation
1008 found for txt. The type of the
1009 return value in the args list
1010 has no limitations.
1011 @type args: C{list}
1012
1013 @return: C{unicode} localized text or
1014 either the original txt argument
1015 or the default value in args if no
1016 localization found.
1017 """
1018 assert(self._init, True)
1019
1020 if type(project) == types.StringType:
1021 project = unicode(project)
1022
1023 if type(name) == types.StringType:
1024 name = unicode(name)
1025
1026 if type(txt) == types.StringType:
1027 txt = unicode(txt)
1028
1029 assert(type(project) == types.UnicodeType)
1030 assert(type(name) == types.UnicodeType)
1031 assert(type(txt) == types.UnicodeType)
1032
1033 if self._fallback:
1034 for locale in self._localeSet:
1035 key = (project, name, locale)
1036
1037
1038
1039 if key in self._gtCache:
1040 return self._gtCache[key].ugettext(txt, *args)
1041
1042 return args and args[0] or txt
1043
1044 key = (project, name, self._localeSet[0])
1045
1046 if key in self._gtCache:
1047 return self._gtCache[key].ugettext(txt, *args)
1048
1049
1050 return args and args[0] or txt
1051
1053 """
1054 Returns True if fallback was set to True in either the
1055 L{EggTranslations.initialize} method or the
1056 L{EggTranslations.setLocaleSet} method.
1057
1058 @return: C{boolean} True if fallback set to True
1059 in initialize or setLocaleSet methods
1060 otherwise False.
1061 """
1062 assert(self._init, True)
1063 return self._fallback
1064
1066 """
1067 returns True if the gettext mo file for the given locale,
1068 project, and name is loaded.
1069
1070 @param project: A project is a root namespace under which
1071 resources and localizations exist.
1072
1073 A project name matches an egg name.
1074 An egg's info file can contain resource
1075 and localizations for more than one project.
1076
1077 The project name must be either an ASCII
1078 C{str} or C{unicode}.
1079
1080 @type project: ASCII C{str} or C{unicode}
1081
1082 @param name: name is the key to lookup in
1083 a resource ini file to retrieve the
1084 value specifed. For example,
1085 myname = this is my value.
1086
1087 The name must be either an ASCII
1088 C{str} or C{unicode}.
1089
1090 @type name: ASCII C{str} or C{unicode}
1091
1092
1093 @param locale: The locale to scan for a gettext .mo translation.
1094 This parameter must contain a valid
1095 language / country locale i.e. "en_US" or "en".
1096
1097 @type locale: C{unicode} or ASCII C{str}
1098
1099 @return: C{bool} True if the .mo gettext file exists for the
1100 project, name, and locale otherwise False.
1101 """
1102
1103 assert(self._init, True)
1104
1105 try:
1106 return self._moCache[(project, name)].has_key(locale)
1107 except Exception:
1108 return False
1109
1111 """
1112 Returns a C{list} of the locales that have registered one or more
1113 key value pairs for the given project.
1114
1115 @param project: A project is a root namespace under which
1116 resources and localizations exist.
1117
1118 A project name matches an egg name.
1119 An egg's info file can contain resource
1120 and localizations for more than one project.
1121
1122 The project name must be either an ASCII
1123 C{str} or C{unicode}.
1124
1125 @type project: ASCII C{str} or C{unicode}
1126
1127 @return: C{list} of C{str} locale language / country codes.
1128 """
1129
1130 assert(self._init, True)
1131
1132 try:
1133 return self._localeCache[project]
1134 except Exception:
1135 return []
1136
1138 """
1139 Returns the C{unicode} file name of the
1140 resource ini files. The default for the resource
1141 ini file is "resource.ini".
1142
1143 The name of the resource ini file is set
1144 in the L{EggTranslations.initialize} method.
1145
1146 @return: C{unicode} the name of the resource ini file.
1147 """
1148
1149 assert(self._init, True)
1150 return self._iniFileName
1151
1153 """
1154 Returns a C{list} of valid C{str} locale language / country
1155 codes. The C{list} is arranged in ascending fallback order.
1156
1157 @return: C{list} of C{str} locale language / country codes.
1158 """
1159
1160 assert(self._init, True)
1161 return self._localeSet
1162
1164 """
1165 Resets the L{EggTranslations} locale set C{list}.
1166
1167 Resetting the locale set includes unloading all gettext
1168 .mo translations, loading the the gettext .mo translations
1169 for the current locale set, and setting the gettext locale
1170 fallback order if the fallback parameter is set to True.
1171
1172 Note the initial locale set for the L{EggTranslations}
1173 must be set in the L{EggTranslations.initialize}
1174 method.
1175
1176 Note, setting the L{EggTranslations} locale set
1177 also adds the language code as a fallback for language /
1178 county code locale definitions when fallback is set to True.
1179 If the locale set contains 'fr_CA' this method will also add
1180 to the locale set 'fr' as a fallback for 'fr_CA'.
1181
1182 @raise UnicodeEncodeError:
1183 @raise NameError:
1184
1185 @param localeSet: A C{unicode} or C{str} or C{list}
1186 containing locale country and language
1187 codes. The value(s) must be able to
1188 be converted to ASCII.
1189
1190 @type localeSet: ASCII C{str} or C{unicode} or
1191 C{list} containing ASCII
1192 C{str} or C{unicode} values
1193
1194 @param fallback: Indicates whether locale set fallback should
1195 take place. If set to True, the
1196 EggTranslations will search all locales
1197 in the locale set till a resource or
1198 gettext mo translation file is found. If
1199 set to False the EggTranslations will
1200 only try to locate a resource or
1201 gettext mo translation file for the
1202 current locale which is the first locale
1203 in the locale set.
1204
1205
1206 @type fallback: C{boolean}
1207 """
1208
1209 assert(self._init, True)
1210
1211 self._fallback = fallback
1212
1213 if type(localeSet) == types.ListType:
1214 tmpLocaleSet = localeSet
1215
1216 for i in xrange(0, len(tmpLocaleSet)):
1217 if type(tmpLocaleSet[i]) == types.UnicodeType:
1218 tmpLocaleSet[i] = str(tmpLocaleSet[i])
1219
1220
1221 assert(type(tmpLocaleSet[i]) == types.StringType)
1222
1223
1224 elif type(localeSet) == types.StringType:
1225 tmpLocaleSet = localeSet.split(",")
1226
1227 elif type(localeSet) == types.UnicodeType:
1228
1229 tmpLocaleSet = str(localeSet).split(",")
1230 else:
1231 raise NameError("localeSet must of type str, unicode, \
1232 or List")
1233
1234 tmpLocaleSet = list(self.normalizeLocale(stripEncodingCode(loc))
1235 for loc in tmpLocaleSet)
1236
1237 for loc in tmpLocaleSet:
1238 if not self.isValidLocaleForm(loc):
1239 raise NameError, "Invalid locale name found '%s'" % (loc,)
1240
1241
1242 self._gtCache = None
1243 self._gtCache = {}
1244
1245 if self._fallback:
1246 self._localeSet = list(self.yieldFallbackLocales(tmpLocaleSet))
1247 else:
1248 self._localeSet = tmpLocaleSet
1249
1250 if __debug__:
1251 logger.debug(u"Setting Locale Set to: %s",
1252 self._localeSet)
1253
1254 keys = self._moCache.keys()
1255
1256 for key in keys:
1257 project, name = key
1258
1259 if not self._fallback:
1260
1261
1262
1263
1264 locale = self._localeSet[0]
1265
1266 if not locale in self._moCache[key]:
1267
1268 continue
1269
1270 dist, mofile = self._moCache[key][locale]
1271
1272
1273
1274 bytes = dist.get_metadata(mofile)
1275 trans = DefaultTranslations(StringIO(bytes))
1276
1277 self._gtCache[(project, name, locale)] = trans
1278
1279 if __debug__:
1280 logger.debug(u"%s EggTranslation %s " \
1281 "(No Fallback)", key, locale)
1282
1283
1284 continue
1285
1286 root = None
1287
1288 for locale in self._localeSet:
1289 if locale not in self._moCache[key]:
1290
1291 continue
1292
1293 dist, mofile = self._moCache[key][locale]
1294
1295 bytes = dist.get_metadata(mofile)
1296 trans = DefaultTranslations(StringIO(bytes))
1297
1298 self._gtCache[(project, name, locale)] = trans
1299
1300 if root is None:
1301 root = trans
1302
1303 if __debug__:
1304 logger.debug(u"%s Root EggTranslation %s",
1305 key, locale)
1306 else:
1307 root.add_fallback(trans)
1308
1309 if __debug__:
1310 logger.debug(u"%s Adding Fallback %s", key,
1311 locale)
1312
1314 """
1315 Normalizes the C{str} locale to
1316 lower case all lang codes and
1317 upper case all country codes.
1318
1319 Thus if passed "Fr_cA" this method
1320 would return "fr_CA".
1321
1322 If passed "FR" this method would return
1323 "fr"
1324
1325 @param locale: a C{str} locale
1326 @type locale: ASCII C{str}
1327
1328 @return: C{str} normalized version of the
1329 locale.
1330 """
1331
1332 assert(type(locale) == types.StringType)
1333
1334 components = locale.split("_")
1335
1336 if len(components) < 2:
1337 return locale.lower()
1338 else:
1339 return "%s_%s" % (components[0].lower(), components[-1].upper())
1340
1369
1371 """
1372 Adds the lang code, if not already present,
1373 to the localeSet as a fallback to the
1374 lang country code.
1375
1376 Thus if passed ["fr_CA", "en_US", "es"] this method
1377 would return ["fr_CA", "fr", "en_US", "en", "es"]
1378
1379 @param localeSet: a C{list} containing ASCII C{str} locales
1380 @type localeSet: C{list}
1381
1382 @return: C{list} containing original locale set C{str}'s
1383 and lang code fallbacks.
1384 """
1385
1386 assert(type(localeSet) == types.ListType)
1387
1388 for locale in localeSet:
1389 yield locale
1390 if (hasCountryCode(locale)):
1391 langCode = stripCountryCode(locale)
1392 if not langCode in localeSet:
1393 yield langCode
1394
1395
1397 """
1398 Returns a C{str} representation of the
1399 L{EggTranslations}'s egg loading values suitable
1400 for debugging using print, the logging package, or a
1401 UI dialog box.
1402
1403 The structure of the debug string is as follows::
1404
1405 EggTranslations
1406 =========================
1407 INI FILE: ResourceFileName (Encoding)
1408 LOCALE SET: [localeSetValues]
1409 FALLBACK: True | False
1410
1411 EggName
1412 ===============================
1413
1414 ProjectName
1415 ---------------------
1416 [LocaleName]
1417 entryKey=entryValue
1418 gettextKey=getTextMoFile (LOADED | NOT_LOADED)
1419
1420
1421 Here's an additional example output using real values::
1422
1423 EggTranslations
1424 =========================
1425 INI FILE: 'resources.ini' (UTF-8)
1426 LOCALE SET: ['fr_CA', 'fr']
1427 FALLBACK: True
1428
1429 MyProject 0.1
1430 ===============================
1431
1432 myProject
1433 -------------------------
1434 [all]
1435 splashScreenImage = imgs/splash.png
1436 readme = README.txt
1437
1438 myAlternateProject
1439 -------------------------
1440 [all]
1441 splashScreenImage = alternate/imgs/splash.png
1442 readme=alternate/README.txt
1443
1444 MyProject.fr 0.1
1445 ===============================
1446
1447 myProject
1448 ------------------------
1449 [fr_CA]
1450 splashScreenImage = locale/fr/imgs/splash.png
1451 readme = locale/fr/README.txt
1452 catalog= locale/fr/myProject.mo (LOADED)
1453
1454 myAlternateProject
1455 ------------------------
1456 [fr_CA]
1457 splashScreenImage = alternate/locale/fr/imgs/splash.png
1458 readme = alternate/locale/fr/README.txt
1459 catalog = alternate/locale/fr/myProject.mo (LOADED)
1460
1461 @return: C{str} debug string of L{EggTranslations}
1462 current configuration in the UTF-8 encoding.
1463 """
1464 tree = {}
1465 sBuffer = []
1466
1467 keys = self._iniCache.keys()
1468
1469
1470
1471
1472 for key in keys:
1473 project, name, locale = key
1474 dist, value = self._iniCache[key]
1475
1476 if not dist in tree:
1477 tree[dist] = {}
1478
1479 if not project in tree[dist]:
1480 tree[dist][project] = {}
1481
1482 if not locale in tree[dist][project]:
1483 tree[dist][project][locale] = {}
1484
1485 tree[dist][project][locale][name] = value
1486
1487
1488 sBuffer.append(u"\n\n%s\n" % (self._NAME))
1489 sBuffer.append(u"=============================\n")
1490 sBuffer.append(u" INI FILE: '%s' (%s)\n" % (
1491 self._iniFileName,
1492 self._iniEncoding))
1493
1494 sBuffer.append(u" LOCALE SET: %s\n" % (self._localeSet))
1495 sBuffer.append(u" FALLBACK: %s\n" % (self._fallback))
1496 sBuffer.append(u"\n")
1497
1498 dists = tree.keys()
1499
1500 for dist in dists:
1501 sBuffer.append(u" %s\n" % (dist))
1502 sBuffer.append(u" ================================\n\n")
1503
1504 for project in tree[dist]:
1505 sBuffer.append(u" %s\n" % (project))
1506 sBuffer.append(u" --------------------" \
1507 "---------\n")
1508
1509
1510 for locale in tree[dist][project]:
1511 sBuffer.append(u" [%s]\n" % (locale))
1512
1513 for name in tree[dist][project][locale]:
1514 val = tree[dist][project][locale][name]
1515
1516 state = ""
1517
1518 if val.endswith(u".mo"):
1519 key = (project, name, locale)
1520
1521 if key in self._gtCache:
1522 state = " (LOADED)"
1523 else:
1524 state = u" (NOT_LOADED)"
1525
1526 sBuffer.append(u" %s = %s%s\n" % (
1527 name, val, state))
1528
1529 sBuffer.append(u"\n")
1530
1531 sBuffer.append(u"\n")
1532
1533 sBuffer.append(u"\n")
1534
1535 return (u"".join(sBuffer)).encode("UTF-8", "replace")
1536
1537
1539 """
1540 Callback method passed to
1541 C{pkg_resources.add_activation_listener} method.
1542
1543 For each egg distribution contained in the dist parameter:
1544 1. Parses the resource ini file (default name
1545 is "resource.ini") for each egg that contains one.
1546 2. Builds a cache of resources based on project,
1547 locale, and name.
1548 3. For each .mo value for a given project,
1549 locale, and name cache the file path to the
1550 .mo gettext file.
1551
1552 @raise INIParsingException:
1553 @raise LookupError:
1554 @raise UnicodeDecodeError:
1555 @raise UnicodeEncodeError:
1556
1557 @param dist: An egg package distribution
1558 @type dist: C{pkg_resources.Distribution}
1559 """
1560
1561 if __debug__:
1562 logger.debug(u"EggTranslations parsing %s", dist)
1563
1564
1565
1566 iniFile = self._iniFileName.encode(self._iniEncoding)
1567
1568 if dist.has_metadata(iniFile):
1569
1570 if __debug__:
1571 logger.debug(u"Found '%s' for project %s",
1572 self._iniFileName, dist)
1573
1574 ini_unicode_data = None
1575
1576 try:
1577 s = dist.get_metadata(iniFile)
1578 ini_unicode_data = unicode(s, self._iniEncoding)
1579 except Exception, e:
1580 s = u"unable to load ini file in %s encoding. (%s)"
1581
1582 self._raiseParseError(dist, s, self._iniEncoding, e)
1583
1584
1585
1586 ini_map = None
1587 try:
1588 data = pkg_resources.split_sections(ini_unicode_data)
1589 ini_map = dict(data)
1590 except Exception, e:
1591 s = u"one or more invalid ini token found: %s"
1592 self._raiseParseError(dist, s, e)
1593
1594 ini_keys = ini_map.keys()
1595
1596 if len(ini_keys) == 1 and ini_keys[0] is None:
1597
1598
1599
1600
1601
1602 if len(ini_map[None]):
1603 s = u"one or more invalid ini token found: %s"
1604 self._raiseParseError(dist, s, ini_map[None])
1605
1606
1607
1608
1609 if __debug__:
1610 logger.debug(u"%s no projects defined in '%s'",
1611 dist, self._iniFileName)
1612 return
1613
1614 ini_values = ini_map.values()
1615
1616 for i in xrange(0, len(ini_map)):
1617 project, locales = (None, None)
1618 try:
1619 project, locales = ini_keys[i].split("::")
1620 except:
1621 s = u"invalid project format found '[%s]'. " \
1622 "Should be [project_name::locale_name]"
1623
1624 self._raiseParseError(dist, s, ini_keys[i])
1625
1626
1627 project = project.strip()
1628 locales = locales.strip()
1629
1630 if not locales or not project:
1631 s = u"invalid project format found '[%s]'. " \
1632 "Should be [project_name::locale_name]"
1633
1634 self._raiseParseError(dist, s, ini_keys[i])
1635
1636 for locale in locales.split(","):
1637 locale = locale.strip()
1638
1639 if not locale:
1640 s = u"invalid project format found '[%s]'. " \
1641 "Should be [project_name::locale_name]"
1642
1643 self._raiseParseError(dist, s, ini_keys[i])
1644
1645 if not ini_values[i]:
1646 logger.warn("%s %s [%s] no key " \
1647 "value entries defined", dist,
1648 self._iniFileName, ini_keys[i])
1649
1650 try:
1651 if locale.lower() == 'all':
1652
1653 locale = locale.lower()
1654 else:
1655
1656
1657 locale = self.normalizeLocale(str(locale))
1658
1659 if self._localeCache.has_key(project):
1660 locales = self._localeCache[project]
1661 else:
1662 locales = []
1663
1664 locales.append(locale)
1665
1666
1667
1668 self._localeCache[project] = locales
1669 except:
1670 s = u"Invalid locale found. Unable to " \
1671 "convert to ASCII: %s"
1672
1673 self._raiseParseError(dist, s, locale)
1674
1675 if locale != 'all' and not \
1676 self.isValidLocaleForm(locale):
1677 s = u"invalid locale format found '%s'"
1678
1679 self._raiseParseError(dist, s, locale)
1680
1681 for line in ini_values[i]:
1682 name, value = (None, None)
1683 try:
1684 name, value = line.split("=", 1)
1685 except:
1686 s = u"invalid key value pair found " \
1687 "'%s' in [%s]. Should be " \
1688 "key = value"
1689
1690 self._raiseParseError(dist, s, line,
1691 ini_keys[i])
1692
1693 if value.rfind("#") != -1:
1694 value, comment = value.split("#", 1)
1695
1696
1697 value = value.strip()
1698 name = name.strip()
1699
1700 key = (project, name, locale)
1701
1702 self._iniCache[key] = (dist, value)
1703
1704 if __debug__:
1705 logger.debug(u"[%s::%s] '%s = %s' " \
1706 "added to ini cache",
1707 project, locale, name, value)
1708
1709 if value.endswith(u".mo") and not \
1710 locale == 'all':
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720 isValidPath = False
1721 bVal = value.encode(self._iniEncoding)
1722
1723 try:
1724 isValidPath = dist.has_metadata(bVal)
1725 except:
1726 pass
1727
1728 if not isValidPath:
1729 s = u" mo file entry '%s' does not " \
1730 "point to a valid resource " \
1731 "path"
1732 self._raiseParseError(dist, s, line)
1733
1734 pKey = (project, name)
1735
1736
1737 pVal = (dist, bVal)
1738
1739 if not pKey in self._moCache:
1740 self._moCache[pKey] = {}
1741
1742 self._moCache[pKey][locale] = pVal
1743
1744 if __debug__:
1745 logger.debug(u"[%s::%s] %s added " \
1746 "%s to mo cache",
1747 project, locale, name,
1748 value)
1749
1750 elif value.endswith(u".mo"):
1751 logger.warn(u"gettext .mo file " \
1752 "definition '%s' found in " \
1753 "'all' default locale. " \
1754 "This file will not be loaded.",
1755 value)
1756
1757
1759 assert(self._init, True)
1760
1761 if type(project) == types.StringType:
1762 project = unicode(project)
1763
1764 if type(name) == types.StringType:
1765 name = unicode(name)
1766
1767
1768 assert(type(project) == types.UnicodeType)
1769 assert(type(name) == types.UnicodeType)
1770
1771 if locale is None and not self._fallback:
1772 locale = self._localeSet[0]
1773
1774 if locale is not None:
1775 if type(locale) == types.UnicodeType:
1776 locale = str(locale)
1777
1778 assert(type(locale) == types.StringType)
1779 key = (project, name, locale)
1780
1781 if key in self._iniCache:
1782 return self._iniCache[key]
1783
1784 return None
1785
1786 for locale in self._localeSet:
1787 key = (project, name, locale)
1788
1789 if key in self._iniCache:
1790 return self._iniCache[key]
1791
1792 key = (project, name, 'all')
1793
1794 if key in self._iniCache:
1795 return self._iniCache[key]
1796
1797 return None
1798
1800 s = u"No match found for [%s::%s] %s" \
1801 % (project, locale and locale or self._localeSet, name)
1802
1803 raise NameError(s.encode("UTF-8", "replace"))
1804
1806 s = u"Error Parsing '%s' for Project '%s' " \
1807 % (self._iniFileName, dist)
1808
1809 if args:
1810 errTxt = errTxt % args
1811
1812 s += errTxt
1813
1814 raise INIParsingException(s.encode("UTF-8", "replace"))
1815
1817 """
1818 Removes the country code from a language / country
1819 code locale. If passed "es_UY" would return "es"
1820
1821 @param locale: a valid C{str} language / country code
1822 locale
1823 @type locale: ASCII C{str}
1824
1825 @return: C{str} with country code removed
1826 """
1827 assert(type(locale) == types.StringType)
1828
1829 return (locale.split('_')[0]).lower()
1830
1832 """
1833 Returns True if the locale passed
1834 contains a country code such as "es_UY"
1835 otherwise False.
1836
1837 @param locale: a C{str} locale
1838 @type locale: ASCII C{str}
1839
1840 @return: C{boolean} True if locale contains country code
1841 otherwise False
1842 """
1843
1844 assert(type(locale) == types.StringType)
1845
1846 return len(locale) == 5 and locale[2] == '_'
1847
1848
1850 """
1851 Strips additional information
1852 off of a locale definition
1853
1854 A locale can contain additional
1855 information beyond the two digit
1856 language and country codes.
1857
1858 For example an encoding can be
1859 specified: such as "en_US.UTF-8"
1860
1861 If passed "en_US.UTF-8" this method
1862 would return "en_US"
1863
1864 @param locale: a C{str} locale
1865 @type locale: ASCII C{str}
1866
1867 @return: C{str} stripped version of locale
1868 or original locale C{str} if no
1869 stripping was required.
1870 """
1871
1872 assert(type(locale) == types.StringType)
1873
1874 dot = locale.find(".")
1875 if dot != -1:
1876 locale = locale[:dot]
1877
1878 return locale
1879