1##############################################################################
2#
3# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Interfaces related to Locales
15"""
16import re
17from zope.interface import Interface, Attribute
18from zope.schema import \
19     Field, Text, TextLine, Int, Bool, Tuple, List, Dict, Date
20from zope.schema import Container, Choice
21
22class ILocaleProvider(Interface):
23    """This interface is our connection to the Zope 3 service. From it
24    we can request various Locale objects that can perform all sorts of
25    fancy operations.
26
27    This service will be singelton global service, since it doe not make much
28    sense to have many locale facilities, especially since this one will be so
29    complete, since we will the ICU XML Files as data.  """
30
31    def loadLocale(language=None, country=None, variant=None):
32        """Load the locale with the specs that are given by the arguments of
33        the method. Note that the LocaleProvider must know where to get the
34        locales from."""
35
36    def getLocale(language=None, country=None, variant=None):
37        """Get the Locale object for a particular language, country and
38        variant."""
39
40
41class ILocaleIdentity(Interface):
42    """Identity information class for ILocale objects.
43
44    Three pieces of information are required to identify a locale:
45
46      o language -- Language in which all of the locale text information are
47        returned.
48
49      o script -- Script in which all of the locale text information are
50        returned.
51
52      o territory -- Territory for which the locale's information are
53        appropriate. None means all territories in which language is spoken.
54
55      o variant -- Sometimes there are regional or historical differences even
56        in a certain country. For these cases we use the variant field. A good
57        example is the time before the Euro in Germany for example. Therefore
58        a valid variant would be 'PREEURO'.
59
60    Note that all of these attributes are read-only once they are set (usually
61    done in the constructor)!
62
63    This object is also used to uniquely identify a locale.
64    """
65
66    language = TextLine(
67        title = u"Language Type",
68        description = u"The language for which a locale is applicable.",
69        constraint = re.compile(r'[a-z]{2}').match,
70        required = True,
71        readonly = True)
72
73    script = TextLine(
74        title = u"Script Type",
75        description = u"""The script for which the language/locale is
76                       applicable.""",
77        constraint = re.compile(r'[a-z]*').match)
78
79    territory = TextLine(
80        title = u"Territory Type",
81        description = u"The territory for which a locale is applicable.",
82        constraint = re.compile(r'[A-Z]{2}').match,
83        required = True,
84        readonly = True)
85
86    variant = TextLine(
87        title = u"Variant Type",
88        description = u"The variant for which a locale is applicable.",
89        constraint = re.compile(r'[a-zA-Z]*').match,
90        required = True,
91        readonly = True)
92
93    version = Field(
94        title = u"Locale Version",
95        description = u"The value of this field is an ILocaleVersion object.",
96        readonly = True)
97
98    def __repr__(self):
99        """Defines the representation of the id, which should be a compact
100        string that references the language, country and variant."""
101
102
103class ILocaleVersion(Interface):
104    """Represents the version of a locale.
105
106    The locale version is part of the ILocaleIdentity object.
107    """
108
109    number = TextLine(
110        title = u"Version Number",
111        description = u"The version number of the locale.",
112        constraint = re.compile(r'^([0-9].)*[0-9]$').match,
113        required = True,
114        readonly = True)
115
116    generationDate = Date(
117        title = u"Generation Date",
118        description = u"Specifies the creation date of the locale.",
119        constraint = lambda date: date < datetime.now(),
120        readonly = True)
121
122    notes = Text(
123        title = u"Notes",
124        description = u"Some release notes for the version of this locale.",
125        readonly = True)
126
127
128class ILocaleDisplayNames(Interface):
129    """Localized Names of common text strings.
130
131    This object contains localized strings for many terms, including
132    language, script and territory names. But also keys and types used
133    throughout the locale object are localized here.
134    """
135
136    languages = Dict(
137        title = u"Language type to translated name",
138        key_type = TextLine(title=u"Language Type"),
139        value_type = TextLine(title=u"Language Name"))
140
141    scripts = Dict(
142        title = u"Script type to script name",
143        key_type = TextLine(title=u"Script Type"),
144        value_type = TextLine(title=u"Script Name"))
145
146    territories = Dict(
147        title = u"Territory type to translated territory name",
148        key_type = TextLine(title=u"Territory Type"),
149        value_type = TextLine(title=u"Territory Name"))
150
151    variants = Dict(
152        title = u"Variant type to name",
153        key_type = TextLine(title=u"Variant Type"),
154        value_type = TextLine(title=u"Variant Name"))
155
156    keys = Dict(
157        title = u"Key type to name",
158        key_type = TextLine(title=u"Key Type"),
159        value_type = TextLine(title=u"Key Name"))
160
161    types = Dict(
162        title = u"Type type and key to localized name",
163        key_type = Tuple(title=u"Type Type and Key"),
164        value_type = TextLine(title=u"Type Name"))
165
166
167class ILocaleTimeZone(Interface):
168    """Represents and defines various timezone information. It mainly manages
169    all the various names for a timezone and the cities contained in it.
170
171    Important: ILocaleTimeZone objects are not intended to provide
172    implementations for the standard datetime module timezone support. They
173    are merily used for Locale support.
174    """
175
176    type = TextLine(
177        title = u"Time Zone Type",
178        description = u"Standard name of the timezone for unique referencing.",
179        required = True,
180        readonly = True)
181
182    cities = List(
183        title = u"Cities",
184        description = u"Cities in Timezone",
185        value_type = TextLine(title=u"City Name"),
186        required = True,
187        readonly = True)
188
189
190    names = Dict(
191        title = u"Time Zone Names",
192        description = u"Various names of the timezone.",
193        key_type = Choice(
194                   title = u"Time Zone Name Type",
195                   values = (u'generic', u'standard', u'daylight')),
196        value_type = Tuple(title=u"Time Zone Name and Abbreviation",
197                           min_length=2, max_length=2),
198        required = True,
199        readonly = True)
200
201
202class ILocaleFormat(Interface):
203    """Specifies a format for a particular type of data."""
204
205    type = TextLine(
206        title=u"Format Type",
207        description=u"The name of the format",
208        required = False,
209        readonly = True)
210
211    displayName = TextLine(
212        title = u"Display Name",
213        description = u"Name of the calendar, for example 'gregorian'.",
214        required = False,
215        readonly = True)
216
217    pattern = TextLine(
218        title = u"Format Pattern",
219        description = u"The pattern that is used to format the object.",
220        required = True,
221        readonly = True)
222
223
224class ILocaleFormatLength(Interface):
225    """The format length describes a class of formats."""
226
227    type = Choice(
228        title = u"Format Length Type",
229        description = u"Name of the format length",
230        values = (u'full', u'long', u'medium', u'short')
231        )
232
233    default = TextLine(
234        title=u"Default Format",
235        description=u"The name of the defaulkt format.")
236
237    formats = Dict(
238        title = u"Formats",
239        description = u"Maps format types to format objects",
240        key_type = TextLine(title = u"Format Type"),
241        value_type = Field(
242                         title = u"Format Object",
243                         description = u"Values are ILocaleFormat objects."),
244        required = True,
245        readonly = True)
246
247
248class ILocaleMonthContext(Interface):
249    """Specifices a usage context for month names"""
250
251    type = TextLine(
252        title=u'Month context type',
253        description=u"Name of the month context, format or stand-alone.")
254
255    defaultWidth = TextLine(
256        title=u'Default month name width',
257        default=u'wide')
258
259    months = Dict(
260        title=u'Month Names',
261        description=u'A mapping of month name widths to a mapping of'
262                    u'corresponding month names.',
263        key_type=Choice(
264            title=u'Width type',
265            values=(u'wide', u'abbreviated', u'narrow')),
266        value_type=Dict(
267            title=u'Month name',
268            key_type=Int(title=u'Type', min=1, max=12),
269            value_type=TextLine(title=u'Month Name'))
270        )
271
272
273class ILocaleDayContext(Interface):
274    """Specifices a usage context for days names"""
275
276    type = TextLine(
277        title=u'Day context type',
278        description=u"Name of the day context, format or stand-alone.")
279
280    defaultWidth = TextLine(
281        title=u'Default day name width',
282        default=u'wide')
283
284    days = Dict(
285        title=u'Day Names',
286        description=u'A mapping of day name widths to a mapping of'
287                    u'corresponding day names.',
288        key_type=Choice(
289            title=u'Width type',
290            values=(u'wide', u'abbreviated', u'narrow')),
291        value_type=Dict(
292            title=u'Day name',
293            key_type=Choice(
294                title=u"Type",
295                values=(u'sun', u'mon', u'tue', u'wed',
296                        u'thu', u'fri', u'sat')),
297            value_type=TextLine(title=u'Day Name'))
298        )
299
300
301class ILocaleCalendar(Interface):
302    """There is a massive amount of information contained in the calendar,
303    which made it attractive to be added."""
304
305    type = TextLine(
306        title=u"Calendar Type",
307        description=u"Name of the calendar, for example 'gregorian'.")
308
309    defaultMonthContext = TextLine(
310        title=u'Default month context',
311        default=u'format')
312
313    monthContexts = Dict(
314        title=u'Month Contexts',
315        description=u'A mapping of month context types to '
316                    u'ILocaleMonthContext objects',
317        key_type=Choice(title=u'Type',
318                        values=(u'format', u'stand-alone')),
319        value_type=Field(title=u'ILocaleMonthContext object'))
320
321    # BBB: leftover from CLDR 1.0
322    months = Dict(
323        title = u"Month Names",
324        description = u"A mapping of all month names and abbreviations",
325        key_type = Int(title=u"Type", min=1, max=12),
326        value_type = Tuple(title=u"Month Name and Abbreviation",
327                           min_length=2, max_length=2))
328
329    defaultDayContext = TextLine(
330        title=u'Default day context',
331        default=u'format')
332
333    dayContexts = Dict(
334        title=u'Day Contexts',
335        description=u'A mapping of day context types to '
336                    u'ILocaleDayContext objects',
337        key_type=Choice(title=u'Type',
338                        values=(u'format', u'stand-alone')),
339        value_type=Field(title=u'ILocaleDayContext object'))
340
341    # BBB: leftover from CLDR 1.0
342    days = Dict(
343        title=u"Weekdays Names",
344        description = u"A mapping of all month names and abbreviations",
345        key_type = Choice(title=u"Type",
346                            values=(u'sun', u'mon', u'tue', u'wed',
347                                    u'thu', u'fri', u'sat')),
348        value_type = Tuple(title=u"Weekdays Name and Abbreviation",
349                           min_length=2, max_length=2))
350
351    week = Dict(
352        title=u"Week Information",
353        description = u"Contains various week information",
354        key_type = Choice(
355            title=u"Type",
356            description=u"""
357            Varies Week information:
358
359              - 'minDays' is just an integer between 1 and 7.
360
361              - 'firstDay' specifies the first day of the week by integer.
362
363              - The 'weekendStart' and 'weekendEnd' are tuples of the form
364                (weekDayNumber, datetime.time)
365            """,
366            values=(u'minDays', u'firstDay',
367                            u'weekendStart', u'weekendEnd')))
368
369    am = TextLine(title=u"AM String")
370
371    pm = TextLine(title=u"PM String")
372
373    eras = Dict(
374        title = u"Era Names",
375        key_type = Int(title=u"Type", min=0),
376        value_type = Tuple(title=u"Era Name and Abbreviation",
377                           min_length=2, max_length=2))
378
379    defaultDateFormat = TextLine(title=u"Default Date Format Type")
380
381    dateFormats = Dict(
382        title=u"Date Formats",
383        description = u"Contains various Date Formats.",
384        key_type = Choice(
385                      title=u"Type",
386                      description = u"Name of the format length",
387                      values = (u'full', u'long', u'medium', u'short')),
388        value_type = Field(title=u"ILocaleFormatLength object"))
389
390    defaultTimeFormat = TextLine(title=u"Default Time Format Type")
391
392    timeFormats = Dict(
393        title=u"Time Formats",
394        description = u"Contains various Time Formats.",
395        key_type = Choice(
396                      title=u"Type",
397                      description = u"Name of the format length",
398                      values = (u'full', u'long', u'medium', u'short')),
399        value_type = Field(title=u"ILocaleFormatLength object"))
400
401    defaultDateTimeFormat = TextLine(title=u"Default Date-Time Format Type")
402
403    dateTimeFormats = Dict(
404        title=u"Date-Time Formats",
405        description = u"Contains various Date-Time Formats.",
406        key_type = Choice(
407                      title=u"Type",
408                      description = u"Name of the format length",
409                      values = (u'full', u'long', u'medium', u'short')),
410        value_type = Field(title=u"ILocaleFormatLength object"))
411
412    def getMonthNames():
413        """Return a list of month names."""
414
415    def getMonthTypeFromName(name):
416        """Return the type of the month with the right name."""
417
418    def getMonthAbbreviations():
419        """Return a list of month abbreviations."""
420
421    def getMonthTypeFromAbbreviation(abbr):
422        """Return the type of the month with the right abbreviation."""
423
424    def getDayNames():
425        """Return a list of weekday names."""
426
427    def getDayTypeFromName(name):
428        """Return the id of the weekday with the right name."""
429
430    def getDayAbbr():
431        """Return a list of weekday abbreviations."""
432
433    def getDayTypeFromAbbr(abbr):
434        """Return the id of the weekday with the right abbr."""
435
436    def isWeekend(datetime):
437        """Determines whether a the argument lies in a weekend."""
438
439    def getFirstDayName():
440        """Return the the type of the first day in the week."""
441
442
443class ILocaleDates(Interface):
444    """This object contains various data about dates, times and time zones."""
445
446    localizedPatternChars = TextLine(
447        title = u"Localized Pattern Characters",
448        description = u"Localized pattern characters used in dates and times")
449
450    calendars = Dict(
451        title = u"Calendar type to ILocaleCalendar",
452        key_type = Choice(
453            title=u"Calendar Type",
454            values=(u'gregorian',
455                            u'arabic',
456                            u'chinese',
457                            u'civil-arabic',
458                            u'hebrew',
459                            u'japanese',
460                            u'thai-buddhist')),
461        value_type=Field(title=u"Calendar",
462                         description=u"This is a ILocaleCalendar object."))
463
464    timezones = Dict(
465        title=u"Time zone type to ILocaleTimezone",
466        key_type=TextLine(title=u"Time Zone type"),
467        value_type=Field(title=u"Time Zone",
468                         description=u"This is a ILocaleTimeZone object."))
469
470    def getFormatter(category, length=None, name=None, calendar=u'gregorian'):
471        """Get a date/time formatter.
472
473        `category` must be one of 'date', 'dateTime', 'time'.
474
475        The 'length' specifies the output length of the value. The allowed
476        values are: 'short', 'medium', 'long' and 'full'. If no length was
477        specified, the default length is chosen.
478        """
479
480
481class ILocaleCurrency(Interface):
482    """Defines a particular currency."""
483
484    type = TextLine(title=u'Type')
485
486    symbol = TextLine(title=u'Symbol')
487
488    displayName = TextLine(title=u'Official Name')
489
490    symbolChoice = Bool(title=u'Symbol Choice')
491
492class ILocaleNumbers(Interface):
493    """This object contains various data about numbers and currencies."""
494
495    symbols = Dict(
496        title = u"Number Symbols",
497        key_type = Choice(
498            title = u"Format Name",
499            values = (u'decimal', u'group', u'list', u'percentSign',
500                              u'nativeZeroDigit', u'patternDigit', u'plusSign',
501                              u'minusSign', u'exponential', u'perMille',
502                              u'infinity', u'nan')),
503        value_type=TextLine(title=u"Symbol"))
504
505    defaultDecimalFormat = TextLine(title=u"Default Decimal Format Type")
506
507    decimalFormats = Dict(
508        title=u"Decimal Formats",
509        description = u"Contains various Decimal Formats.",
510        key_type = Choice(
511                      title=u"Type",
512                      description = u"Name of the format length",
513                      values = (u'full', u'long', u'medium', u'short')),
514        value_type = Field(title=u"ILocaleFormatLength object"))
515
516    defaultScientificFormat = TextLine(title=u"Default Scientific Format Type")
517
518    scientificFormats = Dict(
519        title=u"Scientific Formats",
520        description = u"Contains various Scientific Formats.",
521        key_type = Choice(
522                      title=u"Type",
523                      description = u"Name of the format length",
524                      values = (u'full', u'long', u'medium', u'short')),
525        value_type = Field(title=u"ILocaleFormatLength object"))
526
527    defaultPercentFormat = TextLine(title=u"Default Percent Format Type")
528
529    percentFormats = Dict(
530        title=u"Percent Formats",
531        description = u"Contains various Percent Formats.",
532        key_type = Choice(
533                      title=u"Type",
534                      description = u"Name of the format length",
535                      values = (u'full', u'long', u'medium', u'short')),
536        value_type = Field(title=u"ILocaleFormatLength object"))
537
538    defaultCurrencyFormat = TextLine(title=u"Default Currency Format Type")
539
540    currencyFormats = Dict(
541        title=u"Currency Formats",
542        description = u"Contains various Currency Formats.",
543        key_type = Choice(
544                      title=u"Type",
545                      description = u"Name of the format length",
546                      values = (u'full', u'long', u'medium', u'short')),
547        value_type = Field(title=u"ILocaleFormatLength object"))
548
549    currencies = Dict(
550        title=u"Currencies",
551        description = u"Contains various Currency data.",
552        key_type = TextLine(
553                      title=u"Type",
554                      description = u"Name of the format length"),
555        value_type = Field(title=u"ILocaleCurrency object"))
556
557
558    def getFormatter(category, length=None, name=u''):
559        """Get the NumberFormat based on the category, length and name of the
560        format.
561
562        The 'category' specifies the type of number format you would like to
563        have. The available options are: 'decimal', 'percent', 'scientific',
564        'currency'.
565
566        The 'length' specifies the output length of the number. The allowed
567        values are: 'short', 'medium', 'long' and 'full'. If no length was
568        specified, the default length is chosen.
569
570        Every length can have actually several formats. In this case these
571        formats are named and you can specify the name here. If no name was
572        specified, the first unnamed format is chosen.
573        """
574
575    def getDefaultCurrency():
576        """Get the default currency."""
577
578_orientations = [u"left-to-right", u"right-to-left",
579                 u"top-to-bottom", u"bottom-to-top"]
580class ILocaleOrientation(Interface):
581    """Information about the orientation of text."""
582
583    characters = Choice(
584        title = u"Orientation of characters",
585        values = _orientations,
586        default = u"left-to-right"
587        )
588
589    lines = Choice(
590        title = u"Orientation of characters",
591        values = _orientations,
592        default = u"top-to-bottom"
593        )
594
595class ILocale(Interface):
596    """This class contains all important information about the locale.
597
598    Usually a Locale is identified using a specific language, country and
599    variant.  However, the country and variant are optional, so that a lookup
600    hierarchy develops.  It is easy to recognize that a locale that is missing
601    the variant is more general applicable than the one with the variant.
602    Therefore, if a specific Locale does not contain the required information,
603    it should look one level higher.  There will be a root locale that
604    specifies none of the above identifiers.
605    """
606
607    id = Field(
608        title = u"Locale identity",
609        description = u"ILocaleIdentity object identifying the locale.",
610        required = True,
611        readonly = True)
612
613    displayNames = Field(
614        title = u"Display Names",
615        description = u"""ILocaleDisplayNames object that contains localized
616                        names.""")
617
618    dates = Field(
619        title = u"Dates",
620        description = u"ILocaleDates object that contains date/time data.")
621
622    numbers = Field(
623        title = u"Numbers",
624        description = u"ILocaleNumbers object that contains number data.")
625
626    orientation = Field(
627        title = u"Orientation",
628        description = u"ILocaleOrientation with text orientation info.")
629
630    delimiters = Dict(
631        title=u"Delimiters",
632        description = u"Contains various Currency data.",
633        key_type = Choice(
634            title=u"Delimiter Type",
635            description = u"Delimiter name.",
636            values=(u'quotationStart', u'quotationEnd',
637                            u'alternateQuotationStart',
638                            u'alternateQuotationEnd')),
639        value_type = Field(title=u"Delimiter symbol"))
640
641    def getLocaleID():
642        """Return a locale id as specified in the LDML specification"""
643
644
645class ILocaleInheritance(Interface):
646    """Locale inheritance support.
647
648    Locale-related objects implementing this interface are able to ask for its
649    inherited self. For example, 'en_US.dates.monthNames' can call on itself
650    'getInheritedSelf()' and get the value for 'en.dates.monthNames'.
651    """
652
653    __parent__ = Attribute("The parent in the location hierarchy")
654
655    __name__ = TextLine(
656        title = u"The name within the parent",
657        description=u"""The parent can be traversed with this name to get
658                      the object.""")
659
660    def getInheritedSelf():
661        """Return itself but in the next higher up Locale."""
662
663
664class IAttributeInheritance(ILocaleInheritance):
665    """Provides inheritance properties for attributes"""
666
667    def __setattr__(name, value):
668        """Set a new attribute on the object.
669
670        When a value is set on any inheritance-aware object and the value
671        also implements ILocaleInheritance, then we need to set the
672        '__parent__' and '__name__' attribute on the value.
673        """
674
675    def __getattribute__(name):
676        """Return the value of the attribute with the specified name.
677
678        If an attribute is not found or is None, the next higher up Locale
679        object is consulted."""
680
681
682class IDictionaryInheritance(ILocaleInheritance):
683    """Provides inheritance properties for dictionary keys"""
684
685    def __setitem__(key, value):
686        """Set a new item on the object.
687
688        Here we assume that the value does not require any inheritance, so
689        that we do not set '__parent__' or '__name__' on the value.
690        """
691
692    def __getitem__(key):
693        """Return the value of the item with the specified name.
694
695        If an key is not found or is None, the next higher up Locale
696        object is consulted.
697        """
698
699class ICollator(Interface):
700    """Provide support for collating text strings
701
702    This interface will typically be provided by adapting a locale.
703    """
704
705    def key(text):
706        """Return a collation key for the given text.
707        """
708
709    def cmp(text1, text2):
710        """Compare two text strings.
711
712        The return value is negative if text1 < text2, 0 is they are
713        equal, and positive if text1 > text2.
714        """
715
716
717