1pub(crate) mod articles;
13pub(crate) mod conjugate;
14pub mod gender;
15pub(crate) mod numbers;
16pub(crate) mod pluralize;
17
18use prosaic_core::{
19 AgreementFeatures, Conjunction, Gender, GrammaticalNumber, Language, Person, PluralCategory,
20 ReferenceForm, RstRelation, Tense,
21};
22
23pub use articles::indefinite_article;
24use articles::{article_with_features, basic_article};
25use conjugate::{conjugate_de, past_participle_de, present_participle_de};
26use numbers::number_to_words_de;
27use pluralize::{pluralize_de, singularize_de};
28
29#[derive(Debug, Clone, Default)]
35pub struct German;
36
37impl German {
38 pub fn new() -> Self {
39 Self
40 }
41}
42
43impl Language for German {
44 fn pluralize(&self, word: &str, count: usize) -> String {
45 if count == 1 {
46 word.to_string()
47 } else {
48 pluralize_de(word)
49 }
50 }
51
52 fn singularize(&self, word: &str) -> String {
53 singularize_de(word)
54 }
55
56 fn article(&self, word: &str) -> &str {
58 basic_article(word)
59 }
60
61 fn conjugate(&self, verb: &str, tense: Tense, person: Person) -> String {
62 conjugate_de(verb, tense, person)
63 }
64
65 fn past_participle(&self, verb: &str) -> String {
66 past_participle_de(verb)
67 }
68
69 fn present_participle(&self, verb: &str) -> String {
70 present_participle_de(verb)
71 }
72
73 fn join_list(&self, items: &[&str], conjunction: Conjunction) -> String {
74 let conj = match conjunction {
75 Conjunction::And => "und",
76 Conjunction::Or => "oder",
77 };
78 join_list_de(items, conj)
79 }
80
81 fn ordinal(&self, n: usize) -> String {
82 match n {
83 1 => "erste".into(),
84 2 => "zweite".into(),
85 3 => "dritte".into(),
86 4 => "vierte".into(),
87 5 => "fünfte".into(),
88 6 => "sechste".into(),
89 7 => "siebte".into(),
90 8 => "achte".into(),
91 9 => "neunte".into(),
92 10 => "zehnte".into(),
93 _ => format!("{n}."),
94 }
95 }
96
97 fn number_to_words(&self, n: usize) -> String {
98 number_to_words_de(n)
99 }
100
101 fn plural_category(&self, n: i64) -> PluralCategory {
103 match n {
104 1 => PluralCategory::One,
105 _ => PluralCategory::Other,
106 }
107 }
108
109 fn realize_reference(
110 &self,
111 form: ReferenceForm,
112 features: &AgreementFeatures,
113 ) -> Option<String> {
114 match form {
115 ReferenceForm::Pronoun => Some(german_pronoun(features)),
116 ReferenceForm::Possessive => Some(german_possessive(features)),
117 ReferenceForm::Demonstrative => Some(german_demonstrative(features)),
118 ReferenceForm::Zero => None,
119 ReferenceForm::Full | ReferenceForm::ShortName => None,
120 }
121 }
122
123 fn plural_description(
124 &self,
125 entity_type: &str,
126 count: usize,
127 features: &AgreementFeatures,
128 ) -> String {
129 match count {
130 0 => String::new(),
131 1 => format!("{} {}", article_with_features(features), entity_type),
132 _ => {
133 let plural_features = AgreementFeatures::default()
134 .with_gender(features.gender)
135 .with_case(features.case)
136 .with_number(GrammaticalNumber::Plural);
137 format!(
138 "{} {count} {}",
139 article_with_features(&plural_features),
140 self.pluralize(entity_type, count)
141 )
142 }
143 }
144 }
145
146 fn proportion_phrase(
147 &self,
148 matching: i64,
149 total: i64,
150 noun_singular: Option<&str>,
151 features: &AgreementFeatures,
152 ) -> String {
153 german_proportion(matching, total, noun_singular, features)
154 }
155
156 fn discourse_marker(&self, relation: RstRelation) -> Option<&'static str> {
157 use RstRelation::*;
158 Some(match relation {
159 Elaboration => "Außerdem ",
160 Contrast => "Allerdings ",
161 Cause => "Deshalb ",
162 Result => "Folglich ",
163 Concession => "Dennoch ",
164 Sequence => "Dann ",
165 Condition => "Wenn dies geschieht, ",
166 Background => "Inzwischen ",
167 Summary => "Zusammenfassend ",
168 })
169 }
170
171 fn is_connective_opener(&self, text: &str) -> bool {
172 const GERMAN_OPENERS: &[&str] = &[
173 "Außerdem",
174 "Darüber hinaus",
175 "Zudem",
176 "Ebenso",
177 "Allerdings",
178 "Andererseits",
179 "Inzwischen",
180 "Deshalb",
181 "Folglich",
182 "Dennoch",
183 "Dann",
184 "Wenn dies geschieht,",
185 "Zusammenfassend",
186 ];
187 GERMAN_OPENERS.iter().any(|opener| text.starts_with(opener))
188 }
189
190 #[cfg(feature = "time")]
191 fn since_last_marker(&self, diff_secs: i64) -> String {
192 const MINUTE: i64 = 60;
193 const HOUR: i64 = 60 * MINUTE;
194 const DAY: i64 = 24 * HOUR;
195 const WEEK: i64 = 7 * DAY;
196 const MONTH: i64 = 30 * DAY;
197 const YEAR: i64 = 365 * DAY;
198
199 if diff_secs <= 0 {
200 return "zur gleichen Zeit".to_string();
201 }
202 if diff_secs < 60 {
203 return "einen Augenblick später".to_string();
204 }
205 if diff_secs < HOUR {
206 let n = ((diff_secs + MINUTE / 2) / MINUTE).max(1);
207 return match n {
208 1 => "eine Minute später".to_string(),
209 _ => format!("{n} Minuten später"),
210 };
211 }
212 if diff_secs < DAY {
213 let n = ((diff_secs + HOUR / 2) / HOUR).max(1);
214 if n < 6 {
215 return match n {
216 1 => "eine Stunde später".to_string(),
217 _ => format!("{n} Stunden später"),
218 };
219 }
220 return "später am Tag".to_string();
221 }
222 if diff_secs < 2 * DAY {
223 return "am nächsten Tag".to_string();
224 }
225 if diff_secs < WEEK {
226 let n = diff_secs / DAY;
227 return format!("{n} Tage später");
228 }
229 if diff_secs < 2 * WEEK {
230 return "in der folgenden Woche".to_string();
231 }
232 if diff_secs < MONTH {
233 let n = diff_secs / WEEK;
234 return format!("{n} Wochen später");
235 }
236 if diff_secs < 2 * MONTH {
237 return "im folgenden Monat".to_string();
238 }
239 if diff_secs < YEAR {
240 let n = diff_secs / MONTH;
241 return format!("{n} Monate später");
242 }
243 if diff_secs < 2 * YEAR {
244 return "im folgenden Jahr".to_string();
245 }
246 let n = diff_secs / YEAR;
247 format!("{n} Jahre später")
248 }
249}
250
251fn german_pronoun(features: &AgreementFeatures) -> String {
254 let plural = matches!(
255 features.number,
256 GrammaticalNumber::Plural | GrammaticalNumber::Dual
257 );
258 if plural {
259 return "sie".into();
260 }
261 match features.gender {
262 Gender::Fem => "sie".into(),
263 Gender::Neut => "es".into(),
264 _ => "er".into(),
265 }
266}
267
268fn german_possessive(features: &AgreementFeatures) -> String {
269 let plural = matches!(
270 features.number,
271 GrammaticalNumber::Plural | GrammaticalNumber::Dual
272 );
273 if plural {
274 return "ihre".into();
275 }
276 match features.gender {
277 Gender::Fem => "ihre".into(),
278 _ => "sein".into(),
279 }
280}
281
282fn german_demonstrative(features: &AgreementFeatures) -> String {
283 let plural = matches!(
284 features.number,
285 GrammaticalNumber::Plural | GrammaticalNumber::Dual
286 );
287 if plural {
288 return "diese".into();
289 }
290 match features.gender {
291 Gender::Fem => "diese".into(),
292 Gender::Neut => "dieses".into(),
293 _ => "dieser".into(),
294 }
295}
296
297fn resolve_proportion_gender(noun: Option<&str>, features: &AgreementFeatures) -> Gender {
301 match features.gender {
302 Gender::Unknown => noun
303 .and_then(|n| n.split_whitespace().next())
304 .map(gender::infer_gender)
305 .unwrap_or(Gender::Masc),
306 g => g,
307 }
308}
309
310fn german_proportion(
318 matching: i64,
319 total: i64,
320 noun_singular: Option<&str>,
321 features: &AgreementFeatures,
322) -> String {
323 let n = matching.max(0);
324 let t = total.max(0);
325 let gender = resolve_proportion_gender(noun_singular, features);
326
327 if t == 0 {
328 return match (noun_singular, n, gender) {
329 (Some(noun), 0, Gender::Fem) => format!("keine {noun}"),
330 (Some(noun), 0, _) => format!("kein {noun}"),
331 (None, 0, Gender::Fem) => "keine".to_string(),
332 (None, 0, Gender::Neut) => "keines".to_string(),
333 (None, 0, _) => "keiner".to_string(),
334 (Some(noun), _, _) => format!("{n} von 0 {}", pluralize_de(noun)),
336 (None, _, _) => format!("{n} von 0"),
337 };
338 }
339
340 if n == 0 {
341 let none_word = match gender {
342 Gender::Fem => "keine",
343 Gender::Neut => "keines",
344 _ => "keiner",
345 };
346 return match noun_singular {
348 Some(noun) => format!("{none_word} der {t} {}", pluralize_de(noun)),
349 None => format!("{none_word} der {t}"),
350 };
351 }
352
353 if n >= t {
354 return match (noun_singular, t, gender) {
355 (Some(noun), 1, Gender::Fem) => format!("die einzige {noun}"),
356 (Some(noun), 1, Gender::Neut) => format!("das einzige {noun}"),
357 (Some(noun), 1, _) => format!("der einzige {noun}"),
358 (None, 1, Gender::Fem) => "die einzige".to_string(),
359 (None, 1, Gender::Neut) => "das einzige".to_string(),
360 (None, 1, _) => "der einzige".to_string(),
361 (Some(noun), 2, _) => format!("beide {}", pluralize_de(noun)),
363 (None, 2, _) => "beide".to_string(),
364 (Some(noun), _, _) => format!("alle {t} {}", pluralize_de(noun)),
366 (None, _, _) => format!("alle {t}"),
367 };
368 }
369
370 match noun_singular {
371 Some(noun) => format!("{n} von {t} {}", pluralize_de(noun)),
372 None => format!("{n} von {t}"),
373 }
374}
375
376fn join_list_de(items: &[&str], conj: &str) -> String {
377 match items.len() {
378 0 => String::new(),
379 1 => items[0].into(),
380 2 => format!("{} {} {}", items[0], conj, items[1]),
381 _ => {
382 let (last, rest) = items.split_last().unwrap();
383 format!("{} {} {}", rest.join(", "), conj, last)
385 }
386 }
387}
388
389#[cfg(test)]
392mod tests {
393 use super::*;
394 use prosaic_core::Case;
395
396 #[test]
399 fn pluralize_count_one_returns_unchanged() {
400 let de = German::new();
401 assert_eq!(de.pluralize("Klasse", 1), "Klasse");
402 }
403
404 #[test]
405 fn pluralize_count_zero_pluralizes() {
406 let de = German::new();
407 assert_eq!(de.pluralize("Mann", 0), "Männer");
408 }
409
410 #[test]
411 fn pluralize_count_two_pluralizes() {
412 let de = German::new();
413 assert_eq!(de.pluralize("Kind", 2), "Kinder");
414 }
415
416 #[test]
419 fn singularize_delegates() {
420 let de = German::new();
421 assert_eq!(de.singularize("Männer"), "Mann");
422 assert_eq!(de.singularize("Zeitungen"), "Zeitung");
423 }
424
425 #[test]
428 fn article_masc_inferred() {
429 let de = German::new();
430 assert_eq!(de.article("Tisch"), "der");
431 }
432
433 #[test]
434 fn article_fem_inferred() {
435 let de = German::new();
436 assert_eq!(de.article("Freiheit"), "die");
437 }
438
439 #[test]
440 fn article_neut_inferred() {
441 let de = German::new();
442 assert_eq!(de.article("Buch"), "das");
443 }
444
445 #[test]
448 fn conjugate_present_first() {
449 let de = German::new();
450 assert_eq!(
451 de.conjugate("machen", Tense::Present, Person::First),
452 "mache"
453 );
454 }
455
456 #[test]
457 fn conjugate_present_third() {
458 let de = German::new();
459 assert_eq!(
460 de.conjugate("machen", Tense::Present, Person::Third),
461 "macht"
462 );
463 }
464
465 #[test]
466 fn conjugate_past_first() {
467 let de = German::new();
468 assert_eq!(de.conjugate("machen", Tense::Past, Person::First), "machte");
469 }
470
471 #[test]
472 fn conjugate_past_third() {
473 let de = German::new();
474 assert_eq!(de.conjugate("machen", Tense::Past, Person::Third), "machte");
475 }
476
477 #[test]
480 fn past_participle_machen() {
481 let de = German::new();
482 assert_eq!(de.past_participle("machen"), "gemacht");
483 }
484
485 #[test]
486 fn present_participle_machen() {
487 let de = German::new();
488 assert_eq!(de.present_participle("machen"), "machend");
489 }
490
491 fn no_features() -> AgreementFeatures {
496 AgreementFeatures::default()
497 }
498
499 #[test]
500 fn proportion_two_of_two_fem_noun_reads_beide_plural() {
501 let de = German::new();
502 assert_eq!(
503 de.proportion_phrase(2, 2, Some("Datei"), &no_features()),
504 "beide Dateien"
505 );
506 }
507
508 #[test]
509 fn proportion_all_n_with_noun_reads_alle_n_plural() {
510 let de = German::new();
511 assert_eq!(
512 de.proportion_phrase(13, 13, Some("Datei"), &no_features()),
513 "alle 13 Dateien"
514 );
515 }
516
517 #[test]
518 fn proportion_one_of_one_masc_reads_der_einzige() {
519 let de = German::new();
520 assert_eq!(
521 de.proportion_phrase(1, 1, Some("Tisch"), &no_features()),
522 "der einzige Tisch"
523 );
524 }
525
526 #[test]
527 fn proportion_one_of_one_fem_reads_die_einzige() {
528 let de = German::new();
529 assert_eq!(
530 de.proportion_phrase(1, 1, Some("Datei"), &no_features()),
531 "die einzige Datei"
532 );
533 }
534
535 #[test]
536 fn proportion_one_of_one_neut_reads_das_einzige() {
537 let de = German::new();
538 assert_eq!(
539 de.proportion_phrase(1, 1, Some("Buch"), &no_features()),
540 "das einzige Buch"
541 );
542 }
543
544 #[test]
545 fn proportion_zero_of_n_masc_reads_keiner() {
546 let de = German::new();
547 assert_eq!(
548 de.proportion_phrase(0, 5, Some("Tisch"), &no_features()),
549 "keiner der 5 Tische"
550 );
551 }
552
553 #[test]
554 fn proportion_zero_of_n_fem_reads_keine() {
555 let de = German::new();
556 assert_eq!(
557 de.proportion_phrase(0, 5, Some("Datei"), &no_features()),
558 "keine der 5 Dateien"
559 );
560 }
561
562 #[test]
563 fn proportion_zero_of_n_neut_reads_keines() {
564 let de = German::new();
565 assert_eq!(
566 de.proportion_phrase(0, 5, Some("Buch"), &no_features()),
567 "keines der 5 Bücher"
568 );
569 }
570
571 #[test]
572 fn proportion_zero_zero_masc_reads_kein() {
573 let de = German::new();
574 assert_eq!(
575 de.proportion_phrase(0, 0, Some("Tisch"), &no_features()),
576 "kein Tisch"
577 );
578 }
579
580 #[test]
581 fn proportion_zero_zero_fem_reads_keine() {
582 let de = German::new();
583 assert_eq!(
584 de.proportion_phrase(0, 0, Some("Datei"), &no_features()),
585 "keine Datei"
586 );
587 }
588
589 #[test]
590 fn proportion_zero_zero_neut_reads_kein() {
591 let de = German::new();
592 assert_eq!(
593 de.proportion_phrase(0, 0, Some("Buch"), &no_features()),
594 "kein Buch"
595 );
596 }
597
598 #[test]
599 fn proportion_partial_with_noun() {
600 let de = German::new();
601 assert_eq!(
602 de.proportion_phrase(3, 13, Some("Datei"), &no_features()),
603 "3 von 13 Dateien"
604 );
605 }
606
607 #[test]
608 fn proportion_no_noun_two_two_default_masc() {
609 let de = German::new();
610 assert_eq!(de.proportion_phrase(2, 2, None, &no_features()), "beide");
611 }
612
613 #[test]
614 fn proportion_no_noun_all_n() {
615 let de = German::new();
616 assert_eq!(de.proportion_phrase(7, 7, None, &no_features()), "alle 7");
617 }
618
619 #[test]
620 fn proportion_no_noun_partial() {
621 let de = German::new();
622 assert_eq!(de.proportion_phrase(3, 7, None, &no_features()), "3 von 7");
623 }
624
625 #[test]
626 fn proportion_no_noun_one_of_one_default_masc() {
627 let de = German::new();
628 assert_eq!(
629 de.proportion_phrase(1, 1, None, &no_features()),
630 "der einzige"
631 );
632 }
633
634 #[test]
635 fn proportion_no_noun_one_of_one_fem_explicit() {
636 let de = German::new();
637 let f = AgreementFeatures::default().with_gender(Gender::Fem);
638 assert_eq!(de.proportion_phrase(1, 1, None, &f), "die einzige");
639 }
640
641 #[test]
642 fn proportion_no_noun_one_of_one_neut_explicit() {
643 let de = German::new();
644 let f = AgreementFeatures::default().with_gender(Gender::Neut);
645 assert_eq!(de.proportion_phrase(1, 1, None, &f), "das einzige");
646 }
647
648 #[test]
649 fn proportion_no_noun_zero_of_n_masc() {
650 let de = German::new();
651 assert_eq!(
652 de.proportion_phrase(0, 5, None, &no_features()),
653 "keiner der 5"
654 );
655 }
656
657 #[test]
658 fn proportion_no_noun_zero_of_n_fem() {
659 let de = German::new();
660 let f = AgreementFeatures::default().with_gender(Gender::Fem);
661 assert_eq!(de.proportion_phrase(0, 5, None, &f), "keine der 5");
662 }
663
664 #[test]
665 fn proportion_no_noun_zero_zero_default_masc() {
666 let de = German::new();
667 assert_eq!(de.proportion_phrase(0, 0, None, &no_features()), "keiner");
668 }
669
670 #[test]
671 fn proportion_no_noun_zero_zero_neut() {
672 let de = German::new();
673 let f = AgreementFeatures::default().with_gender(Gender::Neut);
674 assert_eq!(de.proportion_phrase(0, 0, None, &f), "keines");
675 }
676
677 #[test]
678 fn proportion_features_gender_overrides_inference() {
679 let de = German::new();
680 let f = AgreementFeatures::default().with_gender(Gender::Fem);
682 assert_eq!(de.proportion_phrase(0, 0, Some("Tisch"), &f), "keine Tisch");
683 }
684
685 #[test]
686 fn join_list_empty() {
687 let de = German::new();
688 assert_eq!(de.join_list(&[], Conjunction::And), "");
689 }
690
691 #[test]
692 fn join_list_single() {
693 let de = German::new();
694 assert_eq!(de.join_list(&["eins"], Conjunction::And), "eins");
695 }
696
697 #[test]
698 fn join_list_two_and() {
699 let de = German::new();
700 assert_eq!(
701 de.join_list(&["eins", "zwei"], Conjunction::And),
702 "eins und zwei"
703 );
704 }
705
706 #[test]
707 fn join_list_three_and_no_oxford_comma() {
708 let de = German::new();
709 assert_eq!(
710 de.join_list(&["eins", "zwei", "drei"], Conjunction::And),
711 "eins, zwei und drei"
712 );
713 }
714
715 #[test]
716 fn join_list_two_or() {
717 let de = German::new();
718 assert_eq!(
719 de.join_list(&["ja", "nein"], Conjunction::Or),
720 "ja oder nein"
721 );
722 }
723
724 #[test]
727 fn ordinal_first_ten() {
728 let de = German::new();
729 assert_eq!(de.ordinal(1), "erste");
730 assert_eq!(de.ordinal(3), "dritte");
731 assert_eq!(de.ordinal(7), "siebte");
732 assert_eq!(de.ordinal(10), "zehnte");
733 }
734
735 #[test]
736 fn ordinal_beyond_ten_numeric() {
737 let de = German::new();
738 assert_eq!(de.ordinal(11), "11.");
739 assert_eq!(de.ordinal(100), "100.");
740 }
741
742 #[test]
745 fn number_to_words_spot_checks() {
746 let de = German::new();
747 assert_eq!(de.number_to_words(3), "drei");
748 assert_eq!(de.number_to_words(21), "einundzwanzig");
749 }
750
751 #[test]
754 fn plural_category_one_is_one() {
755 let de = German::new();
756 assert_eq!(de.plural_category(1), PluralCategory::One);
757 }
758
759 #[test]
760 fn plural_category_zero_is_other() {
761 let de = German::new();
762 assert_eq!(de.plural_category(0), PluralCategory::Other);
763 }
764
765 #[test]
766 fn plural_category_many_is_other() {
767 let de = German::new();
768 assert_eq!(de.plural_category(5), PluralCategory::Other);
769 }
770
771 #[test]
774 fn pronoun_masc_singular() {
775 let de = German::new();
776 let f = AgreementFeatures::default().with_gender(Gender::Masc);
777 assert_eq!(
778 de.realize_reference(ReferenceForm::Pronoun, &f),
779 Some("er".to_string())
780 );
781 }
782
783 #[test]
784 fn pronoun_fem_singular() {
785 let de = German::new();
786 let f = AgreementFeatures::default().with_gender(Gender::Fem);
787 assert_eq!(
788 de.realize_reference(ReferenceForm::Pronoun, &f),
789 Some("sie".to_string())
790 );
791 }
792
793 #[test]
794 fn pronoun_neut_singular() {
795 let de = German::new();
796 let f = AgreementFeatures::default().with_gender(Gender::Neut);
797 assert_eq!(
798 de.realize_reference(ReferenceForm::Pronoun, &f),
799 Some("es".to_string())
800 );
801 }
802
803 #[test]
804 fn pronoun_plural() {
805 let de = German::new();
806 let f = AgreementFeatures::default().with_number(GrammaticalNumber::Plural);
807 assert_eq!(
808 de.realize_reference(ReferenceForm::Pronoun, &f),
809 Some("sie".to_string())
810 );
811 }
812
813 #[test]
814 fn possessive_tracks_owner_gender_and_plural() {
815 let de = German::new();
816 let masc = AgreementFeatures::default().with_gender(Gender::Masc);
817 assert_eq!(
818 de.realize_reference(ReferenceForm::Possessive, &masc),
819 Some("sein".to_string())
820 );
821 let fem = AgreementFeatures::default().with_gender(Gender::Fem);
822 assert_eq!(
823 de.realize_reference(ReferenceForm::Possessive, &fem),
824 Some("ihre".to_string())
825 );
826 let plural = AgreementFeatures::default().with_number(GrammaticalNumber::Plural);
827 assert_eq!(
828 de.realize_reference(ReferenceForm::Possessive, &plural),
829 Some("ihre".to_string())
830 );
831 }
832
833 #[test]
834 fn demonstrative_masc_singular() {
835 let de = German::new();
836 let f = AgreementFeatures::default().with_gender(Gender::Masc);
837 assert_eq!(
838 de.realize_reference(ReferenceForm::Demonstrative, &f),
839 Some("dieser".to_string())
840 );
841 }
842
843 #[test]
844 fn demonstrative_fem_singular() {
845 let de = German::new();
846 let f = AgreementFeatures::default().with_gender(Gender::Fem);
847 assert_eq!(
848 de.realize_reference(ReferenceForm::Demonstrative, &f),
849 Some("diese".to_string())
850 );
851 }
852
853 #[test]
854 fn demonstrative_neut_singular() {
855 let de = German::new();
856 let f = AgreementFeatures::default().with_gender(Gender::Neut);
857 assert_eq!(
858 de.realize_reference(ReferenceForm::Demonstrative, &f),
859 Some("dieses".to_string())
860 );
861 }
862
863 #[test]
864 fn demonstrative_plural() {
865 let de = German::new();
866 let f = AgreementFeatures::default().with_number(GrammaticalNumber::Plural);
867 assert_eq!(
868 de.realize_reference(ReferenceForm::Demonstrative, &f),
869 Some("diese".to_string())
870 );
871 }
872
873 #[test]
874 fn realize_zero_is_none() {
875 let de = German::new();
876 assert_eq!(
877 de.realize_reference(ReferenceForm::Zero, &AgreementFeatures::default()),
878 None
879 );
880 }
881
882 #[test]
883 fn realize_full_is_none() {
884 let de = German::new();
885 assert_eq!(
886 de.realize_reference(ReferenceForm::Full, &AgreementFeatures::default()),
887 None
888 );
889 }
890
891 #[test]
894 fn plural_description_zero_is_empty() {
895 let de = German::new();
896 assert_eq!(
897 de.plural_description("Klasse", 0, &AgreementFeatures::default()),
898 ""
899 );
900 }
901
902 #[test]
903 fn plural_description_one_masc() {
904 let de = German::new();
905 let f = AgreementFeatures::default().with_gender(Gender::Masc);
906 assert_eq!(de.plural_description("Tisch", 1, &f), "der Tisch");
907 }
908
909 #[test]
910 fn plural_description_one_fem() {
911 let de = German::new();
912 let f = AgreementFeatures::default().with_gender(Gender::Fem);
913 assert_eq!(de.plural_description("Klasse", 1, &f), "die Klasse");
914 }
915
916 #[test]
917 fn plural_description_one_neut() {
918 let de = German::new();
919 let f = AgreementFeatures::default().with_gender(Gender::Neut);
920 assert_eq!(de.plural_description("Haus", 1, &f), "das Haus");
921 }
922
923 #[test]
924 fn plural_description_many_masc() {
925 let de = German::new();
926 let f = AgreementFeatures::default().with_gender(Gender::Masc);
927 assert_eq!(de.plural_description("Mann", 3, &f), "die 3 Männer");
928 }
929
930 #[test]
931 fn plural_description_many_fem() {
932 let de = German::new();
933 let f = AgreementFeatures::default().with_gender(Gender::Fem);
934 assert_eq!(de.plural_description("Klasse", 3, &f), "die 3 Klassen");
935 }
936
937 #[test]
938 fn plural_description_many_neut() {
939 let de = German::new();
940 let f = AgreementFeatures::default().with_gender(Gender::Neut);
941 assert_eq!(de.plural_description("Haus", 3, &f), "die 3 Häuser");
942 }
943
944 #[test]
947 fn plural_description_dative_plural_uses_den() {
948 let de = German::new();
949 let f = AgreementFeatures::default()
950 .with_gender(Gender::Masc)
951 .with_case(Case::Dative);
952 assert_eq!(de.plural_description("Mann", 3, &f), "den 3 Männer");
954 }
955
956 #[test]
957 fn plural_description_dative_singular_masc_uses_dem() {
958 let de = German::new();
959 let f = AgreementFeatures::default()
960 .with_gender(Gender::Masc)
961 .with_case(Case::Dative);
962 assert_eq!(de.plural_description("Tisch", 1, &f), "dem Tisch");
963 }
964
965 #[test]
968 fn discourse_marker_german() {
969 let de = German::new();
970 assert_eq!(
971 de.discourse_marker(RstRelation::Elaboration),
972 Some("Außerdem ")
973 );
974 assert_eq!(
975 de.discourse_marker(RstRelation::Contrast),
976 Some("Allerdings ")
977 );
978 assert_eq!(de.discourse_marker(RstRelation::Result), Some("Folglich "));
979 }
980
981 #[test]
984 fn german_is_send_sync() {
985 fn assert_send_sync<T: Send + Sync>() {}
986 assert_send_sync::<German>();
987 }
988
989 #[cfg(feature = "time")]
992 #[test]
993 fn since_last_marker_at_same_time() {
994 let de = German::new();
995 assert_eq!(de.since_last_marker(0), "zur gleichen Zeit");
996 assert_eq!(de.since_last_marker(-5), "zur gleichen Zeit");
997 }
998
999 #[cfg(feature = "time")]
1000 #[test]
1001 fn since_last_marker_augenblick_spaeter() {
1002 let de = German::new();
1003 assert_eq!(de.since_last_marker(30), "einen Augenblick später");
1004 }
1005
1006 #[cfg(feature = "time")]
1007 #[test]
1008 fn since_last_marker_am_naechsten_tag() {
1009 let de = German::new();
1010 assert_eq!(de.since_last_marker(86_400 + 1), "am nächsten Tag");
1011 }
1012
1013 #[cfg(feature = "time")]
1014 #[test]
1015 fn since_last_marker_folgende_woche() {
1016 let de = German::new();
1017 assert_eq!(
1018 de.since_last_marker(7 * 86_400 + 1),
1019 "in der folgenden Woche"
1020 );
1021 }
1022
1023 #[cfg(feature = "time")]
1024 #[test]
1025 fn since_last_marker_monate_spaeter() {
1026 let de = German::new();
1027 assert_eq!(de.since_last_marker(3 * 30 * 86_400), "3 Monate später");
1028 }
1029}