Repository: django-helpdesk/django-helpdesk

Issue:
Proper translation of "May" as an abbreviated month name
In English, May is both the full month name and the abbreviated form.

As listed in [`helpdesk.views.staff`](https://github.com/rossp/django-helpdesk/blob/3bd2d67193da8c1ab4243ef2c2a6ed3a03ef999b/helpdesk/views/staff.py#L987), it can cause translation problems, because it also redefines the translation for the full name (so, e.g., in Italian "Mag" is used in place of "Maggio").

This can be solved by either providing a translation context and using `pgettext` to fetch the translation or using lowercase abbreviated names (in that case "may" would collide with the verb, which should not be very commonly used as a single translatable item).

You can see a possible patch here: https://github.com/brente/django-helpdesk/commit/27ea5288f8bc7e8d039e38c81dda29619d17d6f4
(This one requires a lot of small changes, since all .po files need to be amended and recompiled; also it requires updating the Transifex repository, so probably meddling with .po files directly is not the best way to go)

Another solution would be using `MONTHS_3` from `django.utils.dates`, or relying on Django for localizing the string.

My personal preference would be using Django's MONTHS_3, but I would like to know your opinion before submitting a PR.

Thanks.



Discussion:
Thanks for bringing this to my attention. I must admit that these sort of "gotchas" are not obvious to me as somebody who only speaks a single language (even worse, is that I speak the primary language of Django).

I like the idea of using MONTHS_3. If you already have a PR for this or similar please submit it and I'll approve right away; if something needs to be written I can can aim to do it this week.


Resolving patch:
diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py
--- a/helpdesk/views/staff.py
+++ b/helpdesk/views/staff.py
@@ -26,6 +26,7 @@
 from django.http import HttpResponseRedirect, Http404, HttpResponse, HttpResponseForbidden
 from django.shortcuts import render_to_response, get_object_or_404
 from django.template import loader, Context, RequestContext
+from django.utils.dates import MONTHS_3
 from django.utils.translation import ugettext as _
 from django.utils.html import escape
 from django import forms
@@ -39,7 +40,7 @@
 from helpdesk.lib import send_templated_mail, query_to_dict, apply_query, safe_template_context
 from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency
 from helpdesk import settings as helpdesk_settings
-  
+
 if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE:
     # treat 'normal' users like 'staff'
     staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active)
@@ -69,7 +70,7 @@ def dashboard(request):
 
     # closed & resolved tickets, assigned to current user
     tickets_closed_resolved =  Ticket.objects.filter(
-            assigned_to=request.user, 
+            assigned_to=request.user,
             status__in = [Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
 
     unassigned_tickets = Ticket.objects.filter(
@@ -107,7 +108,7 @@ def dashboard(request):
             GROUP BY queue, name
             ORDER BY q.id;
     """)
-    
+
     dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
 
     return render_to_response('helpdesk/dashboard.html',
@@ -200,7 +201,7 @@ def view_ticket(request, ticket_id):
 
     if request.GET.has_key('take'):
         # Allow the user to assign the ticket to themselves whilst viewing it.
-        
+
         # Trick the update_ticket() view into thinking it's being called with
         # a valid POST.
         request.POST = {
@@ -261,7 +262,7 @@ def view_ticket(request, ticket_id):
 
 def return_ticketccstring_and_show_subscribe(user, ticket):
     ''' used in view_ticket() and followup_edit()'''
-    # create the ticketcc_string and check whether current user is already 
+    # create the ticketcc_string and check whether current user is already
     # subscribed
     username = user.username.upper()
     useremail = user.email.upper()
@@ -448,7 +449,7 @@ def update_ticket(request, ticket_id, public=False):
 
     messages_sent_to = []
 
-    # ticket might have changed above, so we re-instantiate context with the 
+    # ticket might have changed above, so we re-instantiate context with the
     # (possibly) updated ticket.
     context = safe_template_context(ticket)
     context.update(
@@ -457,7 +458,7 @@ def update_ticket(request, ticket_id, public=False):
         )
 
     if public and (f.comment or (f.new_status in (Ticket.RESOLVED_STATUS, Ticket.CLOSED_STATUS))):
-        
+
 
         if f.new_status == Ticket.RESOLVED_STATUS:
             template = 'resolved_'
@@ -712,7 +713,7 @@ def ticket_list(request):
             or  request.GET.has_key('status')
             or  request.GET.has_key('q')
             or  request.GET.has_key('sort')
-            or  request.GET.has_key('sortreverse') 
+            or  request.GET.has_key('sortreverse')
                 ):
 
         # Fall-back if no querying is being done, force the list to only
@@ -751,7 +752,7 @@ def ticket_list(request):
         date_from = request.GET.get('date_from')
         if date_from:
             query_params['filtering']['created__gte'] = date_from
-        
+
         date_to = request.GET.get('date_to')
         if date_to:
             query_params['filtering']['created__lte'] = date_to
@@ -842,7 +843,7 @@ def edit_ticket(request, ticket_id):
             return HttpResponseRedirect(ticket.get_absolute_url())
     else:
         form = EditTicketForm(instance=ticket)
-    
+
     return render_to_response('helpdesk/edit_ticket.html',
         RequestContext(request, {
             'form': form,
@@ -854,7 +855,7 @@ def create_ticket(request):
         assignable_users = User.objects.filter(is_active=True, is_staff=True).order_by('username')
     else:
         assignable_users = User.objects.filter(is_active=True).order_by('username')
-        
+
     if request.method == 'POST':
         form = TicketForm(request.POST, request.FILES)
         form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
@@ -955,7 +956,7 @@ def run_report(request, report):
         return HttpResponseRedirect(reverse("helpdesk_report_index"))
 
     report_queryset = Ticket.objects.all().select_related()
-   
+
     from_saved_query = False
     saved_query = None
 
@@ -978,22 +979,8 @@ def run_report(request, report):
     # a second table for more complex queries
     summarytable2 = defaultdict(int)
 
+    month_name = lambda m: MONTHS_3[m].title()
 
-    months = (
-        _('Jan'),
-        _('Feb'),
-        _('Mar'),
-        _('Apr'),
-        _('May'),
-        _('Jun'),
-        _('Jul'),
-        _('Aug'),
-        _('Sep'),
-        _('Oct'),
-        _('Nov'),
-        _('Dec'),
-    )
-    
     first_ticket = Ticket.objects.all().order_by('created')[0]
     first_month = first_ticket.created.month
     first_year = first_ticket.created.year
@@ -1005,7 +992,7 @@ def run_report(request, report):
     periods = []
     year, month = first_year, first_month
     working = True
-    periods.append("%s %s" % (months[month - 1], year))
+    periods.append("%s %s" % (month_name(month), year))
 
     while working:
         month += 1
@@ -1014,7 +1001,7 @@ def run_report(request, report):
             month = 1
         if (year > last_year) or (month > last_month and year >= last_year):
             working = False
-        periods.append("%s %s" % (months[month - 1], year))
+        periods.append("%s %s" % (month_name(month), year))
 
     if report == 'userpriority':
         title = _('User by Priority')
@@ -1080,7 +1067,7 @@ def run_report(request, report):
 
         elif report == 'usermonth':
             metric1 = u'%s' % ticket.get_assigned_to
-            metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year)
+            metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
 
         elif report == 'queuepriority':
             metric1 = u'%s' % ticket.queue.title
@@ -1092,11 +1079,11 @@ def run_report(request, report):
 
         elif report == 'queuemonth':
             metric1 = u'%s' % ticket.queue.title
-            metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year)
+            metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
 
         elif report == 'daysuntilticketclosedbymonth':
             metric1 = u'%s' % ticket.queue.title
-            metric2 = u'%s %s' % (months[ticket.created.month - 1], ticket.created.year)
+            metric2 = u'%s %s' % (month_name(ticket.created.month), ticket.created.year)
             metric3 = ticket.modified - ticket.created
             metric3 = metric3.days
 
@@ -1106,15 +1093,15 @@ def run_report(request, report):
             if report == 'daysuntilticketclosedbymonth':
                 summarytable2[metric1, metric2] += metric3
 
-    
+
     table = []
-    
+
     if report == 'daysuntilticketclosedbymonth':
         for key in summarytable2.keys():
             summarytable[key] = summarytable2[key] / summarytable[key]
 
     header1 = sorted(set(list( i.encode('utf-8') for i,_ in summarytable.keys() )))
-    
+
     column_headings = [col1heading] + possible_options
 
     # Pivot the data so that 'header1' fields are always first column
@@ -1330,11 +1317,11 @@ def calc_basic_ticket_stats(Ticket):
     date_30_str = date_30.strftime('%Y-%m-%d')
     date_60_str = date_60.strftime('%Y-%m-%d')
 
-    # > 0 & <= 30 
+    # > 0 & <= 30
     ota_le_30 = all_open_tickets.filter(created__gte = date_30_str)
     N_ota_le_30 = len(ota_le_30)
 
-    # >= 30 & <= 60 
+    # >= 30 & <= 60
     ota_le_60_ge_30 = all_open_tickets.filter(created__gte = date_60_str, created__lte = date_30_str)
     N_ota_le_60_ge_30 = len(ota_le_60_ge_30)
 
@@ -1357,7 +1344,7 @@ def calc_basic_ticket_stats(Ticket):
     average_nbr_days_until_ticket_closed_last_60_days = calc_average_nbr_days_until_ticket_resolved(all_closed_last_60_days)
 
     # put together basic stats
-    basic_ticket_stats = {  'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed, 
+    basic_ticket_stats = {  'average_nbr_days_until_ticket_closed': average_nbr_days_until_ticket_closed,
                             'average_nbr_days_until_ticket_closed_last_60_days': average_nbr_days_until_ticket_closed_last_60_days,
                             'open_ticket_stats': ots, }
 


Test patch: