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}