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 from translate.storage import base
58 from StringIO import StringIO
59 import re
60 import vobject
61
62
64 """An ical entry that is translatable"""
65 - def __init__(self, source=None, encoding="UTF-8"):
70
72 self.location = location
73
75 return [self.location]
76
78 """An ical file"""
79 UnitClass = icalunit
81 """construct an ical file, optionally reading in from inputfile."""
82 self.UnitClass = unitclass
83 base.TranslationStore.__init__(self, unitclass=unitclass)
84 self.units = []
85 self.filename = ''
86 self._icalfile = None
87 if inputfile is not None:
88 self.parse(inputfile)
89
91 _outicalfile = self._icalfile
92 for unit in self.units:
93 for location in unit.getlocations():
94 match = re.match('\\[(?P<uid>.+)\\](?P<property>.+)', location)
95 for component in self._icalfile.components():
96 if component.name != "VEVENT":
97 continue
98 if component.uid.value != match.groupdict()['uid']:
99 continue
100 for property in component.getChildren():
101 if property.name == match.groupdict()['property']:
102 property.value = unit.target
103
104 if _outicalfile:
105 return str(_outicalfile.serialize())
106 else:
107 return ""
108
110 """parse the given file or file source string"""
111 if hasattr(input, 'name'):
112 self.filename = input.name
113 elif not getattr(self, 'filename', ''):
114 self.filename = ''
115 if hasattr(input, "read"):
116 inisrc = input.read()
117 input.close()
118 input = inisrc
119 if isinstance(input, str):
120 input = StringIO(input)
121 self._icalfile = vobject.readComponents(input).next()
122 else:
123 self._icalfile = vobject.readComponents(open(input)).next()
124 for component in self._icalfile.components():
125 if component.name == "VEVENT":
126 for property in component.getChildren():
127 if property.name in ('SUMMARY', 'DESCRIPTION', 'COMMENT', 'LOCATION'):
128 newunit = self.addsourceunit(property.value)
129 newunit.addnote("Start date: %s" % component.dtstart.value)
130 newunit.addlocation("[%s]%s" % (component.uid.value, property.name))
131