Skip to content

Commit

Permalink
Merge ddcb94e into f55671d
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed Dec 21, 2023
2 parents f55671d + ddcb94e commit 9e96beb
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 16 deletions.
5 changes: 4 additions & 1 deletion doc/dbus/bus/org.opensuse.Agama1.Locale.bus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@
* The timezone identifier (e.g., "Europe/Berlin").
* A list containing each part of the name in the language set by the
UILocale property.
* The name, in the language set by UILocale, of the main country
associated to the timezone (typically, the name of the city that is
part of the identifier) or empty string if there is no country.
-->
<method name="ListTimezones">
<arg type="a(sas)" direction="out"/>
<arg type="a(sass)" direction="out"/>
</method>
<method name="Commit">
</method>
Expand Down
5 changes: 4 additions & 1 deletion doc/dbus/org.opensuse.Agama1.Locale.doc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
* The timezone identifier (e.g., "Europe/Berlin").
* A list containing each part of the name in the language set by the
UILocale property.
* The name, in the language set by UILocale, of the main country
associated to the timezone (typically, the name of the city that is
part of the identifier) or empty string if there is no country.
-->
<method name="ListTimezones">
<arg type="a(sas)" direction="out"/>
<arg type="a(sass)" direction="out"/>
</method>
<method name="Commit">
</method>
Expand Down
15 changes: 12 additions & 3 deletions rust/agama-dbus-server/src/locale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,21 @@ impl Locale {
/// * The timezone identifier (e.g., "Europe/Berlin").
/// * A list containing each part of the name in the language set by the
/// UILocale property.
fn list_timezones(&self) -> Result<Vec<(String, Vec<String>)>, Error> {
/// * The name, in the language set by UILocale, of the main country
/// associated to the timezone (typically, the name of the city that is
/// part of the identifier) or empty string if there is no country.
fn list_timezones(&self) -> Result<Vec<(String, Vec<String>, String)>, Error> {
let timezones: Vec<_> = self
.timezones_db
.entries()
.iter()
.map(|tz| (tz.code.to_string(), tz.parts.clone()))
.map(|tz| {
(
tz.code.to_string(),
tz.parts.clone(),
tz.country.clone().unwrap_or_default(),
)
})
.collect();
Ok(timezones)
}
Expand Down Expand Up @@ -187,7 +196,7 @@ impl Locale {
};

let mut timezones_db = TimezonesDatabase::new();
timezones_db.read(&locale)?;
timezones_db.read(&ui_locale.language)?;
let mut default_timezone = DEFAULT_TIMEZONE.to_string();
if !timezones_db.exists(&default_timezone) {
default_timezone = timezones_db.entries().get(0).unwrap().code.to_string();
Expand Down
65 changes: 62 additions & 3 deletions rust/agama-dbus-server/src/locale/timezone.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! This module provides support for reading the timezones database.

use crate::error::Error;
use agama_locale_data::territory::Territories;
use agama_locale_data::timezone_part::TimezoneIdParts;
use std::collections::HashMap;

/// Represents a timezone, including each part as localized.
#[derive(Debug)]
Expand All @@ -10,6 +12,8 @@ pub struct TimezoneEntry {
pub code: String,
/// Localized parts (e.g., "Atlántico", "Canarias").
pub parts: Vec<String>,
/// Localized name of the territory this timezone is associated to
pub country: Option<String>,
}

#[derive(Default)]
Expand Down Expand Up @@ -49,13 +53,26 @@ impl TimezonesDatabase {
fn get_timezones(&self, ui_language: &str) -> Result<Vec<TimezoneEntry>, Error> {
let timezones = agama_locale_data::get_timezones();
let tz_parts = agama_locale_data::get_timezone_parts()?;
let territories = agama_locale_data::get_territories()?;
let tz_countries = agama_locale_data::get_timezone_countries()?;
const COUNTRYLESS: [&str; 2] = ["UTC", "Antarctica/South_Pole"];

let ret = timezones
.into_iter()
.map(|tz| {
.filter_map(|tz| {
let parts = translate_parts(&tz, ui_language, &tz_parts);
TimezoneEntry { code: tz, parts }
let country = translate_country(&tz, ui_language, &tz_countries, &territories);
match country {
None if !COUNTRYLESS.contains(&tz.as_str()) => None,
_ => Some(TimezoneEntry {
code: tz,
parts,
country,
}),
}
})
.collect();

Ok(ret)
}
}
Expand All @@ -71,6 +88,23 @@ fn translate_parts(timezone: &str, ui_language: &str, tz_parts: &TimezoneIdParts
.collect()
}

fn translate_country(
timezone: &str,
lang: &str,
countries: &HashMap<String, String>,
territories: &Territories,
) -> Option<String> {
let tz = match timezone {
"Asia/Rangoon" => "Asia/Yangon",
"Europe/Kiev" => "Europe/Kyiv",
_ => timezone,
};
let country_id = countries.get(tz)?;
let territory = territories.find_by_id(country_id)?;
let name = territory.names.name_for(lang)?;
Some(name)
}

#[cfg(test)]
mod tests {
use super::TimezonesDatabase;
Expand All @@ -89,7 +123,32 @@ mod tests {
assert_eq!(
found.parts,
vec!["Europa".to_string(), "Berlín".to_string()]
)
);
assert_eq!(found.country, Some("Alemania".to_string()));
}

#[test]
fn test_read_timezone_without_country() {
let mut db = TimezonesDatabase::new();
db.read("es").unwrap();
let timezone = db
.entries()
.into_iter()
.find(|tz| tz.code == "UTC")
.unwrap();
assert_eq!(timezone.country, None);
}

#[test]
fn test_read_kiev_country() {
let mut db = TimezonesDatabase::new();
db.read("en").unwrap();
let timezone = db
.entries()
.into_iter()
.find(|tz| tz.code == "Europe/Kiev")
.unwrap();
assert_eq!(timezone.country, Some("Ukraine".to_string()));
}

#[test]
Expand Down
28 changes: 25 additions & 3 deletions rust/agama-locale-data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::Context;
use flate2::bufread::GzDecoder;
use quick_xml::de::Deserializer;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
Expand Down Expand Up @@ -92,6 +93,27 @@ pub fn get_timezone_parts() -> anyhow::Result<timezone_part::TimezoneIdParts> {
Ok(ret)
}

/// Returns a hash mapping timezones to its main country (typically, the country of
/// the city that is used to name the timezone). The information is read from the
/// file /usr/share/zoneinfo/zone.tab.
pub fn get_timezone_countries() -> anyhow::Result<HashMap<String, String>> {
const FILE_PATH: &str = "/usr/share/zoneinfo/zone.tab";
let content = std::fs::read_to_string(FILE_PATH)
.with_context(|| format!("Failed to read {}", FILE_PATH))?;

let countries = content
.lines()
.filter_map(|line| {
if line.starts_with('#') {
return None;
}
let fields: Vec<&str> = line.split('\t').collect();
Some((fields.get(2)?.to_string(), fields.first()?.to_string()))
})
.collect();
Ok(countries)
}

/// Gets list of non-deprecated timezones
pub fn get_timezones() -> Vec<String> {
chrono_tz::TZ_VARIANTS
Expand All @@ -115,21 +137,21 @@ mod tests {
#[test]
fn test_get_languages() {
let result = get_languages().unwrap();
let first = result.language.first().expect("no keyboards");
let first = result.language.first().expect("no languages");
assert_eq!(first.id, "aa")
}

#[test]
fn test_get_territories() {
let result = get_territories().unwrap();
let first = result.territory.first().expect("no keyboards");
let first = result.territory.first().expect("no territories");
assert_eq!(first.id, "001") // looks strange, but it is meta id for whole world
}

#[test]
fn test_get_timezone_parts() {
let result = get_timezone_parts().unwrap();
let first = result.timezone_part.first().expect("no keyboards");
let first = result.timezone_part.first().expect("no timezone parts");
assert_eq!(first.id, "Abidjan")
}

Expand Down
6 changes: 6 additions & 0 deletions rust/package/agama-cli.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Thu Dec 21 11:12:45 UTC 2023 - Ancor Gonzalez Sosa <ancor@suse.com>

- The result of ListTimezones includes the localized country name
for each timezone (gh#openSUSE/agama#946)

-------------------------------------------------------------------
Fri Dec 15 16:29:20 UTC 2023 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
5 changes: 5 additions & 0 deletions web/package/cockpit-agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
-------------------------------------------------------------------
Thu Dec 21 11:18:37 UTC 2023 - Ancor Gonzalez Sosa <ancor@suse.com>

- Display countries at timezone selector (gh#openSUSE/agama#946).

-------------------------------------------------------------------
Mon Dec 18 10:42:53 UTC 2023 - José Iván López González <jlopez@suse.com>

Expand Down
4 changes: 2 additions & 2 deletions web/src/assets/styles/blocks.scss
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,14 @@ ul[data-of="agama/keymaps"] {
ul[data-of="agama/timezones"] {
li {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-columns: 1fr 2fr 1fr 1fr;

> :last-child {
grid-column: 1 / -1;
font-size: 80%;
}

> :nth-child(3) {
> :nth-child(4) {
color: var(--color-gray-dimmed);
text-align: end;
}
Expand Down
7 changes: 4 additions & 3 deletions web/src/client/l10n.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const LOCALE_PATH = "/org/opensuse/Agama1/Locale";
* @typedef {object} Timezone
* @property {string} id - Timezone id (e.g., "Atlantic/Canary").
* @property {Array<string>} parts - Name of the timezone parts (e.g., ["Atlantic", "Canary"]).
* @property {string} country - Name of the country associated to the zone or empty string (e.g., "Spain").
* @property {number} utcOffset - UTC offset.
*/

Expand Down Expand Up @@ -230,13 +231,13 @@ class L10nClient {
/**
* @private
*
* @param {[string, Array<string>]} dbusTimezone
* @param {[string, Array<string>, string]} dbusTimezone
* @returns {Timezone}
*/
buildTimezone([id, parts]) {
buildTimezone([id, parts, country]) {
const utcOffset = timezoneUTCOffset(id);

return ({ id, parts, utcOffset });
return ({ id, parts, country, utcOffset });
}

/**
Expand Down
1 change: 1 addition & 0 deletions web/src/components/l10n/TimezoneSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const TimezoneItem = ({ timezone, date }) => {
<>
<div>{part1}</div>
<div>{restParts.join('-')}</div>
<div>{timezone.country}</div>
<div>{time || ""}</div>
<div>{timezone.details}</div>
</>
Expand Down

0 comments on commit 9e96beb

Please sign in to comment.