format_da.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2017 Mycroft AI Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 
18 from mycroft.util.lang.format_common import convert_to_mixed_fraction
19 from math import floor
20 
21 months = ['januar', 'februar', 'märz', 'april', 'mai', 'juni',
22  'juli', 'august', 'september', 'oktober', 'november',
23  'dezember']
24 
25 NUM_STRING_DA = {
26  0: 'nul',
27  1: 'en',
28  2: 'to',
29  3: 'tre',
30  4: 'fire',
31  5: 'fem',
32  6: 'seks',
33  7: 'syv',
34  8: 'otte',
35  9: 'ni',
36  10: 'ti',
37  11: 'elve',
38  12: 'tolv',
39  13: 'tretten',
40  14: 'fjorten',
41  15: 'femten',
42  16: 'seksten',
43  17: 'sytten',
44  18: 'atten',
45  19: 'nitten',
46  20: 'tyve',
47  30: 'tredive',
48  40: 'fyrre',
49  50: 'halvtres',
50  60: 'tres',
51  70: 'halvfjers',
52  80: 'firs',
53  90: 'halvfems',
54  100: 'hundrede'
55 }
56 
57 NUM_POWERS_OF_TEN = [
58  'hundred',
59  'tusind',
60  'million',
61  'milliard',
62  'billion',
63  'billiard',
64  'trillion',
65  'trilliard'
66 ]
67 
68 FRACTION_STRING_DA = {
69  2: 'halv',
70  3: 'trediedel',
71  4: 'fjerdedel',
72  5: 'femtedel',
73  6: 'sjettedel',
74  7: 'syvendedel',
75  8: 'ottendedel',
76  9: 'niendedel',
77  10: 'tiendedel',
78  11: 'elftedel',
79  12: 'tolvtedel',
80  13: 'trettendedel',
81  14: 'fjortendedel',
82  15: 'femtendedel',
83  16: 'sejstendedel',
84  17: 'syttendedel',
85  18: 'attendedel',
86  19: 'nittendedel',
87  20: 'tyvendedel'
88 }
89 
90 # Numbers below 1 million are written in one word in German, yielding very
91 # long words
92 # In some circumstances it may better to seperate individual words
93 # Set EXTRA_SPACE=" " for separating numbers below 1 million (
94 # orthographically incorrect)
95 # Set EXTRA_SPACE="" for correct spelling, this is standard
96 
97 # EXTRA_SPACE = " "
98 EXTRA_SPACE = ""
99 
100 
101 def nice_number_da(number, speech, denominators):
102  """ Danish helper for nice_number
103  This function formats a float to human understandable functions. Like
104  4.5 becomes "4 einhalb" for speech and "4 1/2" for text
105  Args:
106  number (int or float): the float to format
107  speech (bool): format for speech (True) or display (False)
108  denominators (iter of ints): denominators to use, default [1 .. 20]
109  Returns:
110  (str): The formatted string.
111  """
112  result = convert_to_mixed_fraction(number, denominators)
113  if not result:
114  # Give up, just represent as a 3 decimal number
115  return str(round(number, 3)).replace(".", ",")
116  whole, num, den = result
117  if not speech:
118  if num == 0:
119  # TODO: Number grouping? E.g. "1,000,000"
120  return str(whole)
121  else:
122  return '{} {}/{}'.format(whole, num, den)
123  if num == 0:
124  return str(whole)
125  den_str = FRACTION_STRING_DA[den]
126  if whole == 0:
127  if num == 1:
128  return_string = '{} {}'.format(num, den_str)
129  else:
130  return_string = '{} {}e'.format(num, den_str)
131  else:
132  if num == 1:
133  return_string = '{} og {} {}'.format(whole, num, den_str)
134  else:
135  return_string = '{} og {} {}e'.format(whole, num, den_str)
136 
137  return return_string
138 
139 
140 def pronounce_number_da(num, places=2):
141  """
142  Convert a number to its spoken equivalent
143  For example, '5.2' would return 'five point two'
144  Args:
145  num(float or int): the number to pronounce (set limit below)
146  places(int): maximum decimal places to speak
147  Returns:
148  (str): The pronounced number
149 
150  """
151 
152  def pronounce_triplet_da(num):
153  result = ""
154  num = floor(num)
155  if num > 99:
156  hundreds = floor(num / 100)
157  if hundreds > 0:
158  if hundreds == 1:
159  result += 'et' + 'hundrede' + EXTRA_SPACE
160  else:
161  result += NUM_STRING_DA[hundreds] + \
162  'hundrede' + EXTRA_SPACE
163  num -= hundreds * 100
164  if num == 0:
165  result += '' # do nothing
166  elif num == 1:
167  result += 'et'
168  elif num <= 20:
169  result += NUM_STRING_DA[num] + EXTRA_SPACE
170  elif num > 20:
171  ones = num % 10
172  tens = num - ones
173  if ones > 0:
174  result += NUM_STRING_DA[ones] + EXTRA_SPACE
175  if tens > 0:
176  result += 'og' + EXTRA_SPACE
177  if tens > 0:
178  result += NUM_STRING_DA[tens] + EXTRA_SPACE
179 
180  return result
181 
182  def pronounce_fractional_da(num, places):
183  # fixed number of places even with trailing zeros
184  result = ""
185  place = 10
186  while places > 0:
187  # doesn't work with 1.0001 and places = 2: int(
188  # num*place) % 10 > 0 and places > 0:
189  result += " " + NUM_STRING_DA[int(num * place) % 10]
190  place *= 10
191  places -= 1
192  return result
193 
194  def pronounce_whole_number_da(num, scale_level=0):
195  if num == 0:
196  return ''
197 
198  num = floor(num)
199  result = ''
200  last_triplet = num % 1000
201 
202  if last_triplet == 1:
203  if scale_level == 0:
204  if result != '':
205  result += '' + 'et'
206  else:
207  result += "en"
208  elif scale_level == 1:
209  result += 'et' + EXTRA_SPACE + 'tusinde' + EXTRA_SPACE
210  else:
211  result += "en " + NUM_POWERS_OF_TEN[scale_level] + ' '
212  elif last_triplet > 1:
213  result += pronounce_triplet_da(last_triplet)
214  if scale_level == 1:
215  result += 'tusinde' + EXTRA_SPACE
216  if scale_level >= 2:
217  result += "og" + NUM_POWERS_OF_TEN[scale_level]
218  if scale_level >= 2:
219  if scale_level % 2 == 0:
220  result += "er" # MillionER
221  result += "er " # MilliardER, MillioneER
222 
223  num = floor(num / 1000)
224  scale_level += 1
225  return pronounce_whole_number_da(num,
226  scale_level) + result + EXTRA_SPACE
227 
228  result = ""
229  if abs(num) >= 1000000000000000000000000: # cannot do more than this
230  return str(num)
231  elif num == 0:
232  return str(NUM_STRING_DA[0])
233  elif num < 0:
234  return "minus " + pronounce_number_da(abs(num), places)
235  else:
236  if num == int(num):
237  return pronounce_whole_number_da(num)
238  else:
239  whole_number_part = floor(num)
240  fractional_part = num - whole_number_part
241  result += pronounce_whole_number_da(whole_number_part)
242  if places > 0:
243  result += " komma"
244  result += pronounce_fractional_da(fractional_part, places)
245  return result
246 
247 
249  # ordinals for 1, 3, 7 and 8 are irregular
250  # this produces the base form, it will have to be adapted for genus,
251  # casus, numerus
252 
253  ordinals = ["nulte", "første", "anden", "tredie", "fjerde", "femte",
254  "sjette", "syvende", "ottende", "niende", "tiende"]
255 
256  # only for whole positive numbers including zero
257  if num < 0 or num != int(num):
258  return num
259  if num < 10:
260  return ordinals[num]
261  if num < 30:
262  if pronounce_number_da(num)[-1:] == 'e':
263  return pronounce_number_da(num) + "nde"
264  else:
265  return pronounce_number_da(num) + "ende"
266  if num < 40:
267  return pronounce_number_da(num) + "fte"
268  else:
269  if pronounce_number_da(num)[-1:] == 'e':
270  return pronounce_number_da(num) + "nde"
271  else:
272  return pronounce_number_da(num) + "ende"
273 
274 
275 def nice_time_da(dt, speech=True, use_24hour=False, use_ampm=False):
276  """
277  Format a time to a comfortable human format
278 
279  For example, generate 'five thirty' for speech or '5:30' for
280  text display.
281 
282  Args:
283  dt (datetime): date to format (assumes already in local timezone)
284  speech (bool): format for speech (default/True) or display (False)=Fal
285  use_24hour (bool): output in 24-hour/military or 12-hour format
286  use_ampm (bool): include the am/pm for 12-hour format
287  Returns:
288  (str): The formatted time string
289  """
290  if use_24hour:
291  # e.g. "03:01" or "14:22"
292  string = dt.strftime("%H:%M")
293  else:
294  if use_ampm:
295  # e.g. "3:01 AM" or "2:22 PM"
296  string = dt.strftime("%I:%M %p")
297  else:
298  # e.g. "3:01" or "2:22"
299  string = dt.strftime("%I:%M")
300 
301  if not speech:
302  return string
303 
304  # Generate a speakable version of the time
305  speak = ""
306  if use_24hour:
307  if dt.hour == 1:
308  speak += "et" # 01:00 is "et" not "en"
309  else:
310  speak += pronounce_number_da(dt.hour)
311  if not dt.minute == 0:
312  if dt.minute < 10:
313  speak += ' nul'
314  speak += " " + pronounce_number_da(dt.minute)
315 
316  return speak # ampm is ignored when use_24hour is true
317  else:
318  if dt.hour == 0 and dt.minute == 0:
319  return "midnat"
320  if dt.hour == 12 and dt.minute == 0:
321  return "middag"
322  # TODO: "half past 3", "a quarter of 4" and other idiomatic times
323 
324  if dt.hour == 0:
325  speak += pronounce_number_da(12)
326  elif dt.hour <= 13:
327  if dt.hour == 1 or dt.hour == 13: # 01:00 and 13:00 is "et"
328  speak += 'et'
329  else:
330  speak += pronounce_number_da(dt.hour)
331  else:
332  speak += pronounce_number_da(dt.hour - 12)
333 
334  if not dt.minute == 0:
335  if dt.minute < 10:
336  speak += ' nul'
337  speak += " " + pronounce_number_da(dt.minute)
338 
339  if use_ampm:
340  if dt.hour > 11:
341  if dt.hour < 18:
342  # 12:01 - 17:59 nachmittags/afternoon
343  speak += " om eftermiddagen"
344  elif dt.hour < 22:
345  # 18:00 - 21:59 abends/evening
346  speak += " om aftenen"
347  else:
348  # 22:00 - 23:59 nachts/at night
349  speak += " om natten"
350  elif dt.hour < 3:
351  # 00:01 - 02:59 nachts/at night
352  speak += " om natten"
353  else:
354  # 03:00 - 11:59 morgens/in the morning
355  speak += " om morgenen"
356 
357  return speak
358 
359 
361  # check for months and call nice_ordinal_da declension of ordinals
362  # replace "^" with "hoch" (to the power of)
363  words = text.split()
364 
365  for idx, word in enumerate(words):
366  if word.lower() in months:
367  text = nice_ordinal_da(text)
368 
369  if word == '^':
370  wordNext = words[idx + 1] if idx + 1 < len(words) else ""
371  if wordNext.isnumeric():
372  words[idx] = "opløftet i"
373  text = " ".join(words)
374  return text
375 
376 
377 def nice_ordinal_da(text):
378  # check for months for declension of ordinals before months
379  # depending on articles/prepositions
380  normalized_text = text
381  words = text.split()
382 
383  for idx, word in enumerate(words):
384  wordNext = words[idx + 1] if idx + 1 < len(words) else ""
385  wordPrev = words[idx - 1] if idx > 0 else ""
386  if word[-1:] == ".":
387  if word[:-1].isdecimal():
388  if wordNext.lower() in months:
389  word = pronounce_ordinal_da(int(word[:-1]))
390  if wordPrev.lower() in ["om", "den", "fra", "til",
391  "(fra", "(om", "til"]:
392  word += "n"
393  elif wordPrev.lower() not in ["den"]:
394  word += "r"
395  words[idx] = word
396  normalized_text = " ".join(words)
397  return normalized_text
def nice_number_da(number, speech, denominators)
Definition: format_da.py:101
def pronounce_number_da(num, places=2)
Definition: format_da.py:140
def nice_time_da(dt, speech=True, use_24hour=False, use_ampm=False)
Definition: format_da.py:275
def convert_to_mixed_fraction(number, denominators)


mycroft_ros
Author(s):
autogenerated on Mon Apr 26 2021 02:35:40