Skip to content

Commit 0c1d130

Browse files
marcin-kordas-hocsequbaclaude
authored
Add TEXTJOIN function with i18n and documentation (#1640)
## Summary Adds the `TEXTJOIN` function — joins text from multiple strings and/or ranges with a configurable delimiter. Replaces #1625 (was opened from fork, now from upstream branch directly). ### Features - Scalar and array/range delimiter support with cycling behavior - `ignore_empty` parameter to skip empty strings - Type coercion (numbers, booleans → strings) - Error propagation from both delimiter and text arguments - 32,767 character limit (Excel compatibility) - i18n translations for all 17 supported languages - Documentation in `built-in-functions.md` ### Implementation - New `textjoin` method + `flattenArgToStrings` helper in `TextPlugin` - `repeatLastArgs: 1` metadata pattern (same as SUMPRODUCT, etc.) - Defensive `CellError` check on `coerceScalarToString` return value ### Changed files | File | Change | |------|--------| | `src/interpreter/plugin/TextPlugin.ts` | `textjoin()` + `flattenArgToStrings()` | | `src/error-message.ts` | `TextJoinResultTooLong` message | | `src/i18n/languages/*.ts` (17 files) | TEXTJOIN translations | | `docs/guide/built-in-functions.md` | TEXTJOIN row (alphabetically between TEXT and TRIM) | ### Review feedback addressed (from #1625) - Tests moved to private `hyperformula-tests` repo (companion PR pending) - Fixed docs alphabetical ordering - Fixed unsafe `as string` cast in `flattenArgToStrings` ## Test plan - [x] 35 tests in `hyperformula-tests/unit/interpreter/function-textjoin.spec.ts` - [x] Full suite: 480 suites / 5396 tests passed <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds a new interpreter function (`TEXTJOIN`) with range flattening and type coercion, which touches formula evaluation paths and may introduce edge-case regressions around error propagation and large-string handling. > > **Overview** > Adds the new `TEXTJOIN` spreadsheet function, including interpreter support for joining scalars and ranges with a delimiter (including delimiter cycling), optional skipping of empty strings, and consistent error propagation. > > Introduces a new `ErrorMessage.ResultTooLong` and enforces Excel’s 32,767-character output limit (returning `#VALUE!` when exceeded). > > Updates function documentation, the unreleased changelog, and adds `TEXTJOIN` translations across all supported language packs. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 81426d7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Kuba Sekowski <jakub.sekowski@handsontable.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 72db14b commit 0c1d130

20 files changed

Lines changed: 142 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added a new function: TEXTJOIN. [#1640](https://github.com/handsontable/hyperformula/pull/1640)
13+
1014
### Fixed
1115

1216
- Fixed the IRR function returning `#NUM!` error when the initial investment significantly exceeds the sum of returns. [#1628](https://github.com/handsontable/hyperformula/issues/1628)

docs/guide/built-in-functions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ Total number of functions: **{{ $page.functionsCount }}**
502502
| SUBSTITUTE | Returns string where occurrences of Old_text are replaced by New_text. Replaces only specific occurrence if last parameter is provided. | SUBSTITUTE(Text, Old_text, New_text, [Occurrence]) |
503503
| T | Returns text if given value is text, empty string otherwise. | T(Value) |
504504
| TEXT | Converts a number into text according to a given format.<br>By default, accepts the same formats that can be passed to the [`dateFormats`](../api/interfaces/configparams.md#dateformats) option, but can be further customized with the [`stringifyDateTime`](../api/interfaces/configparams.md#stringifydatetime) option. | TEXT(Number, Format) |
505+
| TEXTJOIN | Joins text from multiple strings and/or ranges with a delimiter. Supports array/range delimiters that cycle through gaps. When ignore_empty is TRUE, empty strings are skipped. Returns #VALUE! if result exceeds 32,767 characters. | TEXTJOIN(Delimiter, Ignore_empty, Text1, [Text2, ...]) |
505506
| TRIM | Strips extra spaces from text. | TRIM("Text") |
506507
| UNICHAR | Returns the character created by using provided code point. | UNICHAR(Number) |
507508
| UNICODE | Returns the Unicode code point of a first character of a text. | UNICODE(Text) |

src/error-message.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export class ErrorMessage {
7373
public static ComplexNumberExpected = 'Complex number expected.'
7474
public static ShouldBeIorJ = 'Should be \'i\' or \'j\'.'
7575
public static SizeMismatch = 'Array dimensions mismatched.'
76+
public static ResultTooLong = 'Result exceeds the maximum allowed length.'
7677
public static FunctionName = (arg: string) => `Function name ${arg} not recognized.`
7778
public static NamedExpressionName = (arg: string) => `Named expression ${arg} not recognized.`
7879
public static LicenseKey = (arg: string) => `License key is ${arg}.`

src/i18n/languages/csCZ.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'TBILLPRICE',
216216
TBILLYIELD: 'TBILLYIELD',
217217
TEXT: 'HODNOTA.NA.TEXT',
218+
TEXTJOIN: 'TEXTJOIN',
218219
TIME: 'ČAS',
219220
TIMEVALUE: 'ČASHODN',
220221
TODAY: 'DNES',

src/i18n/languages/daDK.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'STATSOBLIGATION.KURS',
216216
TBILLYIELD: 'STATSOBLIGATION.AFKAST',
217217
TEXT: 'TEKST',
218+
TEXTJOIN: 'TEKST.KOMBINER',
218219
TIME: 'TID',
219220
TIMEVALUE: 'TIDSVÆRDI',
220221
TODAY: 'IDAG',

src/i18n/languages/deDE.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'TBILLKURS',
216216
TBILLYIELD: 'TBILLRENDITE',
217217
TEXT: 'TEXT',
218+
TEXTJOIN: 'TEXTVERKETTEN',
218219
TIME: 'ZEIT',
219220
TIMEVALUE: 'ZEITWERT',
220221
TODAY: 'HEUTE',

src/i18n/languages/enGB.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ const dictionary: RawTranslationPackage = {
217217
TBILLPRICE: 'TBILLPRICE',
218218
TBILLYIELD: 'TBILLYIELD',
219219
TEXT: 'TEXT',
220+
TEXTJOIN: 'TEXTJOIN',
220221
TIME: 'TIME',
221222
TIMEVALUE: 'TIMEVALUE',
222223
TODAY: 'TODAY',

src/i18n/languages/esES.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'LETRA.DE.TES.PRECIO',
216216
TBILLYIELD: 'LETRA.DE.TES.RENDTO',
217217
TEXT: 'TEXTO',
218+
TEXTJOIN: 'UNIRCADENAS',
218219
TIME: 'NSHORA',
219220
TIMEVALUE: 'HORANUMERO',
220221
TODAY: 'HOY',

src/i18n/languages/fiFI.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'OBLIG.HINTA',
216216
TBILLYIELD: 'OBLIG.TUOTTO',
217217
TEXT: 'TEKSTI',
218+
TEXTJOIN: 'TEKSTI.YHDISTÄ',
218219
TIME: 'AIKA',
219220
TIMEVALUE: 'AIKA_ARVO',
220221
TODAY: 'TÄMÄ.PÄIVÄ',

src/i18n/languages/frFR.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const dictionary: RawTranslationPackage = {
215215
TBILLPRICE: 'PRIX.BON.TRESOR',
216216
TBILLYIELD: 'RENDEMENT.BON.TRESOR',
217217
TEXT: 'TEXTE',
218+
TEXTJOIN: 'JOINDRE.TEXTE',
218219
TIME: 'TEMPS',
219220
TIMEVALUE: 'TEMPSVAL',
220221
TODAY: 'AUJOURDHUI',

0 commit comments

Comments
 (0)