UPDATE: Adds bindings to update timezone and locale

UPDATE: Update "generate locale" utility

FIX: Minor fixes to UI and proper support for locales/timezones

UPDATE: Adds "display language" setting to core
This commit is contained in:
Nicolás Hatcher
2025-11-28 21:21:19 +01:00
parent 402a13bd00
commit ffe5d1a158
109 changed files with 4783 additions and 3216 deletions

View File

@@ -30,7 +30,7 @@ fn main() {
let file_path = path::Path::new(file_name);
let base_name = file_path.file_stem().unwrap().to_str().unwrap();
let output_file_name = &format!("{base_name}.output.xlsx");
let mut model = load_from_xlsx(file_name, "en", "UTC").unwrap();
let mut model = load_from_xlsx(file_name, "en", "UTC", "en").unwrap();
model.evaluate();
println!("Saving result as: {output_file_name}. Please open with Excel and test.");
save_to_xlsx(&model, output_file_name).unwrap();

View File

@@ -29,6 +29,6 @@ fn main() {
&format!("{base_name}.ic")
};
let model = load_from_xlsx(file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(file_name, "en", "UTC", "en").unwrap();
save_to_icalc(&model, output_file_name).unwrap();
}

View File

@@ -185,15 +185,15 @@ pub(crate) fn compare_models(m1: &Model, m2: &Model) -> Result<(), String> {
/// Tests that file in file_path produces the same results in Excel and in IronCalc.
pub fn test_file(file_path: &str) -> Result<(), String> {
let model1 = load_from_xlsx(file_path, "en", "UTC").unwrap();
let mut model2 = load_from_xlsx(file_path, "en", "UTC").unwrap();
let model1 = load_from_xlsx(file_path, "en", "UTC", "en").unwrap();
let mut model2 = load_from_xlsx(file_path, "en", "UTC", "en").unwrap();
model2.evaluate();
compare_models(&model1, &model2)
}
/// Tests that file in file_path can be converted to xlsx and read again
pub fn test_load_and_saving(file_path: &str, temp_dir_name: &Path) -> Result<(), String> {
let model1 = load_from_xlsx(file_path, "en", "UTC").unwrap();
let model1 = load_from_xlsx(file_path, "en", "UTC", "en").unwrap();
let base_name = Path::new(file_path).file_name().unwrap().to_str().unwrap();
@@ -202,7 +202,7 @@ pub fn test_load_and_saving(file_path: &str, temp_dir_name: &Path) -> Result<(),
// test can save
save_to_xlsx(&model1, temp_file_path).unwrap();
// test can open
let mut model2 = load_from_xlsx(temp_file_path, "en", "UTC").unwrap();
let mut model2 = load_from_xlsx(temp_file_path, "en", "UTC", "en").unwrap();
model2.evaluate();
compare_models(&model1, &model2)
}
@@ -214,9 +214,9 @@ mod tests {
#[test]
fn compare_different_sheets() {
let mut model1 = Model::new_empty("model", "en", "UTC").unwrap();
let mut model1 = Model::new_empty("model", "en", "UTC", "en").unwrap();
model1.new_sheet();
let model2 = Model::new_empty("model", "en", "UTC").unwrap();
let model2 = Model::new_empty("model", "en", "UTC", "en").unwrap();
assert!(compare(&model1, &model2).is_err());
}

View File

@@ -8,7 +8,7 @@ use crate::import::load_from_icalc;
use crate::{export::save_to_xlsx, import::load_from_xlsx};
pub fn new_empty_model() -> Model {
Model::new_empty("model", "en", "UTC").unwrap()
Model::new_empty("model", "en", "UTC", "en").unwrap()
}
#[test]
@@ -42,7 +42,7 @@ fn test_values() {
let temp_file_name = "temp_file_test_values.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456");
assert_eq!(
model.get_formatted_cell_value(0, 2, 1).unwrap(),
@@ -66,7 +66,7 @@ fn test_values() {
let temp_file_name = "temp_file_test_values.ic";
save_to_icalc(&model, temp_file_name).unwrap();
let model = load_from_icalc(temp_file_name).unwrap();
let model = load_from_icalc(temp_file_name, "en").unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "123.456");
assert_eq!(
model.get_formatted_cell_value(0, 2, 1).unwrap(),
@@ -95,7 +95,7 @@ fn frozen_rows() {
model.evaluate();
let temp_file_name = "temp_file_test_frozen_rows.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(model.get_frozen_rows_count(0).unwrap(), 23);
fs::remove_file(temp_file_name).unwrap();
}
@@ -107,7 +107,7 @@ fn frozen_columns() {
model.evaluate();
let temp_file_name = "temp_file_test_frozen_columns.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(model.get_frozen_columns_count(0).unwrap(), 42);
fs::remove_file(temp_file_name).unwrap();
}
@@ -120,7 +120,7 @@ fn frozen_rows_and_columns() {
model.evaluate();
let temp_file_name = "temp_file_test_frozen_rows_and_columns.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(model.get_frozen_rows_count(0).unwrap(), 23);
assert_eq!(model.get_frozen_columns_count(0).unwrap(), 42);
fs::remove_file(temp_file_name).unwrap();
@@ -144,7 +144,7 @@ fn test_formulas() {
let temp_file_name = "temp_file_test_formulas.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 2).unwrap(), "11");
assert_eq!(model.get_formatted_cell_value(0, 2, 2).unwrap(), "13");
assert_eq!(model.get_formatted_cell_value(0, 3, 2).unwrap(), "15");
@@ -166,7 +166,7 @@ fn test_sheets() {
let temp_file_name = "temp_file_test_sheets.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert_eq!(
model.workbook.get_worksheet_names(),
vec!["Sheet1", "With space", "Tango & Cash", "你好世界"]
@@ -195,7 +195,7 @@ fn test_named_styles() {
let temp_file_name = "temp_file_test_named_styles.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC", "en").unwrap();
assert!(model
.workbook
.styles

View File

@@ -140,16 +140,21 @@ pub fn load_from_xlsx_bytes(
}
/// Loads a [Model] from an xlsx file
pub fn load_from_xlsx(file_name: &str, locale: &str, tz: &str) -> Result<Model, XlsxError> {
pub fn load_from_xlsx(
file_name: &str,
locale: &str,
tz: &str,
language: &str,
) -> Result<Model, XlsxError> {
let workbook = load_from_excel(file_name, locale, tz)?;
Model::from_workbook(workbook).map_err(XlsxError::Workbook)
Model::from_workbook(workbook, language).map_err(XlsxError::Workbook)
}
/// Loads a [Model] from an `ic` file (a file in the IronCalc internal representation)
pub fn load_from_icalc(file_name: &str) -> Result<Model, XlsxError> {
pub fn load_from_icalc(file_name: &str, language_id: &str) -> Result<Model, XlsxError> {
let contents = fs::read(file_name)
.map_err(|e| XlsxError::IO(format!("Could not extract workbook name: {e}")))?;
let workbook: Workbook = bitcode::decode(&contents)
.map_err(|e| XlsxError::IO(format!("Failed to decode file: {e}")))?;
Model::from_workbook(workbook).map_err(XlsxError::Workbook)
Model::from_workbook(workbook, language_id).map_err(XlsxError::Workbook)
}

View File

@@ -1,11 +1,13 @@
#![allow(clippy::unwrap_used)]
use ironcalc_base::expressions::parser::static_analysis::add_implicit_intersection;
use ironcalc_base::expressions::parser::{
new_parser_english, static_analysis::add_implicit_intersection,
};
use std::{collections::HashMap, io::Read, num::ParseIntError};
use ironcalc_base::{
expressions::{
parser::{stringify::to_rc_format, DefinedNameS, Parser},
parser::{stringify::to_rc_format, DefinedNameS},
token::{get_error_by_english_name, Error},
types::CellReferenceRC,
utils::{column_to_number, parse_reference_a1},
@@ -304,7 +306,7 @@ fn from_a1_to_rc(
tables: HashMap<String, Table>,
defined_names: Vec<DefinedNameS>,
) -> Result<String, XlsxError> {
let mut parser = Parser::new(worksheets.to_owned(), defined_names, tables);
let mut parser = new_parser_english(worksheets.to_owned(), defined_names, tables);
let cell_reference =
parse_reference(&context).map_err(|error| XlsxError::Xml(error.to_string()))?;
let mut t = parser.parse(&formula, &cell_reference);