#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lang {
En,
De,
}
pub struct Text {
pub person: &'static str,
pub given_name: &'static str,
pub family_name: &'static str,
pub additional_names: &'static str,
pub websites: &'static str,
pub country: &'static str,
pub affiliations: &'static str,
pub started: &'static str,
pub ended: &'static str,
pub filters: &'static str,
pub facet_year: &'static str,
pub facet_type: &'static str,
pub open_all: &'static str,
pub close_all: &'static str,
pub clear_filters: &'static str,
pub search: &'static str,
pub search_placeholder: &'static str,
pub copy_citation: &'static str,
pub content: &'static str,
pub published: &'static str,
pub in_container: &'static str,
pub license: &'static str,
pub no_results: &'static str,
pub no_works: &'static str,
pub pagination: &'static str,
pub previous_page: &'static str,
pub next_page: &'static str,
pub language: &'static str,
pub apply: &'static str,
pub featured_only: &'static str,
pub clear: &'static str,
pub export_format: &'static str,
pub citation_style: &'static str,
pub formatted_citation: &'static str,
pub export: &'static str,
pub copy: &'static str,
pub copied: &'static str,
}
const EN: Text = Text {
person: "Person",
given_name: "Given name",
family_name: "Family name",
additional_names: "Also known as",
websites: "Websites & social links",
country: "Country",
affiliations: "Affiliations",
started: "Started",
ended: "Ended at",
filters: "Filters",
facet_year: "Publication year",
facet_type: "Content type",
open_all: "Open all",
close_all: "Close all",
clear_filters: "Clear filters",
search: "Search",
search_placeholder: "Search these works",
copy_citation: "Copy citation",
content: "Content",
published: "published",
in_container: "in",
license: "Distributed under the terms of the",
no_results: "No results found.",
no_works: "No works on this page.",
pagination: "Pagination",
previous_page: "Previous page",
next_page: "Next page",
language: "Language",
apply: "Apply",
featured_only: "Featured only",
clear: "Clear",
export_format: "Export format",
citation_style: "Citation style",
formatted_citation: "Formatted Citation",
export: "Export",
copy: "Copy",
copied: "Copied",
};
const DE: Text = Text {
person: "Person",
given_name: "Vorname",
family_name: "Familienname",
additional_names: "Auch bekannt als",
websites: "Webseiten & soziale Netzwerke",
country: "Land",
affiliations: "Zugehörigkeiten",
started: "Beginn",
ended: "Ende",
filters: "Filter",
facet_year: "Erscheinungsjahr",
facet_type: "Inhaltstyp",
open_all: "Alle öffnen",
close_all: "Alle schließen",
clear_filters: "Filter zurücksetzen",
search: "Suche",
search_placeholder: "Diese Werke durchsuchen",
copy_citation: "Zitat kopieren",
content: "Inhalt",
published: "veröffentlicht",
in_container: "in",
license: "Veröffentlicht unter den Bedingungen der",
no_results: "Keine Ergebnisse gefunden.",
no_works: "Keine Werke auf dieser Seite.",
pagination: "Seitennummerierung",
previous_page: "Vorherige Seite",
next_page: "Nächste Seite",
language: "Sprache",
apply: "Anwenden",
featured_only: "Nur hervorgehobene",
clear: "Löschen",
export_format: "Exportformat",
citation_style: "Zitierstil",
formatted_citation: "Formatiertes Zitat",
export: "Exportieren",
copy: "Kopieren",
copied: "Kopiert",
};
pub struct Pages {
pub library: &'static str,
pub library_version: &'static str,
pub user: &'static str,
pub items: &'static str,
pub attachments: &'static str,
pub notes: &'static str,
pub collections: &'static str,
pub tags: &'static str,
pub in_trash: &'static str,
pub files: &'static str,
pub stored: &'static str,
pub attachment_storage: &'static str,
pub backups: &'static str,
pub backup_last: &'static str,
pub backup_never: &'static str,
pub backup_off: &'static str,
pub backup_failed: &'static str,
pub backup_warning: &'static str,
pub signed_downloads: &'static str,
pub streamed_downloads: &'static str,
pub files_up_to: &'static str,
pub are_accepted: &'static str,
pub public_profile: &'static str,
pub profile_hidden: &'static str,
pub profile_visible: &'static str,
pub it_lists: &'static str,
pub all_of_my_publications: &'static str,
pub show_profile: &'static str,
pub hide_profile: &'static str,
pub connect_zotero: &'static str,
pub connect_requires: &'static str,
pub connect_then: &'static str,
pub enrich: &'static str,
pub enrich_uses: &'static str,
pub sign_in: &'static str,
pub api_key: &'static str,
pub key_holders: &'static str,
pub needs_signing_in: &'static str,
pub sign_in_with: &'static str,
pub sign_in_with_end: &'static str,
pub approve_with_end: &'static str,
pub from_another_site: &'static str,
pub not_a_key: &'static str,
pub sign_in_failed: &'static str,
pub no_orcid: &'static str,
pub not_allowed: &'static str,
pub signing_out: &'static str,
pub signed_out_here: &'static str,
pub continue_signing_out: &'static str,
pub authorize_zotero: &'static str,
pub authorized: &'static str,
pub login_expired: &'static str,
pub no_login_request: &'static str,
pub not_authorized: &'static str,
pub pending_signed_in: &'static str,
pub pending_sign_in: &'static str,
pub pending_key: &'static str,
pub return_to_zotero: &'static str,
pub expired_message: &'static str,
pub missing_message: &'static str,
pub approve: &'static str,
pub approval_from_another_site: &'static str,
pub not_allowed_to_approve: &'static str,
pub signed_in_as: &'static str,
pub storage_used: &'static str,
pub of: &'static str,
pub no_limit: &'static str,
pub over_quota: &'static str,
pub devices: &'static str,
pub devices_intro: &'static str,
pub no_devices: &'static str,
pub revoke: &'static str,
pub added: &'static str,
pub last_used: &'static str,
pub never_used: &'static str,
pub admin: &'static str,
pub invites: &'static str,
pub invites_intro: &'static str,
pub no_invites: &'static str,
pub orcid_id: &'static str,
pub note: &'static str,
pub invite: &'static str,
pub uninvite: &'static str,
pub invite_used: &'static str,
pub not_an_orcid: &'static str,
pub already_a_user: &'static str,
pub not_a_quota: &'static str,
pub users: &'static str,
pub quota_mb: &'static str,
pub default_quota_is: &'static str,
pub empty_for_no_limit: &'static str,
pub save: &'static str,
pub disable: &'static str,
pub enable: &'static str,
pub disabled: &'static str,
pub owner: &'static str,
pub you: &'static str,
pub devices_count: &'static str,
pub sign_in_to_approve: &'static str,
pub key_only_reads: &'static str,
}
const PAGES_EN: Pages = Pages {
library: "Library",
library_version: "Library version",
user: "user",
items: "Items",
attachments: "Attachments",
notes: "Notes",
collections: "Collections",
tags: "Tags",
in_trash: "In trash",
files: "Files",
stored: "Stored",
attachment_storage: "Attachment storage",
backups: "Backups",
backup_last: "Last backup:",
backup_never: "No backup has run since the server started.",
backup_off: "Scheduled backups are off.",
backup_failed: "The last backup failed:",
backup_warning: "With a warning:",
signed_downloads: "Clients download files directly from the bucket through pre-signed URLs.",
streamed_downloads: "Downloads are streamed through this server.",
files_up_to: "Files up to",
are_accepted: "are accepted.",
public_profile: "Public profile",
profile_hidden: "Hidden: the address answers \"not found\".",
profile_visible: "Visible to anyone.",
it_lists: "It lists",
all_of_my_publications: ": everything in My Publications in Zotero.",
show_profile: "Show profile",
hide_profile: "Hide profile",
connect_zotero: "Connect Zotero",
connect_requires: "Requires Zotero 10; only My Library is synced. In Zotero, open Settings → \
Advanced → Config Editor and set (keep the trailing slash):",
connect_then: "Restart Zotero, then log in under Settings → Sync and approve the login on \
this server.",
enrich: "Enrich with commonmeta",
enrich_uses: "commonmeta sync talks to the same API (see docs/commonmeta-rs-changes.md for \
the commonmeta release that supports a remote server):",
sign_in: "Sign in",
api_key: "API key",
key_holders: "This server's library status and settings are shown to holders of one of its \
API keys.",
needs_signing_in: "This server's library status and settings need signing in.",
sign_in_with: "Sign in with",
sign_in_with_end: "",
approve_with_end: "to approve",
from_another_site: "The sign-in was sent from another site.",
not_a_key: "That is not an API key of this library.",
sign_in_failed: "Signing in did not work. Please try again.",
no_orcid: "This account has no ORCID iD, which signing in here needs.",
not_allowed: "This account is not allowed into this library.",
signing_out: "Signing out",
signed_out_here: "You are signed out here. If nothing happens,",
continue_signing_out: "continue signing out",
authorize_zotero: "Authorize Zotero?",
authorized: "Authorized",
login_expired: "Login expired",
no_login_request: "No login request",
not_authorized: "Not authorized",
pending_signed_in: "A Zotero client asked to sync with this library. Approve only if you \
just started the login yourself.",
pending_sign_in: "A Zotero client asked to sync with this library. Sign in to approve, and \
only if you just started the login yourself.",
pending_key: "A Zotero client asked to sync with this library. Approve with an API key that \
may write to it, and only if you just started the login yourself.",
return_to_zotero: "You can close this page and return to Zotero.",
expired_message: "This login request is unknown or has expired. Start the login in Zotero \
again.",
missing_message: "Open this page from Zotero’s Settings → Sync → Log In.",
approve: "Approve",
approval_from_another_site: "The approval was sent from another site.",
not_allowed_to_approve: "You are not allowed to approve logins to this library.",
signed_in_as: "Signed in as",
storage_used: "Storage used",
of: "of",
no_limit: "no limit",
over_quota: "Your storage is full: new attachment files are refused until you delete some or an admin raises your quota.",
devices: "Devices",
devices_intro: "The keys your Zotero logins got. Revoke one to sign that device out.",
no_devices: "No devices yet: log in from Zotero to connect one.",
revoke: "Revoke",
added: "added",
last_used: "last used",
never_used: "not used yet",
admin: "Admin",
invites: "Invitations",
invites_intro: "Someone with an invited ORCID iD can sign in and gets a library of their own.",
no_invites: "Nobody is invited.",
orcid_id: "ORCID iD",
note: "Note",
invite: "Invite",
uninvite: "Withdraw",
invite_used: "now user",
not_an_orcid: "That is not an ORCID iD.",
already_a_user: "That ORCID iD is a user already.",
not_a_quota: "A quota is a number of MB, or nothing for the default.",
users: "Users",
quota_mb: "Quota (MB)",
default_quota_is: "empty for the default:",
empty_for_no_limit: "empty for no limit",
save: "Save",
disable: "Disable",
enable: "Enable",
disabled: "disabled",
owner: "owner",
you: "you",
devices_count: "devices",
sign_in_to_approve: "Sign in to approve logins to this library.",
key_only_reads: "This key may only read; approve with one that may write.",
};
const PAGES_DE: Pages = Pages {
library: "Bibliothek",
library_version: "Bibliotheksversion",
user: "Nutzer",
items: "Einträge",
attachments: "Anhänge",
notes: "Notizen",
collections: "Sammlungen",
tags: "Tags",
in_trash: "Im Papierkorb",
files: "Dateien",
stored: "Gespeichert",
attachment_storage: "Dateispeicher",
backups: "Sicherungen",
backup_last: "Letzte Sicherung:",
backup_never: "Seit dem Start des Servers lief noch keine Sicherung.",
backup_off: "Geplante Sicherungen sind aus.",
backup_failed: "Die letzte Sicherung ist fehlgeschlagen:",
backup_warning: "Mit einer Warnung:",
signed_downloads: "Clients laden Dateien über vorsignierte URLs direkt aus dem Bucket.",
streamed_downloads: "Downloads laufen über diesen Server.",
files_up_to: "Dateien bis",
are_accepted: "werden angenommen.",
public_profile: "Öffentliches Profil",
profile_hidden: "Verborgen: Die Adresse antwortet mit „nicht gefunden“.",
profile_visible: "Für alle sichtbar.",
it_lists: "Es listet",
all_of_my_publications: ": alles aus „Meine Publikationen“ in Zotero.",
show_profile: "Profil anzeigen",
hide_profile: "Profil verbergen",
connect_zotero: "Zotero verbinden",
connect_requires: "Erfordert Zotero 10; nur „Meine Bibliothek“ wird synchronisiert. Öffne \
in Zotero Einstellungen → Erweitert → Konfigurationseditor und setze \
(mit dem Schrägstrich am Ende):",
connect_then: "Starte Zotero neu, melde dich unter Einstellungen → Sync an und bestätige \
die Anmeldung auf diesem Server.",
enrich: "Mit commonmeta anreichern",
enrich_uses: "commonmeta sync nutzt dieselbe API (siehe docs/commonmeta-rs-changes.md zur \
commonmeta-Version, die einen entfernten Server unterstützt):",
sign_in: "Anmelden",
api_key: "API-Schlüssel",
key_holders: "Status und Einstellungen dieser Bibliothek sehen nur Inhaber eines ihrer \
API-Schlüssel.",
needs_signing_in: "Für Status und Einstellungen dieser Bibliothek ist eine Anmeldung nötig.",
sign_in_with: "Mit",
sign_in_with_end: "anmelden",
approve_with_end: "anmelden und bestätigen",
from_another_site: "Die Anmeldung kam von einer anderen Website.",
not_a_key: "Das ist kein API-Schlüssel dieser Bibliothek.",
sign_in_failed: "Die Anmeldung hat nicht geklappt. Bitte versuche es noch einmal.",
no_orcid: "Dieses Konto hat keine ORCID iD, die für die Anmeldung hier nötig ist.",
not_allowed: "Dieses Konto hat keinen Zugang zu dieser Bibliothek.",
signing_out: "Abmelden",
signed_out_here: "Du bist hier abgemeldet. Falls nichts passiert,",
continue_signing_out: "Abmeldung fortsetzen",
authorize_zotero: "Zotero zulassen?",
authorized: "Zugelassen",
login_expired: "Anmeldung abgelaufen",
no_login_request: "Keine Anmeldeanfrage",
not_authorized: "Nicht zugelassen",
pending_signed_in: "Ein Zotero-Client möchte mit dieser Bibliothek synchronisieren. Bestätige \
nur, wenn du die Anmeldung gerade selbst gestartet hast.",
pending_sign_in: "Ein Zotero-Client möchte mit dieser Bibliothek synchronisieren. Melde dich \
an, um zu bestätigen, und nur, wenn du die Anmeldung gerade selbst \
gestartet hast.",
pending_key: "Ein Zotero-Client möchte mit dieser Bibliothek synchronisieren. Bestätige mit \
einem API-Schlüssel mit Schreibrecht, und nur, wenn du die Anmeldung gerade \
selbst gestartet hast.",
return_to_zotero: "Du kannst diese Seite schließen und zu Zotero zurückkehren.",
expired_message: "Diese Anmeldeanfrage ist unbekannt oder abgelaufen. Starte die Anmeldung \
in Zotero noch einmal.",
missing_message: "Öffne diese Seite aus Zotero über Einstellungen → Sync → Anmelden.",
approve: "Bestätigen",
approval_from_another_site: "Die Bestätigung kam von einer anderen Website.",
not_allowed_to_approve: "Du darfst keine Anmeldungen bei dieser Bibliothek bestätigen.",
signed_in_as: "Angemeldet als",
storage_used: "Belegter Speicher",
of: "von",
no_limit: "unbegrenzt",
over_quota: "Dein Speicher ist voll: Neue Anhänge werden abgelehnt, bis du welche löschst oder eine Verwaltungsperson dein Kontingent erhöht.",
devices: "Geräte",
devices_intro: "Die Schlüssel, die deine Zotero-Anmeldungen erhalten haben. Widerrufe einen, um das Gerät abzumelden.",
no_devices: "Noch keine Geräte: Melde dich in Zotero an, um eines zu verbinden.",
revoke: "Widerrufen",
added: "hinzugefügt",
last_used: "zuletzt benutzt",
never_used: "noch nicht benutzt",
admin: "Verwaltung",
invites: "Einladungen",
invites_intro: "Wer eine eingeladene ORCID iD hat, kann sich anmelden und erhält eine eigene Bibliothek.",
no_invites: "Niemand ist eingeladen.",
orcid_id: "ORCID iD",
note: "Notiz",
invite: "Einladen",
uninvite: "Zurückziehen",
invite_used: "jetzt Benutzer",
not_an_orcid: "Das ist keine ORCID iD.",
already_a_user: "Diese ORCID iD gehört schon einem Benutzer.",
not_a_quota: "Ein Kontingent ist eine Zahl in MB, oder leer für den Standard.",
users: "Benutzer",
quota_mb: "Kontingent (MB)",
default_quota_is: "leer für den Standard:",
empty_for_no_limit: "leer für unbegrenzt",
save: "Speichern",
disable: "Sperren",
enable: "Entsperren",
disabled: "gesperrt",
owner: "Inhaber",
you: "du",
devices_count: "Geräte",
sign_in_to_approve: "Melde dich an, um Anmeldungen bei dieser Bibliothek zu bestätigen.",
key_only_reads: "Dieser Schlüssel darf nur lesen; bestätige mit einem mit Schreibrecht.",
};
pub struct LayoutText {
pub lang: &'static str,
pub toggle_dark: &'static str,
pub sign_out: &'static str,
pub language: &'static str,
pub apply: &'static str,
pub tagline: &'static str,
pub built_with: &'static str,
pub by: &'static str,
pub software: &'static str,
pub mit_license: &'static str,
}
pub const LAYOUT_EN: LayoutText = LayoutText {
lang: "en",
toggle_dark: "Toggle dark mode",
sign_out: "Sign out",
language: "Language",
apply: "Apply",
tagline: "Self-hosted Zotero sync and file storage",
built_with: "Built with",
by: "by",
software: "software provided under an",
mit_license: "MIT license",
};
const LAYOUT_DE: LayoutText = LayoutText {
lang: "de",
toggle_dark: "Dunkelmodus umschalten",
sign_out: "Abmelden",
language: "Sprache",
apply: "Anwenden",
tagline: "Selbst gehostete Zotero-Synchronisierung und Dateiablage",
built_with: "Erstellt mit",
by: "von",
software: "Software bereitgestellt unter einer",
mit_license: "MIT-Lizenz",
};
const MONTHS_EN: [&str; 12] = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const MONTHS_DE: [&str; 12] = [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
];
const ITEM_TYPES_DE: &[(&str, &str)] = &[
("artwork", "Kunstwerk"),
("audioRecording", "Tonaufnahme"),
("bill", "Gesetzentwurf"),
("blogPost", "Blogbeitrag"),
("book", "Buch"),
("bookSection", "Buchkapitel"),
("case", "Gerichtsentscheidung"),
("computerProgram", "Software"),
("conferencePaper", "Konferenzbeitrag"),
("dataset", "Datensatz"),
("dictionaryEntry", "Wörterbucheintrag"),
("document", "Dokument"),
("email", "E-Mail"),
("encyclopediaArticle", "Lexikonartikel"),
("film", "Film"),
("forumPost", "Forumsbeitrag"),
("hearing", "Anhörung"),
("instantMessage", "Sofortnachricht"),
("interview", "Interview"),
("journalArticle", "Zeitschriftenartikel"),
("letter", "Brief"),
("magazineArticle", "Magazinartikel"),
("manuscript", "Manuskript"),
("map", "Karte"),
("newspaperArticle", "Zeitungsartikel"),
("patent", "Patent"),
("podcast", "Podcast"),
("preprint", "Preprint"),
("presentation", "Präsentation"),
("radioBroadcast", "Radiosendung"),
("report", "Bericht"),
("standard", "Norm"),
("statute", "Gesetz"),
("thesis", "Abschlussarbeit"),
("tvBroadcast", "Fernsehsendung"),
("videoRecording", "Videoaufnahme"),
("webpage", "Webseite"),
];
const LANGUAGES: &[(&str, &str, &str)] = &[
("ar", "Arabic", "Arabisch"),
("cs", "Czech", "Tschechisch"),
("da", "Danish", "Dänisch"),
("de", "German", "Deutsch"),
("el", "Greek", "Griechisch"),
("en", "English", "Englisch"),
("es", "Spanish", "Spanisch"),
("fi", "Finnish", "Finnisch"),
("fr", "French", "Französisch"),
("he", "Hebrew", "Hebräisch"),
("hu", "Hungarian", "Ungarisch"),
("it", "Italian", "Italienisch"),
("ja", "Japanese", "Japanisch"),
("ko", "Korean", "Koreanisch"),
("nb", "Norwegian", "Norwegisch"),
("nl", "Dutch", "Niederländisch"),
("no", "Norwegian", "Norwegisch"),
("pl", "Polish", "Polnisch"),
("pt", "Portuguese", "Portugiesisch"),
("ru", "Russian", "Russisch"),
("sv", "Swedish", "Schwedisch"),
("tr", "Turkish", "Türkisch"),
("zh", "Chinese", "Chinesisch"),
];
impl Lang {
pub const ALL: [Lang; 2] = [Lang::En, Lang::De];
pub fn parse(code: &str) -> Option<Lang> {
match code.to_ascii_lowercase().as_str() {
"en" => Some(Lang::En),
"de" => Some(Lang::De),
_ => None,
}
}
pub fn from_accept_language(header: &str) -> Lang {
header
.split(',')
.filter_map(|tag| {
let tag = tag.split(';').next()?.trim();
Lang::parse(tag.split('-').next()?)
})
.next()
.unwrap_or(Lang::En)
}
pub fn code(&self) -> &'static str {
match self {
Lang::En => "en",
Lang::De => "de",
}
}
pub fn name(&self) -> &'static str {
match self {
Lang::En => "English",
Lang::De => "Deutsch",
}
}
pub fn citation_locale(self) -> &'static str {
match self {
Lang::En => "en-US",
Lang::De => "de-DE",
}
}
pub fn text(self) -> &'static Text {
match self {
Lang::En => &EN,
Lang::De => &DE,
}
}
pub fn pages(self) -> &'static Pages {
match self {
Lang::En => &PAGES_EN,
Lang::De => &PAGES_DE,
}
}
pub fn decimal(self, number: &str) -> String {
match self {
Lang::En => number.to_string(),
Lang::De => number.replace('.', ","),
}
}
pub fn layout(self) -> &'static LayoutText {
match self {
Lang::En => &LAYOUT_EN,
Lang::De => &LAYOUT_DE,
}
}
pub fn count(&self, n: usize) -> String {
let separator = match self {
Lang::En => ',',
Lang::De => '.',
};
let digits = n.to_string();
let mut out = String::with_capacity(digits.len() + digits.len() / 3);
for (i, digit) in digits.chars().enumerate() {
if i > 0 && (digits.len() - i).is_multiple_of(3) {
out.push(separator);
}
out.push(digit);
}
out
}
pub fn works(self, n: usize) -> String {
let noun = match (self, n) {
(Lang::En, 1) => "work",
(Lang::En, _) => "works",
(_, 1) => "Werk",
(_, _) => "Werke",
};
format!("{} {noun}", self.count(n))
}
pub fn page_of(self, page: usize, pages: usize) -> String {
let (page, pages) = (self.count(page), self.count(pages));
match self {
Lang::En => format!("page {page} of {pages}"),
Lang::De => format!("Seite {page} von {pages}"),
}
}
pub fn date(self, date: &str) -> String {
let parts: Vec<&str> = date.trim().split('-').collect();
let months = match self {
Lang::En => &MONTHS_EN,
Lang::De => &MONTHS_DE,
};
let month = parts
.get(1)
.and_then(|m| m.parse::<usize>().ok())
.and_then(|m| months.get(m.wrapping_sub(1)));
let day = parts.get(2).and_then(|d| d.parse::<u32>().ok());
match (self, parts[0], month, day) {
(Lang::En, year, Some(month), Some(day)) => format!("{month} {day}, {year}"),
(_, year, Some(month), Some(day)) => format!("{day}. {month} {year}"),
(_, year, Some(month), None) => format!("{month} {year}"),
(_, year, _, _) => year.to_string(),
}
}
pub fn date_range(self, start: &str, end: &str) -> String {
let text = self.text();
match (start.trim().is_empty(), end.trim().is_empty()) {
(false, false) => format!("{} – {}", self.date(start), self.date(end)),
(false, true) => format!("{} {}", text.started, self.date(start)),
(true, false) => format!("{} {}", text.ended, self.date(end)),
(true, true) => String::new(),
}
}
pub fn item_type(self, item_type: &str) -> String {
if self != Lang::En
&& let Some((_, label)) = ITEM_TYPES_DE.iter().find(|(t, _)| *t == item_type)
{
return label.to_string();
}
let mut label = String::new();
for (i, c) in item_type.chars().enumerate() {
if i == 0 {
label.extend(c.to_uppercase());
} else if c.is_uppercase() {
label.push(' ');
label.extend(c.to_lowercase());
} else {
label.push(c);
}
}
label
}
pub fn language(self, language: &str) -> String {
let language = language.trim();
let primary = language
.split('-')
.next()
.unwrap_or("")
.to_ascii_lowercase();
match LANGUAGES.iter().find(|(code, _, _)| *code == primary) {
Some((_, en, _)) if self == Lang::En => en.to_string(),
Some((_, _, de)) => de.to_string(),
None => language.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn picks_a_language_from_the_browser() {
let accept = Lang::from_accept_language;
assert_eq!(accept("de-DE,de;q=0.9,en;q=0.8"), Lang::De);
assert_eq!(accept("de-AT,de;q=0.9"), Lang::De);
assert_eq!(accept("fr-FR,fr;q=0.9,de;q=0.8,en;q=0.7"), Lang::De);
assert_eq!(accept("en-GB,de;q=0.5"), Lang::En);
assert_eq!(accept("fr"), Lang::En);
assert_eq!(accept(""), Lang::En);
}
#[test]
fn formats_dates_and_ranges() {
assert_eq!(Lang::En.date("2015"), "2015");
assert_eq!(Lang::En.date("2015-08"), "August 2015");
assert_eq!(Lang::En.date("2021-08-01"), "August 1, 2021");
assert_eq!(Lang::De.date("2021-08-01"), "1. August 2021");
assert_eq!(Lang::De.date("2015-03"), "März 2015");
assert_eq!(Lang::De.date("2021-01-05"), "5. Januar 2021");
assert_eq!(Lang::En.date("May 2021"), "May 2021");
assert_eq!(
Lang::En.date_range("2021-08-01", ""),
"Started August 1, 2021"
);
assert_eq!(
Lang::De.date_range("2021-08-01", ""),
"Beginn 1. August 2021"
);
assert_eq!(
Lang::En.date_range("2015-08", "2021-07-31"),
"August 2015 – July 31, 2021"
);
assert_eq!(Lang::En.date_range("", ""), "");
}
#[test]
fn counts_in_the_page_language() {
assert_eq!(Lang::En.count(1043), "1,043");
assert_eq!(Lang::De.count(1043), "1.043");
assert_eq!(Lang::En.count(999), "999");
assert_eq!(Lang::En.works(1), "1 work");
assert_eq!(Lang::De.works(1043), "1.043 Werke");
assert_eq!(Lang::De.page_of(1, 105), "Seite 1 von 105");
}
#[test]
fn names_types_and_languages() {
assert_eq!(Lang::En.item_type("blogPost"), "Blog post");
assert_eq!(Lang::De.item_type("blogPost"), "Blogbeitrag");
assert_eq!(Lang::De.item_type("unknownType"), "Unknown type");
assert_eq!(Lang::En.language("en-US"), "English");
assert_eq!(Lang::De.language("en"), "Englisch");
assert_eq!(Lang::En.language("Klingon"), "Klingon");
}
}