|
43 | 43 | #include <string.h>
|
44 | 44 | #include <cinttypes>
|
45 | 45 | #include <unicode/listformatter.h>
|
| 46 | +#include <unicode/rbnf.h> |
46 | 47 |
|
47 | 48 | #include "qof.h"
|
48 | 49 | #include "gnc-prefs.h"
|
@@ -1496,149 +1497,44 @@ gnc_wrap_text_with_bidi_ltr_isolate (const char* text)
|
1496 | 1497 | /********************************************************************\
|
1497 | 1498 | ********************************************************************/
|
1498 | 1499 |
|
1499 |
| -#define FUDGE .00001 |
1500 |
| - |
1501 |
| -/* This function is basically untranslatable. I'd |
1502 |
| - guess out of the 29 translations we have, 20 will have their number |
1503 |
| - wordings in a totally different way than English has (not to |
1504 |
| - mention gender-dependent number endings). Which means this |
1505 |
| - word-by-word translation will be useless or even plain |
1506 |
| - wrong. For this reason, we don't even start to pretend a |
1507 |
| - word-by-word translation would be of any use, so we don't mark any |
1508 |
| - of these strings for translation. cstim, 2007-04-15. */ |
1509 |
| -static const char* small_numbers[] = |
1510 |
| -{ |
1511 |
| - /* Translators: This section is for generating the "amount, in |
1512 |
| - words" field when printing a check. This function gets the |
1513 |
| - wording right for English, but unfortunately not for most other |
1514 |
| - languages. Decide for yourself whether the check printing is |
1515 |
| - actually needed in your language; if not, you can safely skip the |
1516 |
| - translation of all of these strings. */ |
1517 |
| - "Zero", "One", "Two", "Three", "Four", |
1518 |
| - "Five", "Six", "Seven", "Eight", "Nine", |
1519 |
| - "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", |
1520 |
| - "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", |
1521 |
| - "Twenty" |
1522 |
| -}; |
1523 |
| -static const char* medium_numbers[] = |
| 1500 | +#ifdef _MSC_VER |
| 1501 | +static double round(double x) |
1524 | 1502 | {
|
1525 |
| - "Zero", "Ten", "Twenty", "Thirty", "Forty", |
1526 |
| - "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" |
1527 |
| -}; |
1528 |
| -static const char* big_numbers[] = |
1529 |
| -{ |
1530 |
| - /* Translators: This is the word for the number 10^2 */ |
1531 |
| - "Hundred", |
1532 |
| - /* Translators: This is the word for the number 10^3 */ |
1533 |
| - "Thousand", |
1534 |
| - /* Translators: This is the word for the number 10^6, one thousand |
1535 |
| - thousands. */ |
1536 |
| - "Million", |
1537 |
| - /* Translators: This is the word for the number 10^9, one thousand |
1538 |
| - millions. WATCH OUT: In British English and many other languages |
1539 |
| - this word is used for 10^12 which is one million millions! In |
1540 |
| - contrast to this, here in GnuCash this is used in the American |
1541 |
| - English meaning of 10^9. */ |
1542 |
| - "Billion", |
1543 |
| - /* Translators: This is the word for the number 10^12, one million |
1544 |
| - millions. */ |
1545 |
| - "Trillion", |
1546 |
| - /* Translators: This is the word for the number 10^15 */ |
1547 |
| - "Quadrillion", |
1548 |
| - /* Translators: This is the word for the number 10^18 */ |
1549 |
| - "Quintillion" |
1550 |
| -}; |
| 1503 | + // A simple round() implementation because MSVC doesn't seem to have that |
| 1504 | + return floor(x + 0.5); |
| 1505 | +} |
| 1506 | +#endif |
1551 | 1507 |
|
1552 |
| -static char* |
1553 |
| -integer_to_words(gint64 val) |
| 1508 | +static std::string |
| 1509 | +number_to_words(double val) |
1554 | 1510 | {
|
1555 |
| - if (val == 0) |
1556 |
| - return g_strdup("zero"); |
1557 |
| - if (val < 0) |
1558 |
| - val = -val; |
1559 |
| - |
1560 |
| - auto result = g_string_sized_new(100); |
1561 |
| - |
1562 |
| - while (val >= 1000) |
1563 |
| - { |
1564 |
| - int log_val = log10(val) / 3 + FUDGE; |
1565 |
| - int pow_val = exp(log_val * 3 * G_LN10) + FUDGE; |
1566 |
| - int this_part = val / pow_val; |
1567 |
| - val -= this_part * pow_val; |
1568 |
| - auto tmp = integer_to_words(this_part); |
1569 |
| - g_string_append_printf(result, "%s %s ", tmp, gettext(big_numbers[log_val])); |
1570 |
| - g_free(tmp); |
1571 |
| - } |
1572 |
| - |
1573 |
| - if (val >= 100) |
1574 |
| - { |
1575 |
| - int this_part = val / 100; |
1576 |
| - val -= this_part * 100; |
1577 |
| - g_string_append_printf(result, "%s %s ", |
1578 |
| - gettext(small_numbers[this_part]), |
1579 |
| - gettext(big_numbers[0])); |
1580 |
| - } |
1581 |
| - |
1582 |
| - if (val > 20) |
| 1511 | + UErrorCode status{U_ZERO_ERROR}; |
| 1512 | + icu::RuleBasedNumberFormat formatter{icu::URBNF_SPELLOUT, icu::Locale{}, status}; |
| 1513 | + icu::UnicodeString result; |
| 1514 | + std::string words; |
| 1515 | + if (U_FAILURE(status)) |
1583 | 1516 | {
|
1584 |
| - int this_part = val / 10; |
1585 |
| - val -= this_part * 10; |
1586 |
| - g_string_append(result, gettext(medium_numbers[this_part])); |
1587 |
| - g_string_append_c(result, ' '); |
| 1517 | + PERR("Error creating formatter: %s", u_errorName(status)); |
| 1518 | + return ""; |
1588 | 1519 | }
|
1589 | 1520 |
|
1590 |
| - if (val > 0) |
| 1521 | + formatter.format (std::fabs(val), result, status); |
| 1522 | + if (U_FAILURE(status)) |
1591 | 1523 | {
|
1592 |
| - int this_part = val; |
1593 |
| - g_string_append(result, gettext(small_numbers[this_part])); |
1594 |
| - g_string_append_c(result, ' '); |
| 1524 | + PERR("Error formatting number: %s", u_errorName(status)); |
| 1525 | + return ""; |
1595 | 1526 | }
|
1596 | 1527 |
|
1597 |
| - result = g_string_truncate(result, result->len - 1); |
1598 |
| - return g_string_free(result, FALSE); |
1599 |
| -} |
1600 |
| - |
1601 |
| -#ifdef _MSC_VER |
1602 |
| -static double round(double x) |
1603 |
| -{ |
1604 |
| - // A simple round() implementation because MSVC doesn't seem to have that |
1605 |
| - return floor(x + 0.5); |
1606 |
| -} |
1607 |
| -#endif |
| 1528 | + result.toUTF8String(words); |
| 1529 | + DEBUG ("Number %f in words: %s", val, words.c_str()); |
1608 | 1530 |
|
1609 |
| -char* |
1610 |
| -number_to_words(double val, int64_t denom) |
1611 |
| -{ |
1612 |
| - if (val < 0) val = -val; |
1613 |
| - if (denom < 0) denom = -denom; |
1614 |
| - |
1615 |
| - auto int_part = floor(val); |
1616 |
| - auto frac_part = static_cast<int64_t>(round((val - int_part) * denom)); |
1617 |
| - |
1618 |
| - auto int_string = integer_to_words(int_part); |
1619 |
| - /* Inside of the gettext macro _(...) we must not use any macros but |
1620 |
| - only plain string literals. For this reason, convert the strings |
1621 |
| - separately. */ |
1622 |
| - auto nomin_string = g_strdup_printf("%02" PRId64, frac_part); |
1623 |
| - auto denom_string = g_strdup_printf("%" PRId64, denom); |
1624 |
| - auto full_string = |
1625 |
| - /* Translators: This is for the "amount, in words" field in check |
1626 |
| - printing. The first %s is the integer amount of dollars (or |
1627 |
| - whatever currency), the second and third %s the cent amount as |
1628 |
| - a fraction, e.g. 47/100. */ |
1629 |
| - g_strdup_printf("%s and %s/%s", |
1630 |
| - int_string, nomin_string, denom_string); |
1631 |
| - g_free(int_string); |
1632 |
| - g_free(nomin_string); |
1633 |
| - g_free(denom_string); |
1634 |
| - return full_string; |
| 1531 | + return words; |
1635 | 1532 | }
|
1636 | 1533 |
|
1637 | 1534 | char*
|
1638 | 1535 | numeric_to_words(gnc_numeric val)
|
1639 | 1536 | {
|
1640 |
| - return number_to_words(gnc_numeric_to_double(val), |
1641 |
| - gnc_numeric_denom(val)); |
| 1537 | + return g_strdup(number_to_words (gnc_numeric_to_double(val)).c_str()); |
1642 | 1538 | }
|
1643 | 1539 |
|
1644 | 1540 | const char*
|
|
0 commit comments