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

@@ -3,6 +3,8 @@ use crate::constants::{LAST_COLUMN, LAST_ROW};
use crate::expressions::parser::move_formula::to_string_array_node;
use crate::expressions::parser::static_analysis::add_implicit_intersection;
use crate::expressions::token::{OpSum, OpUnary};
use crate::language::{get_language, Language};
use crate::locale::{get_locale, Locale};
use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str};
pub enum DisplaceData {
@@ -43,17 +45,44 @@ pub enum DisplaceData {
/// This is the internal mode in IronCalc
pub fn to_rc_format(node: &Node) -> String {
stringify(node, None, &DisplaceData::None, false)
#[allow(clippy::expect_used)]
let locale = get_locale("en").expect("");
#[allow(clippy::expect_used)]
let language = get_language("en").expect("");
stringify(node, None, &DisplaceData::None, false, locale, language)
}
/// This is the mode used to display the formula in the UI
pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
stringify(node, Some(context), &DisplaceData::None, false)
pub fn to_localized_string(
node: &Node,
context: &CellReferenceRC,
locale: &Locale,
language: &Language,
) -> String {
stringify(
node,
Some(context),
&DisplaceData::None,
false,
locale,
language,
)
}
/// This is the mode used to export the formula to Excel
pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
stringify(node, Some(context), &DisplaceData::None, true)
#[allow(clippy::expect_used)]
let locale = get_locale("en").expect("");
#[allow(clippy::expect_used)]
let language = get_language("en").expect("");
stringify(
node,
Some(context),
&DisplaceData::None,
true,
locale,
language,
)
}
pub fn to_string_displaced(
@@ -61,7 +90,11 @@ pub fn to_string_displaced(
context: &CellReferenceRC,
displace_data: &DisplaceData,
) -> String {
stringify(node, Some(context), displace_data, false)
#[allow(clippy::expect_used)]
let locale = get_locale("en").expect("");
#[allow(clippy::expect_used)]
let language = get_language("en").expect("");
stringify(node, Some(context), displace_data, false, locale, language)
}
/// Converts a local reference to a string applying some displacement if needed.
@@ -273,19 +306,41 @@ fn format_function(
context: Option<&CellReferenceRC>,
displace_data: &DisplaceData,
export_to_excel: bool,
locale: &Locale,
language: &Language,
) -> String {
let mut first = true;
let mut arguments = "".to_string();
let arg_separator = if locale.numbers.symbols.decimal == "." {
','
} else {
';'
};
for el in args {
if !first {
arguments = format!(
"{},{}",
"{}{}{}",
arguments,
stringify(el, context, displace_data, export_to_excel)
arg_separator,
stringify(
el,
context,
displace_data,
export_to_excel,
locale,
language
)
);
} else {
first = false;
arguments = stringify(el, context, displace_data, export_to_excel);
arguments = stringify(
el,
context,
displace_data,
export_to_excel,
locale,
language,
);
}
}
format!("{name}({arguments})")
@@ -321,11 +376,26 @@ fn stringify(
context: Option<&CellReferenceRC>,
displace_data: &DisplaceData,
export_to_excel: bool,
locale: &Locale,
language: &Language,
) -> String {
use self::Node::*;
match node {
BooleanKind(value) => format!("{value}").to_ascii_uppercase(),
NumberKind(number) => to_excel_precision_str(*number),
BooleanKind(value) => {
if *value {
language.booleans.r#true.to_string()
} else {
language.booleans.r#false.to_string()
}
}
NumberKind(number) => {
let s = to_excel_precision_str(*number);
if locale.numbers.symbols.decimal == "." {
s
} else {
s.replace(".", &locale.numbers.symbols.decimal)
}
}
StringKind(value) => format!("\"{value}\""),
WrongReferenceKind {
sheet_name,
@@ -469,32 +539,95 @@ fn stringify(
}
OpRangeKind { left, right } => format!(
"{}:{}",
stringify(left, context, displace_data, export_to_excel),
stringify(right, context, displace_data, export_to_excel)
stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language
),
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
),
OpConcatenateKind { left, right } => format!(
"{}&{}",
stringify(left, context, displace_data, export_to_excel),
stringify(right, context, displace_data, export_to_excel)
stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language
),
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
),
CompareKind { kind, left, right } => format!(
"{}{}{}",
stringify(left, context, displace_data, export_to_excel),
stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language
),
kind,
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
),
OpSumKind { kind, left, right } => {
let left_str = stringify(left, context, displace_data, export_to_excel);
let left_str = stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language,
);
// if kind is minus then we need parentheses in the right side if they are OpSumKind or CompareKind
let right_str = if (matches!(kind, OpSum::Minus) && matches!(**right, OpSumKind { .. }))
| matches!(**right, CompareKind { .. })
{
format!(
"({})",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
)
} else {
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language,
)
};
format!("{left_str}{kind}{right_str}")
@@ -503,16 +636,44 @@ fn stringify(
let x = match **left {
OpSumKind { .. } | CompareKind { .. } => format!(
"({})",
stringify(left, context, displace_data, export_to_excel)
stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language
)
),
_ => stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language,
),
_ => stringify(left, context, displace_data, export_to_excel),
};
let y = match **right {
OpSumKind { .. } | CompareKind { .. } | OpProductKind { .. } => format!(
"({})",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
),
_ => stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language,
),
_ => stringify(right, context, displace_data, export_to_excel),
};
format!("{x}{kind}{y}")
}
@@ -528,7 +689,14 @@ fn stringify(
| DefinedNameKind(_)
| TableNameKind(_)
| WrongVariableKind(_)
| WrongRangeKind { .. } => stringify(left, context, displace_data, export_to_excel),
| WrongRangeKind { .. } => stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language,
),
OpRangeKind { .. }
| OpConcatenateKind { .. }
| OpProductKind { .. }
@@ -543,7 +711,14 @@ fn stringify(
| ImplicitIntersection { .. }
| EmptyArgKind => format!(
"({})",
stringify(left, context, displace_data, export_to_excel)
stringify(
left,
context,
displace_data,
export_to_excel,
locale,
language
)
),
};
let y = match **right {
@@ -556,9 +731,14 @@ fn stringify(
| DefinedNameKind(_)
| TableNameKind(_)
| WrongVariableKind(_)
| WrongRangeKind { .. } => {
stringify(right, context, displace_data, export_to_excel)
}
| WrongRangeKind { .. } => stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language,
),
OpRangeKind { .. }
| OpConcatenateKind { .. }
| OpProductKind { .. }
@@ -574,29 +754,56 @@ fn stringify(
| ImplicitIntersection { .. }
| EmptyArgKind => format!(
"({})",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
),
};
format!("{x}^{y}")
}
InvalidFunctionKind { name, args } => {
format_function(name, args, context, displace_data, export_to_excel)
}
InvalidFunctionKind { name, args } => format_function(
&name.to_ascii_lowercase(),
args,
context,
displace_data,
export_to_excel,
locale,
language,
),
FunctionKind { kind, args } => {
let name = if export_to_excel {
kind.to_xlsx_string()
} else {
kind.to_string()
kind.to_localized_name(language)
};
format_function(&name, args, context, displace_data, export_to_excel)
format_function(
&name,
args,
context,
displace_data,
export_to_excel,
locale,
language,
)
}
ArrayKind(args) => {
let mut first_row = true;
let mut matrix_string = String::new();
let row_separator = if locale.numbers.symbols.decimal == "." {
';'
} else {
'/'
};
let col_separator = if row_separator == ';' { ',' } else { ';' };
for row in args {
if !first_row {
matrix_string.push(';');
matrix_string.push(row_separator);
} else {
first_row = false;
}
@@ -604,11 +811,11 @@ fn stringify(
let mut row_string = String::new();
for el in row {
if !first_column {
row_string.push(',');
row_string.push(col_separator);
} else {
first_column = false;
}
row_string.push_str(&to_string_array_node(el));
row_string.push_str(&to_string_array_node(el, locale, language));
}
matrix_string.push_str(&row_string);
}
@@ -647,19 +854,40 @@ fn stringify(
if needs_parentheses {
format!(
"-({})",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
)
} else {
format!(
"-{}",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
)
}
}
OpUnary::Percentage => {
format!(
"{}%",
stringify(right, context, displace_data, export_to_excel)
stringify(
right,
context,
displace_data,
export_to_excel,
locale,
language
)
)
}
},
@@ -680,17 +908,38 @@ fn stringify(
add_implicit_intersection(&mut new_node, true);
if matches!(&new_node, Node::ImplicitIntersection { .. }) {
return stringify(child, context, displace_data, export_to_excel);
return stringify(
child,
context,
displace_data,
export_to_excel,
locale,
language,
);
}
return format!(
"_xlfn.SINGLE({})",
stringify(child, context, displace_data, export_to_excel)
stringify(
child,
context,
displace_data,
export_to_excel,
locale,
language
)
);
}
format!(
"@{}",
stringify(child, context, displace_data, export_to_excel)
stringify(
child,
context,
displace_data,
export_to_excel,
locale,
language
)
)
}
}