Date Handler/he

From Gramps
Jump to: navigation, search

מדוע קיימים מספר סדרני תאריכים?

מדינות, תרבויות ואפילו אזורים שונים נוטים לסגל לעצמם מוסכמות מסויימות ושונות לניתוח והצגת תאריכים. דוגמה פשןטה היא אופן תסדור מרוזת תאריך במשומות שונים, סדר תצוג החודש והיום שונה בין רוב המדינות האירופיות לזה הנהוג בארצות הברית (dd/mm/yyyy לעומת mm/dd/yyyy). כמו כן, כל שפה מנהיגה על פי רוב ערכת מבחינים (qualifiers) ומאייכים (modifiers) משלה וסימונים מקובלים לתצוג תאריכים: מחרוזות לתצוג טווח או תקופה, כמו "מ־X עד Y" או "בין X ל־Y" עשויים להיות בעלי סדר מילים שונה בשפות שונות. דומה הדבר במונחים שבאים לתאר את 'איכות' התאריכם כמו"בסביבות", "מחושב", "משוער", "מקורב". אם לא די באלו, שמות החודשים והימים שונים משפה לשפה ואם נוסיף לזאת גם את שבעת לוחות השנה השונים, והרי ההנמקה לצורך מובהק בסדרן תאריכים יעודי לכל תרבות או שפה.

אפשור הנהגת מספר סדרני תאריך, פותרת מספר סוגיות.

  • תיקנון – תוכנת גרמפס תדע לנתח כל תאריך שהוזן על ידי משתמשים, ללא תלות בשפת ההזנה.
  • תצוג – התאריכים שמוצגים למשתמשים יהיו מובנים, ברורים ונכונים.
  • המקמה – אין צורך לתרגום ביטויים שגרתיים כמו (מ|לפני|בין).

לרוע המזל, התברר שנטל התחזוקה של סדרן תאריכים לכל שפההוא גבוה מאוד בהשוואה לתקורת התחזוקה הרגילות של מחרוזות במסד נתוני התרגום. במסגרת הטיפול בתקל 6926 נעשה נסיון להעביר את הנטל מהצורך בקידוד חזרה למסד נתוני תרגום. המערכת החדשה מאפשרת גם תמיכה בהטית שמות חודשים בשפות שבהן זה נחוץ. קוד סדרן תאריכים עדיין קיים, אבל הוא הרבה יותר פשוט וקל לתחזוקה. לקריאה נוספת סוגיות פתוחות קשורות.

דגשים בייצור סדרן תאריכים

Gnome-important.png
מאחר שהקוד החדש בפתוח תוכנת גרמפס נוצר ב־master

הוראות אלו יישימות רק ל־API הנוכחי. לתיעוד API ישן יותר , נא לעיין בהיסטוריית הגרסאות בוויקי בבקרת המקור.

לאיזכור וסיוע בקידוד סדרן תאריכים לשפה אות תרבות מסויימים (לדוגמה לעברית) נא להשתמש בתיעוד שנוצר על מסגרת סדרן תאריכים. להלן מספר הדגשים על הדרך בה יש לפתח מתקע סדרן תאריכים עבור שפה מסויימת.

  • המתקע משתמש בכמה מחרוזות במסד הנתוני התרגום אותם יש לתרגם. מוסכמות מיוחדות מאפשרות שליטה לשונית עדינה להטיית שמות חודשים בשפות בהן הטייה כזאת נחוצה.
  • כדי שהמתקע יפעל, נדרש להגדיר שתי מחלקות:
אחת לניתוח תאריכים והאחרת להצגת תאריכים.

התאמות אישיות ברמות שונות אפשרית כתלות בשונות התקן לעיצוב התאריכים בשפת היעד מהמקור האנגלי.

  • חובה לרשום את המתקע במערכת גרמפס.
  • לפני דחיפת המתקע לבסיס הקוד בשרת ה־Git, יש לבדוק אותו באמצעות מסגרות הבדיקה הקיימות.

המקמת מחרוזות התאריך

כדי לתרגם מחרוזות תאריך כמו, שמות חודשים, מבחיני ומאייכי תאריך ודומיהם יש להצמד לשגרת התרגום הרגילה. מחרוזות אלו מאוחסנות במחלקה בשם DateStrings, שמשמשת את מנתח התאריכים ואת מתצג התאריכים.

אם בשפת היעד נהוגים כללי הטייה מיוחדים, חשוב לשים לב שהמחרוזות האלה מעובדות באמצעות gen.utils .grampslocale.GrampsTranslation.lexgettext למופעים של gen.utils.grampslocale.Lexeme. אפשר להעזר ולהשתמש בדוגמאות ב־Lexeme docs כדי ללמוד כיצד לספק את כל ההטיות הנדרשות עבור כל ההקשרים שבהם נעשה שימוש מאוחר יותר במצג התאריכים. לדוגמה (מרוסית ru.po):

#: ../gramps/gen/datehandler/_datestrings.py:70
msgid "localized lexeme inflections||May"
msgstr "И=май|Р=мая|Т=маем|П=мае"

כל ההטיות שסופקו וכל הקידומות החד משמעיות שלהן יוכרו אוטומטית כמילים נרדפות חוקיות לקוד המחלקה הבסיסית של מנתח תאריכים.

המקמת מחלקה למצג תאריכים

מחלקת מתצג התאריכים צריכה להגזר ממחלקת DateDisplay באופן הבא:

from _DateDisplay import DateDisplay
class MyDateParser(DateDisplay):
   ...

בנוסף היא אמורה לספק שיטת

display(). 

אם משתמשם ביישום ברירת המחדל, יורשים ממנו את סגנון iso, כלומר, כל התאריכים יוצגו ללא הבדל גלוי בין תאריכי תקופה וטווח.

הרמת ההתאמה האישית הבאה תהיה בחירת חלופה "ארוזה" מראש אחרת, שמסופקת במחלקה הבסיסית, כדלקמן (כפי ש־DateDisplayEN עושה):

display = DateDisplay.display_formatted

התאמה זו תתאים ליותר ממחצית מהשפות הנתמכות נכון להיום, שפות שלא מטפללות את רשימת המשתנים הזמינים למתצג התאריך.

ליבת display_formatted מציגה תאריכים רגילים ו/או מורכבים, ומקשטת אותם, במידת הצורך, במבחינים (כמו "לפני..."), מאייכי איכות (כמו "מחושב...") ולוחות שנה בעלי מידע תחילת שנה לא תקניים.

כששפת היעד דורשת הטיית שמות חודשים תלויי מבחינים, או, למשל, אם התאריך הוא תאריך התחלה או סיום בטווח, נדרש להשתמש ולתרגם מחרוזות מיוחדות כדי לציין את ההטיה המתאימה. תיעוד לתרחיש מתקדם זה ניתן למצוא בהערות קוד המקור של DateDisplay.

כששפת היעד עוקפת את Display.formats למשהו אחר, היא צריכה לעקוף גם את _display_calendar (או את _display_gregorian/_display_hebrew/... בנפרד, לכל השיטות שמאצילות ל־_display_calendar כברירת מחדל עם רשימות שמות החודשים המתאימים). עקיפת _display_calendar צריכה לעצב את התאריך בהתאם לתסדיר מותאם אישית שנבחר, תוך שימוש במספר self.format כמפתח לרשימת self.formats.

יצירת מחלקת נתח תאריכים ממומקמת חדשה

המחלקה החדש חייבת להגזר ממחלקת DateParser:

from _DateParser import DateParser
class MyDateParser(DateParser):
...

מחלקת הנתח חייבת לספק את השיטה
parse()
למעשה, מכיוון שהמחלקה הבסיסית כבר מגדירה שיטה זו, סביר להניח שיהיה צורך להגדיר מחדש רק את קבועי המחלקה ואולי את השיטה
init_strings()

Localizing the date formats

If you use DateDisplay._display_calendar in your date displayer, whether directly or via DateDisplay.display_formatted, then your dates will be formatted using the translated format strings therein. The month names (whether short or long) in these formats are used to format an instance of the class Lexeme, initialized to the set of inflection forms from the translated month string.

These formats all have a warning in the translation database sending you to the entrance point to these docs. You have the following options in translating such a format string:

1) If you don't translate the string, it will just use the same order as in English, without any inflection selection.

2) If you omit the inflection selection in a format, then the default inflection will be used, or, if your language has no inflections, just the one and only name for the month (see the documentation for class Lexeme for details). REMEMBER the format specifiers themselves must not be translated, otherwise there will be a runtime error in the formatting code. E.g.:

msgid "{day:d} {long_month} {year}"
msgstr "{day:d}/{long_month}/{year}"

(Note how the words "long_month" remain in English!!!)

3) However, if your language does support inflections, here you should select the appropriate inflection for the month name, using the same name of the inflection as you entered in the translated month string. Again, see the documentation for class Lexeme for details. E.g.:

#. TRANSLATORS: see
#. http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
#. to learn how to select proper inflection for your language.
#: ../gramps/gen/datehandler/_datedisplay.py:475
msgid "{day:d} {long_month} {year}"
msgstr "{day:d} {long_month.f[Р]} {year}"

Some of the strings need not be translated at all for your language if you don't use the inflection control feature of _display_calendar, e.g.:

#. first date in a span
#. You only need to translate this string if you translate one of the
#. inflect=_("...") with "from"
#: ../gramps/gen/datehandler/_datedisplay.py:160
msgid "from|{long_month} {year}"
msgstr ""

Documentation for this advanced scenario can be found in the source code comments to DateDisplay.

Upgrading an older date handler to the framework from 6926

First, before changing your code, run the helper code to generate a snippet you can merge into your language's xx.po. See the documentation.

If you stop here, the handler should work already (strings previously in _grampslocale.py are now picked up from the translation database, and the rest remains the same). However, it'll likely have lots of redundant code and strings in it...

Then remove from your DateDisplayXX class any overrides of date strings from base class, such as long_months, short_months, calendar, _mod_str, _qual_str -- these are now localized via the translation database.

Gramps-notes.png
NOTE:

Leave _bce_str for now (it will be removed when 7064non-Gregorian calendars wrongly use BCE notation for negative dates; is resolved).

In your DateParserXX class, remove the various ..._to_int that are now initialized based on the strings in DateStrings, i.e.:

month_to_int
hebrew_to_int
french_to_int
islamic_to_int
persian_to_int
calendar_to_int

If you had various spellings of the same word, you can use the lexeme mechanism to express that in the translation database:

#: ../gramps/gen/datehandler/_datestrings.py:140
msgid "Hebrew month lexeme|Nisan"
msgstr "И=нисан|Р=нисана|Т=нисаном|П=нисане|И1=ниссан|Р1=ниссана|Т1=ниссаном"

Here we unite the alternative spellings Nisan and Nissan into the same lexeme.

Alternatively, you can extend the base string table that is initialized from the date strings explicitly from the code, as before. For example, here's how the Russian parser adds an alternative name for the Persian calendar:

   def init_strings(self):
       DateParser.init_strings(self)
       DateParser.calendar_to_int.update({
           'персидский'    : Date.CAL_PERSIAN, 
           'п'             : Date.CAL_PERSIAN, 
       })
       ...

If you don't have custom language-specific formats

If you override formats and it corresponds exactly to the formats in the DateDisplay.formats, only changing the strings inside to translate them to your language, then remove the formats override and any overrides of _display_gregorian/_display_julian/... and display methods.

Just alias your display to DateDisplay.display_formatted.

If you have custom language-specific formats

Consider rewriting your existing overrides of _display_gregorian and display to a single override of _display_calendar. At the very least, you'll need to update the signature of your _display_* methods, to match _display_calendar. You may just add (and ignore) a trailing **kwargs if you're lazy...

Converted date handlers

Add yours here when done!!!

  • FR (romjerome)
  • HR (josip)
  • RU (vassilii)

Registering the localized date handler

The module must register itself as the date handler. This is done by inserting the following code at the end of your _Date_xx.py file:

   from _DateHandler import register_datehandler
   register_datehandler(('xx_XX','xx','xxxxx','xx_YY'),
                        MyDateParser,MyDateDisplay)
where MyDateParser and MyDateDisplay are the classes defined for parsing and displaying, and the items in quotes are language identifiers that may possibly be associated with your language. For example, different systems use ru, RU, ru_RU, koi8r, ru_koi8r, russian, Russian, ru_RU.koi8r, etc. to identify the Russian language.

Then you need to import the parser to Gramps by adding

   import _Date_xx

line to __init__.py file located in the same directory.

That's it for the requirements. The example date handling plugins can be found in _Date_xx.py under the gramps/gen/datehandler directory.

Classes and relations

Gramps-notes.png
pyreverse -o png *.py

A quick overview of current classes: Date classes.svg

and relations between Date handlers: Date packages.svg


Calendars

Gramps uses Gregorian calendar by-default, but also supports Julian calendar, Hebrew calendar, Islamic calendar, Persian calendar, French Republican calendar and Swedish calendar.

It is possible to add new one, like Chinese calendar, Indian Civil calendar, Bahá'í calendar, Mayan calendar, Zoroastrian calendar or Japanese calendar ... into gen/lib/calendar.

We need to have rules which properly convert this new calendar to sdn (serial date number) and from sdn to calendar.

How to test a date handler for your locale

Testing with Gramps

When you run from the Git tree, you have the gramps/plugins/tool/dateparserdisplaytest.py debug tool available under Menu -> Tools -> Debug ->.

  1. Create a new empty Family Tree.
  2. Go on Menu -> Tools -> Debug -> test for date parser and displayer.
  3. Compare source and target dates in the entries on Events View. Alternatively, compare the birth and death dates in the People View. In the latter view, the failed tests are marked with a "Fail" tag on the person, you can use the "Customize view" option to add the "Tags" column to sort on the pass/fail status.

Tests with command-line

In gramps/ directory:

1) Ensure you haven't broken the basic date functionality in English:

 LC_ALL=en_GB.utf-8 LANG=en_GB.utf-8 GRAMPS_RESOURCES=$PWD  python3 -m unittest discover -p 'date*test.py'

2) Test your locale. For example, for Russian:

 LC_ALL=ru_RU.utf-8 LANG=ru_RU.utf-8 GRAMPS_RESOURCES=$PWD  python3 -m unittest discover -p 'date*test.py'

See also Testing Gramps.

See also

Issues

  • 7084 576/3795 DateParserEN failures under the DateTest tool