1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Class that manages iCalender files for translation
23
24 Implementation
25 ==============
26 iCalendar files follow the U{RFC2445<http://tools.ietf.org/html/rfc2445>}
27 specification.
28
29 The iCalendar specification uses the following naming conventions:
30 - Component: an event, journal entry, timezone, etc
31 - Property: a property of a component: summary, description, start time, etc
32 - Attribute: an attribute of a property, e.g. language
33
34 The following are localisable in this implementation:
35 - VEVENT component: SUMMARY, DESCRIPTION, COMMENT and LOCATION properties
36
37 While other items could be localised this is not seen as important until use
38 cases arise. In such a case simply adjusting the component.name and
39 property.name lists to include these will allow expanded localisation.
40
41 LANGUAGE Attribute
42 ------------------
43 While the iCalendar format allows items to have a language attribute this is
44 not used. The reason being that for most of the items that we localise they
45 are only allowed to occur zero or once. Thus 'summary' would ideally
46 be present in multiple languages in one file, the format does not allow
47 such multiple entries. This is unfortunate as it prevents the creation
48 of a single multilingual iCalendar file.
49
50 Future Format Support
51 =====================
52 As this format used U{vobject<http://vobject.skyhouseconsulting.com/>} which
53 supports various formats including U{vCard<http://en.wikipedia.org/wiki/VCard>}
54 it is possible to expand this format to understand those if needed.
55
56 """
57 import re
58 from StringIO import StringIO
59
60 import vobject
61
62 from translate.storage import base
63
64
66 """An ical entry that is translatable"""
67
68 - def __init__(self, source=None, encoding="UTF-8"):
73
75 self.location = location
76
78 return [self.location]
79
80
82 """An ical file"""
83 UnitClass = icalunit
84
86 """construct an ical file, optionally reading in from inputfile."""
87 self.UnitClass = unitclass
88 base.TranslationStore.__init__(self, unitclass=unitclass)
89 self.units = []
90 self.filename = ''
91 self._icalfile = None
92 if inputfile is not None:
93 self.parse(inputfile)
94
96 _outicalfile = self._icalfile
97 for unit in self.units:
98 for location in unit.getlocations():
99 match = re.match('\\[(?P<uid>.+)\\](?P<property>.+)', location)
100 for component in self._icalfile.components():
101 if component.name != "VEVENT":
102 continue
103 if component.uid.value != match.groupdict()['uid']:
104 continue
105 for property in component.getChildren():
106 if property.name == match.groupdict()['property']:
107 property.value = unit.target
108
109 if _outicalfile:
110 return str(_outicalfile.serialize())
111 else:
112 return ""
113
115 """parse the given file or file source string"""
116 if hasattr(input, 'name'):
117 self.filename = input.name
118 elif not getattr(self, 'filename', ''):
119 self.filename = ''
120 if hasattr(input, "read"):
121 inisrc = input.read()
122 input.close()
123 input = inisrc
124 if isinstance(input, str):
125 input = StringIO(input)
126 self._icalfile = vobject.readComponents(input).next()
127 else:
128 self._icalfile = vobject.readComponents(open(input)).next()
129 for component in self._icalfile.components():
130 if component.name == "VEVENT":
131 for property in component.getChildren():
132 if property.name in ('SUMMARY', 'DESCRIPTION', 'COMMENT', 'LOCATION'):
133 newunit = self.addsourceunit(property.value)
134 newunit.addnote("Start date: %s" % component.dtstart.value)
135 newunit.addlocation("[%s]%s" % (component.uid.value, property.name))
136