gistools/readers/gtfs/schedule/translations.rs
1use crate::readers::csv::parse_csv_as_record;
2use alloc::{string::String, vec::Vec};
3use s2json::MValueCompatible;
4
5/// # Translations
6///
7/// **Optional**
8/// Provides language-specific translations for text fields in various GTFS tables.
9/// Each row defines a single translation for a specific field in a specific language,
10/// either targeting a specific record (and possibly sub-record) or matching by field value.
11///
12/// **Primary Key**: (table_name, field_name, language, record_id, record_sub_id, field_value)
13#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
14pub struct GTFSTranslation {
15 /// **Required**
16 /// The table containing the field to be translated.
17 ///
18 /// Allowed values (official spec):
19 /// - "agency"
20 /// - "stops"
21 /// - "routes"
22 /// - "trips"
23 /// - "stop_times"
24 /// - "pathways"
25 /// - "levels"
26 /// - "feed_info"
27 /// - "attributions"
28 ///
29 /// Other optional files (calendar, shapes, etc.) may appear for unofficial field translations.
30 pub table_name: String,
31 /// **Required**
32 /// Name of the field within the table that is being translated.
33 /// Typically text, URL, phone, or email fields.
34 pub field_name: String,
35 /// **Required**
36 /// ISO language code (e.g., "en", "fr", "mul") for this translation.
37 pub language: String,
38 /// **Required**
39 /// The translated value, matching the type of the original field (text, URL, phone, email).
40 pub translation: String,
41 /// **Conditionally Required**
42 /// Identifies the primary key of the record in the table if the table has a unique ID
43 /// (e.g., `agency_id`, `stop_id`, `route_id`, `trip_id`, `pathway_id`, `level_id`, `attribution_id`).
44 /// Required unless `fieldValue` is used or if `tableName=feed_info`.
45 pub record_id: Option<String>,
46 /// **Conditionally Required**
47 /// Secondary key if the table doesn’t have a single unique ID (e.g., stop_sequence for stop_times).
48 /// Required if `recordId` is used and `tableName=stop_times`. Forbidden otherwise.
49 pub record_sub_id: Option<String>,
50 /// **Conditionally Required**
51 /// The exact field value to match for translation if `recordId` and `recordSubId` are not used.
52 /// Forbidden if `recordId` is defined or if `tableName=feed_info`.
53 pub field_value: Option<String>,
54}
55impl GTFSTranslation {
56 /// Create a new GTFSTranslation
57 pub fn new(source: &str) -> Vec<GTFSTranslation> {
58 let mut res = Vec::new();
59 for record in parse_csv_as_record::<GTFSTranslation>(source, None, None) {
60 res.push(record);
61 }
62 res
63 }
64}