From 6d6a8f6b57c393866cf698a2ec7ff220db0e4402 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Tue, 19 May 2026 11:57:27 +0100 Subject: [PATCH 01/11] initial cursor skills and agents files --- .cursor/agents/docs-reviewer.md | 18 +++ .cursor/agents/docs-writer.md | 14 +++ .cursor/commands/new-doc-page.md | 12 ++ .cursor/commands/review-docs.md | 14 +++ .cursor/rules/cortex-docs-repo.mdc | 15 +++ .cursor/rules/hugo-content-markdown.mdc | 13 +++ .cursor/rules/hugo-shortcodes.mdc | 20 ++++ .cursor/rules/security-cursorignore.mdc | 12 ++ .../skills/add-documentation-page/SKILL.md | 28 +++++ .cursor/skills/edit-versioned-images/SKILL.md | 24 ++++ .cursor/skills/shared-sections/SKILL.md | 25 +++++ .cursorignore | 103 ++++++++++++++++++ AGENTS.md | 59 ++++++++++ content/AGENTS.md | 31 ++++++ 14 files changed, 388 insertions(+) create mode 100644 .cursor/agents/docs-reviewer.md create mode 100644 .cursor/agents/docs-writer.md create mode 100644 .cursor/commands/new-doc-page.md create mode 100644 .cursor/commands/review-docs.md create mode 100644 .cursor/rules/cortex-docs-repo.mdc create mode 100644 .cursor/rules/hugo-content-markdown.mdc create mode 100644 .cursor/rules/hugo-shortcodes.mdc create mode 100644 .cursor/rules/security-cursorignore.mdc create mode 100644 .cursor/skills/add-documentation-page/SKILL.md create mode 100644 .cursor/skills/edit-versioned-images/SKILL.md create mode 100644 .cursor/skills/shared-sections/SKILL.md create mode 100644 .cursorignore create mode 100644 AGENTS.md create mode 100644 content/AGENTS.md diff --git a/.cursor/agents/docs-reviewer.md b/.cursor/agents/docs-reviewer.md new file mode 100644 index 000000000..30f769e76 --- /dev/null +++ b/.cursor/agents/docs-reviewer.md @@ -0,0 +1,18 @@ +--- +name: docs-reviewer +description: Reviewer for CORTEX documentation PRs — conventions, scope, security, and Hugo build risks. +--- + +# Docs reviewer + +Review documentation changes critically but constructively. + +Check: + +1. **Scope** — only intended version(s) and files touched. +2. **Conventions** — frontmatter, `ctx`, shortcodes, shared sections. +3. **Links/media** — `url`/`section`/`figure` paths likely valid; flag new `Cortex.*` keys. +4. **Security** — no secrets, config, or ignored paths in the diff. +5. **Consistency** — tone and structure match sibling pages. + +Provide prioritized findings: must-fix vs nice-to-have. diff --git a/.cursor/agents/docs-writer.md b/.cursor/agents/docs-writer.md new file mode 100644 index 000000000..28eedac1e --- /dev/null +++ b/.cursor/agents/docs-writer.md @@ -0,0 +1,14 @@ +--- +name: docs-writer +description: Technical writer for CORTEX Product Portal Hugo/Docsy content. Use for drafting or restructuring customer-facing documentation. +--- + +# Docs writer + +You write and edit CORTEX product documentation in this Hugo/Docsy repo. + +- Follow [AGENTS.md](../../AGENTS.md) and [content/AGENTS.md](../../content/AGENTS.md). +- Plan multi-page work before editing; prefer `_shared` over duplication. +- Use project shortcodes; never hardcode branding where `{{% ctx %}}` is standard. +- Respect `.cursorignore`; ask for `urls.toml` snippets when adding new `url` paths. +- Keep diffs minimal and limited to requested versions. diff --git a/.cursor/commands/new-doc-page.md b/.cursor/commands/new-doc-page.md new file mode 100644 index 000000000..072c1899d --- /dev/null +++ b/.cursor/commands/new-doc-page.md @@ -0,0 +1,12 @@ +--- +name: new-doc-page +description: Create a new CORTEX documentation page from a sibling template in the requested version and section. +--- + +Create a new documentation page for CORTEX Product Portal. + +1. Confirm doc version (e.g. 2026.3) and target folder under `content/en/docs/{version}/`. +2. Open the closest sibling `_index.md` or leaf `.md` and mirror structure. +3. Apply the add-documentation-page skill workflow. +4. List files created/changed and any `urls.toml` keys the user must add (do not read ignored data files). +5. Summarize nav impact (`weight`, parent section). diff --git a/.cursor/commands/review-docs.md b/.cursor/commands/review-docs.md new file mode 100644 index 000000000..90e1ec320 --- /dev/null +++ b/.cursor/commands/review-docs.md @@ -0,0 +1,14 @@ +--- +name: review-docs +description: Review documentation changes for Hugo conventions, branding, links, and scope. +--- + +Review current doc changes (branch diff or open files). + +1. Frontmatter complete; CORTEX literal in title/linkTitle; `ctx` in body where expected. +2. Shortcodes valid (`section` paths exist under `_shared`, `figure` paths under `content/static`). +3. No edits to ignored paths or unrelated doc versions. +4. Tone matches surrounding customer-facing procedural docs. +5. List risks: missing `urls.toml` keys, cross-version drift, broken shared paths. + +Output: brief bullet list of issues and suggested fixes. diff --git a/.cursor/rules/cortex-docs-repo.mdc b/.cursor/rules/cortex-docs-repo.mdc new file mode 100644 index 000000000..440692986 --- /dev/null +++ b/.cursor/rules/cortex-docs-repo.mdc @@ -0,0 +1,15 @@ +--- +description: CORTEX Product Portal repo purpose, structure, and agent workflow +alwaysApply: true +--- + +# CORTEX docs repository + +- Source for https://docs.wearecortex.com/ (Hugo + Docsy). +- Versioned docs: `content/en/docs/{version}/` with `{version}/_shared/`. +- Shortcodes: `layouts/shortcodes/` (`ctx`, `version`, `section`, `url`, `figure`, …). +- Images: `content/static/{version}/images/`. + +Read [AGENTS.md](../../AGENTS.md) before large edits. Contributions: [CORTEX Support](https://support.wearecortex.com/) per README. + +Canonical page: `content/en/docs/2026.3/getting-started/on-premise/install-innovation-only/single-server-without-ha/architecture.md` diff --git a/.cursor/rules/hugo-content-markdown.mdc b/.cursor/rules/hugo-content-markdown.mdc new file mode 100644 index 000000000..16a35b86f --- /dev/null +++ b/.cursor/rules/hugo-content-markdown.mdc @@ -0,0 +1,13 @@ +--- +description: Hugo/Docsy markdown and frontmatter for CORTEX documentation +globs: content/**/*.md +alwaysApply: false +--- + +# Hugo content + +- Frontmatter: `title`, `linkTitle`, `description`, `weight`. +- Literal CORTEX in `title`/`linkTitle`; `{{% ctx %}}` in body where siblings do. +- Reuse `_shared` + `{{< section "/path.md" >}}` instead of copy-paste. +- Internal links: `{{< url path="Cortex...." >}}` like neighboring pages. +- Procedural tone; no drive-by edits across doc versions. diff --git a/.cursor/rules/hugo-shortcodes.mdc b/.cursor/rules/hugo-shortcodes.mdc new file mode 100644 index 000000000..58091abd2 --- /dev/null +++ b/.cursor/rules/hugo-shortcodes.mdc @@ -0,0 +1,20 @@ +--- +description: Custom Hugo shortcode usage and constraints +globs: content/**/*.md, layouts/shortcodes/** +alwaysApply: false +--- + +# Shortcodes + +Read `layouts/shortcodes/*.html` before changing usage. + +| Shortcode | Notes | +| --- | --- | +| `ctx` | Renders CORTEX; not in `title`/`linkTitle` | +| `version` | Uses `{version}/_shared/currentVersion.md` | +| `section` | Path under `{version}/_shared/`; must exist | +| `url` | Keys from `urls.toml` (ignored — ask user for keys) | +| `figure` | `src` under `/images/...`; file in `content/static/{version}/images/` | +| `image` | `src` under `/images/...`; file in `content/static/{version}/images/` | + +Template edits affect all versions — scope testing accordingly. diff --git a/.cursor/rules/security-cursorignore.mdc b/.cursor/rules/security-cursorignore.mdc new file mode 100644 index 000000000..20bbdd05a --- /dev/null +++ b/.cursor/rules/security-cursorignore.mdc @@ -0,0 +1,12 @@ +--- +description: Respect .cursorignore — no secrets, config, or data file access +alwaysApply: true +--- + +# Security + +Follow [.cursorignore](../../.cursorignore). + +Do not read/commit: `config.*`, `data/**`, `urls.toml`, CI configs, `.env*`, certs/keys, `public/**`, `node_modules/**`, internal/draft content paths. + +If link keys are needed, copy from an existing page in the same section or ask the user. diff --git a/.cursor/skills/add-documentation-page/SKILL.md b/.cursor/skills/add-documentation-page/SKILL.md new file mode 100644 index 000000000..100990de4 --- /dev/null +++ b/.cursor/skills/add-documentation-page/SKILL.md @@ -0,0 +1,28 @@ +--- +name: add-documentation-page +description: Add a new Hugo documentation page under a versioned CORTEX docs section. Use when creating topics, install steps, reference pages, or _index.md section roots. +--- + +# Add documentation page + +## When to use + +- New leaf page or section `_index.md` under `content/en/docs/{version}/` +- User names a doc version (e.g. 2026.3) and section path + +## Steps + +1. Find a sibling page in the same folder; copy frontmatter shape (`title`, `linkTitle`, `description`, `weight`). +2. Set `weight` relative to neighbors (lower = earlier in sidebar). +3. Use literal CORTEX in `title`/`linkTitle`; `{{% ctx %}}` in body. +4. For install/guide style, use `# {{% param title %}}` and match callouts (`pageinfo`, `alert`). +5. Add next-step links with `{{< url path="Cortex...." >}}` — reuse keys from sibling pages. +6. If content repeats across pages, add `{version}/_shared/...` and `{{< section "/path.md" >}}` instead. +7. Do not edit other version trees unless requested. + +## Checklist + +- [ ] Frontmatter complete +- [ ] Branding matches `ctx` convention +- [ ] Links follow section `url` patterns +- [ ] Scope limited to requested version diff --git a/.cursor/skills/edit-versioned-images/SKILL.md b/.cursor/skills/edit-versioned-images/SKILL.md new file mode 100644 index 000000000..39b6ca8b0 --- /dev/null +++ b/.cursor/skills/edit-versioned-images/SKILL.md @@ -0,0 +1,24 @@ +--- +name: edit-versioned-images +description: Add or update versioned diagrams and screenshots under content/static. Use for figures, architecture diagrams, or draw.io editable assets. +paths: content/static/**/* +--- + +# Versioned images + +## When to use + +- New/updated `{{< figure >}}` or diagram in docs +- User mentions draw.io, editable folder, or architecture screenshots + +## Steps + +1. Confirm doc version → static folder `content/static/{version}/images/`. +2. Editable diagrams: `images/editable/` (see `content/static/2026.3/images/_ReadMe.md`). +3. Reference in markdown: `{{< figure src="/images/editable/....png" class="centre" title="..." >}}`. +4. Ensure file exists on disk before referencing (figure shortcode checks `content/static/{version}/...`). +5. For new versions, mirror structure from the latest version's `images/` tree when appropriate. + +## Screenshot conventions (from _ReadMe) + +- Service Fabric screenshots: 1500×700, device emulation, refresh off where noted. diff --git a/.cursor/skills/shared-sections/SKILL.md b/.cursor/skills/shared-sections/SKILL.md new file mode 100644 index 000000000..acafa71bb --- /dev/null +++ b/.cursor/skills/shared-sections/SKILL.md @@ -0,0 +1,25 @@ +--- +name: shared-sections +description: Create or edit reusable _shared markdown included via the section shortcode. Use for duplicated install, architecture, or upgrade steps within a doc version. +paths: content/**/_shared/**/*.md +--- + +# Shared sections + +## When to use + +- Same paragraph/block appears on multiple pages in one version +- User asks to DRY up install/architecture/upgrade content + +## Steps + +1. Locate `{version}/_shared/` (e.g. `content/en/docs/2026.3/_shared/`). +2. Find existing fragment before creating a new file. +3. Add or edit `.md` under `_shared/` with normal markdown (no page frontmatter required for fragments). +4. Include from pages: `{{< section "/relative/path/from/_shared.md" >}}` (leading path as in `architecture.md`). +5. Fix all includers if you rename or move a shared file. + +## Pitfalls + +- Path must resolve for the page's version or Hugo errors at build. +- Shared files are per-version; syncing across 2026.3 / 2025.9 requires explicit user approval. diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 000000000..a580df2d1 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,103 @@ +# ============================================ +# Hugo / Docsy Sensitive Data Protection +# ============================================ + +# Core secrets (keep from previous list) +.env +.env.* +*.env +*.secret +*.secrets + +# Hugo config files (frequently contain URLs, tokens, etc.) +config.toml +config.yaml +config.yml +config.json + +# If you split config: +config/**/production* +config/**/prod* +config/**/secrets* +config/**/private* + +# Hugo module cache (can include fetched private modules) +resources/_gen/** +.hugo_build.lock + +# Content drafts / unpublished content +content/**/drafts/** +content/**/_drafts/** +content/**/private/** +content/**/internal/** + +# Frontmatter-heavy markdown (may contain PII / metadata leaks) +# (Optional depending on your workflow) +# content/**/*.md + +# Data files (VERY important in Hugo) +data/** +*.yaml +*.yml +*.json +*.toml +*.csv + +# Docsy-specific +# Includes navigation and structure that may leak internal structure +content/en/docs/internal/** +content/en/docs/private/** +content/en/docs/drafts/** + +# Static assets (can accidentally contain secrets) +static/uploads/** +static/private/** +static/internal/** +static/files/** +static/data/** + +# Built output (can contain rendered secrets if misconfigured) +public/** + +# Scripts / build tooling +scripts/** +tools/** + +# Node dependencies (Docsy commonly uses npm tooling) +node_modules/** +package-lock.json +pnpm-lock.yaml +yarn.lock + +# CI/CD configs (often contain refs to secrets) +.github/workflows/** +.gitlab-ci.yml +.azure-pipelines.yml + +# Logs / temporary files +*.log +tmp/** +.cache/** + +# ============================================ +# Global sensitive patterns +# ============================================ +**/*password* +**/*secret* +**/*credential* +**/*token* +**/*apikey* +**/*private* + +# Certificates & keys +*.pem +*.key +*.crt +*.p12 +*.pfx + +# Cloud credentials +.aws/** +.azure/** +.gcp/** +*service-account*.json \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..554300f0b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,59 @@ +# CORTEX Product Portal — Agent Instructions + +This repository is the source for the [CORTEX Product Portal](https://docs.wearecortex.com/) (Hugo + Docsy). Use these instructions when editing documentation, layouts, or static assets. + +## Repository purpose + +- **Site**: Public product documentation for the CORTEX automation platform. +- **Stack**: Hugo static site with Docsy theme, custom shortcodes under `layouts/shortcodes/`, versioned content under `content/en/docs/`. +- **Contributions**: Per [README.md](README.md), external contributions and feedback go through [CORTEX Support](https://support.wearecortex.com/). Do not assume undocumented publish or deploy steps. + +## How to work in this repo + +### Plan before large edits + +For multi-page or cross-version changes, outline affected paths first (which version folders, `_shared` snippets, `urls.toml` entries, images). Prefer small, reviewable diffs over sweeping rewrites. + +### Versioned documentation + +- Product docs live under `content/en/docs/{version}/` (e.g. `2026.3`, `2025.9`, `2024.9`). +- Each version has a `_shared/` folder for reusable sections included via the `section` shortcode. +- When changing behavior that applies to multiple releases, identify whether content is duplicated per version or centralized in `_shared` before editing. +- Canonical example: `content/en/docs/2026.3/getting-started/on-premise/install-innovation-only/single-server-without-ha/architecture.md` + +### Branding and naming + +- In page **body** text, use `{{% ctx %}}` for the product name — do not hardcode "CORTEX" in prose when the shortcode applies. +- In YAML **`title`** and **`linkTitle`**, use the literal name (e.g. `CORTEX`) — shortcodes are not used there. +- Release label in prose: `{{< version >}}` (reads `{version}/_shared/currentVersion.md`). + +### Images and diagrams + +- Versioned assets: `content/static/{version}/images/` (`editable/` for draw.io). +- Use `{{< figure src="/images/..." >}}`; see `layouts/shortcodes/figure.html`. +- Diagram notes: `content/static/2026.3/images/_ReadMe.md` + +### Security and ignored paths + +Respect [.cursorignore](.cursorignore). Do not read or commit Hugo config, `data/**`, CI workflows, secrets, or internal/draft paths. If `urls.toml` is needed, ask the user for the relevant key or snippet. + +## Project agent assets + +| Asset | Location | +| --- | --- | +| Rules | `.cursor/rules/*.mdc` | +| Skills | `.cursor/skills/*/SKILL.md` | +| Commands | `.cursor/commands/*.md` | +| Agents | `.cursor/agents/*.md` | + +## Quality bar + +- Procedural, customer-facing tone; preserve frontmatter (`title`, `linkTitle`, `description`, `weight`). +- Prefer shortcodes over raw HTML; match `{{< url path="Cortex...." >}}` patterns in the same section. +- Scope changes to the version(s) the user requested. + +## What not to do + +- Do not modify `public/`, `node_modules/`, or ignored config/data files. +- Do not invent install commands or CI steps not documented in-repo. +- Do not remove or weaken `.cursorignore` without explicit user approval. diff --git a/content/AGENTS.md b/content/AGENTS.md new file mode 100644 index 000000000..4d844c0e6 --- /dev/null +++ b/content/AGENTS.md @@ -0,0 +1,31 @@ +# Content authoring (Hugo / Docsy) + +Applies when editing files under `content/`. + +## Page types + +| File | Purpose | +| --- | --- | +| `_index.md` | Section landing; `linkTitle` + `weight` control nav | +| `*.md` | Leaf topic | +| `_shared/**/*.md` | Fragments via `{{< section "/path.md" >}}` | + +## Frontmatter + +```yaml +--- +title: "Page Title" +linkTitle: "Nav Label" +description: "Summary for indexes and SEO." +weight: 10 +--- +``` + +- `title` / `linkTitle`: literal **CORTEX**, not `{{% ctx %}}`. +- `description`: may use `{{% ctx %}}` in prose. + +## Body + +- Match siblings: `# {{% param title %}}`, `pageinfo`, `alert`, `figure`, `url`, `section`, `tab`/`tabpane`. +- Search `{version}/_shared/` before duplicating paragraphs. +- Do not edit other version folders unless explicitly asked. From df2f16f4d9eae4831b9091d146f11122fc57666a Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Tue, 19 May 2026 15:04:53 +0100 Subject: [PATCH 02/11] used cursor to generate what is a collection? and its items page --- .../working-with/collections/items.md | 174 +++++++++++-- .../collections/what-is-a-collection.md | 241 ++++++++++++++---- data/urls.toml | 12 +- 3 files changed, 359 insertions(+), 68 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md index 816bde126..ad712b3f2 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md @@ -1,25 +1,85 @@ --- title: "Items" linkTitle: "Items" -description: "Information related to working with Items." +description: "Information related to working with items in collections." weight: 100 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +An **item** is a single value stored in a [collection][]. Collections group items so they can be passed between blocks, read or updated in expressions, and processed in a flow. + +In collection data types, the item type is usually written as `TItem` (a [generic type parameter][]). For example, in `List` each item is an `Int32`; in `Dictionary` each item may be any data type. See [What is a Collection?][] for how collection types differ. + +| Collection type | How items are identified | Item type parameter | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | [Key][] | `TItem` | +| [Structure][] | [String][] [key][] or property name | Any data type | +| [List<TItem>][] | Zero-based [index][] | `TItem` | +| [Array][] | Zero-based [index][] | Element type of the array | +| [QueueWithPriority<TItem, TPriority>][] | Position in queue (not random access) | `TItem` | +| [Data Storage Collection][] | [String][] [key][] (`Data` value) | [dynamic][] | + +Items are not the same as [keys][]: in key/item collections, the key is used to look up the item. In ordered collections, the [index][] (or [occurrence][] when matching by value) identifies which item you mean. + +## Item data types + +### Homogenous and heterogenous items + +A collection is [homogenous][] when every item has the same data type (for example `List` or `Dictionary`). It is [heterogenous][] when items may have different data types (for example `List`, `List`, or `Dictionary`). + +Literal syntax in the [Expression Editor][] often infers item types from the values you provide—for example `[1, 2, 3]` creates a `List`, while `["Some Text", true, 1]` creates a `List`. See [Create a List<TItem>][] and [Create a Dictionary<TKey, TItem>][]. + +### Null items + +Whether an item can be `null` depends on the collection and how it is used: + +* [Keys][] cannot be `null` in dictionaries and structures. +* Items may be `null` in some collections (for example a `List` may contain `null` entries). Some blocks throw [InvalidPropertyValueException][] when `null` is not allowed for the declared `TItem` type. + +Check the block or data type documentation for the collection you are using. + +## Accessing items + +### In the Expression Editor + +How you read or assign an item depends on the collection type: + +| Collection type | Typical syntax | More information | +| --- | --- | --- | +| [List<TItem>][] / [Array][] | `list[0]`, `list[^1]`, `list[0..2]` | [Index expressions][], [Indexes][] | +| [Dictionary<TKey, TItem>][] | `dictionary["Key1"]` | [Index expressions][], [Keys][] | +| [Structure][] | `structure.Name` or `structure["any-key"]` | [Property expressions][], [Index expressions][] | + +[List][] and [Array][] items use zero-based indexes. [Dictionary][] and [Structure][] items use keys. Range and index-from-end syntax (`^1`, `..`) follow C# rules; see [Indices and Ranges][] in the [Expression Editor][] documentation. + +### With blocks + +[Dictionary][], [List][], [Queue][], and [Data Storage][] blocks expose properties such as `Item`, `Value`, or `Data` for the item being added, read, updated, or removed. Access patterns include: + +* **By key** — [Get Item With Key][], [Set Item With Key][], [Read Data With Key][] +* **By index** — [Get Item At Index][], [Set Item At Index][] +* **By value and occurrence** — [Remove Item With Value][], [Get Index Of Item With Value][] (see [Occurrences][]) +* **Queue order** — [Enqueue Item][], [Dequeue Item][], [Peek Item][] (items are not selected by index or key) + +For a full list of collection types and access patterns, see [What is a Collection?][]. + +## Comparing and matching items + +Many blocks need to know whether an item in a collection **matches** a value you supply—for example when removing items with a given value or checking whether a key is already present. + +How matching works depends on whether you use C# expressions or blocks: + +| Context | How equality is determined | +| --- | --- | +| C# syntax in the [Expression Editor][] | [Reference equality][] for reference types and [value equality][] for value types, following standard C# rules (including `==` and `Equals` where applicable). | +| [List][] and [Dictionary][] blocks | For reference types, [reference equality][] is tried first; if no matching reference is found, comparison falls back to [value equality][]. For value types, [value equality][] is used. | + +This difference matters when items are reference types (for example two different `Structure` instances with the same field values). Blocks may treat them as equal when C# `==` would not. -- Overview/summary -- What is an Item? -- How are they accessed? - - Link to Indexes - - Link to Keys -- If you are using C# syntax, then the equality is done using reference equality for reference types, and value equality for value types -- If you are using List of Dictionary blocks, then the equality is done using reference equality for reference types and falls back to value equality if no reference was found, and value equality for value types +For examples and affected blocks, see [Object Equality][]. For general C# equality rules, see [Equality comparisons][MS Equality]. ## Remarks @@ -31,16 +91,100 @@ None ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Keys][] +* [Indexes][] +* [Occurrences][] +* [Generics][] +* [Object Equality][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [List<TItem>][] +* [Array][] +* [QueueWithPriority<TItem, TPriority>][] +* [IDictionary<TKey, TItem>][] +* [IList<TItem>][] +* [dynamic][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][], [Get Item With Key][]) +* [List][] blocks (for example [Add Item At End][], [Get Item At Index][], [Remove Item With Value][]) +* [Queue][] blocks (for example [Enqueue Item][], [Dequeue Item][], [Peek Item][]) +* [Data Storage][] blocks (for example [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [Equality comparisons (C#)][MS Equality] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[collection]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[generic type parameter]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[homogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Homogenous" >}} +[heterogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Heterogenous" >}} +[reference equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[value equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} + +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Queue]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} + +[Create a List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.CreateNew" >}} +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} +[Property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} +[Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} + +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd.MainDoc" >}} +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Set Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemAtIndex.MainDoc" >}} +[Remove Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemWithValue.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} +[Enqueue Item]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue Item]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek Item]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} + +[InvalidPropertyValueException]: {{< url path="Cortex.Reference.Exceptions.Flows.Blocks.InvalidPropertyValueException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS Equality]: {{< url path="MSDocs.CSharp.EqualityOperators" >}} +[System.Collections.Generic.Dictionary<TKey, TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md index 99be3b3b1..473bfda9c 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md @@ -7,102 +7,153 @@ weight: 1 # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO +A collection is a data structure that groups multiple values (called [items][]) so they can be stored, passed between blocks, and processed together in a flow. + +{{% ctx %}} supports several collection types. Each type differs in how items are ordered, how they are accessed, and whether the collection exists only for the duration of an execution or is persisted on the platform. The main types are: + +| Collection type | Typical access | Ordered | Persisted | +| --- | --- | --- | --- | +| [Dictionary][] | [Key][] | No | No | +| [Structure][] | [Key][] or property | No | No | +| [List][] | [Index][] | Yes | No | +| [Array][] | [Index][] | Yes | No | +| [Queue][] | Dequeue, peek, or enumerate | Yes (by priority, then FIFO) | No | +| [Data Storage Collection][] | [Key][] only | No | Yes | + +For more detail on [keys][], [indexes][], and [items][], see the related concept pages in this section. ## Types of Collections ### Dictionaries -TODO: +A [Dictionary<TKey, TItem>][] is a collection of key/item pairs based on [System.Collections.Generic.Dictionary<TKey, TItem>][]. + +* `TKey` is the data type of each [key][] used to access an item. +* `TItem` is the data type of each [item][] stored in the collection. + +Each item is accessed by its key. In the [Expression Editor][], dictionary items are accessed using [index expressions][] (for example `dictionary["StringKey1"]`). + +#### Homogenous and heterogenous dictionaries + +A dictionary is [homogenous][] when every item has the same data type (for example `Dictionary`). It is [heterogenous][] when items may have different data types (for example `Dictionary`). -- Talk about typed dictionaries (TKey, TItem) -- Talk about heterogenous vs homogenous dictionaries (fix single data type vs multiple data types links in all dictionary blocks) -- Talk about keys, uniqueness and equality (link to Keys page) -- Talk about complex keys (link to Keys page) -- Items can be accessed as indexes -- Link to Data Type -- Creating a Dictionary +For more examples of creating dictionaries with literal and expression syntax, see [Create a Dictionary<TKey, TItem>][] on the [Dictionary<TKey, TItem>][] data type page. -#### Accessing Items +#### Keys + +Each key in a dictionary must be unique. Keys cannot be `null`. How equality is determined depends on whether you use C# syntax or dictionary blocks; see [Keys][] and [Object Equality][]. + +Keys whose data type is not a simple scalar (complex keys) are supported for some `TKey` types; see the Known Limitations section on [Dictionary<TKey, TItem>][] for supported key types and display limitations in Gateway. + +#### Accessing items ##### Keys +Use a [key][] in an [index expression][] or pass the key to a [Dictionary][] block property. Dictionary blocks that add, set, or remove items document key equality in their remarks; see [Object Equality][]. + ### Structures -TODO: +A [Structure][] is a {{% ctx %}} collection type (`Cortex.DataTypes.Dictionaries.Structure`) that represents key/item pairs where keys are always [String][] and items may be any data type. It implements [IDictionary<TKey, TItem>][] (effectively `IDictionary`) and behaves like a [heterogenous][] dictionary with string keys. -- Structures are IDictionary<string, object> underlying (how does this interact with blocks?) - - Talk about {} and dynamic vs object -- Talk about keys, uniqueness and equality (link to Keys page) -- Items can be accessed as indexes or properties -- Link to Data Type -- Creating a Structure +In the [Expression Editor][]: -#### Accessing Items +* A JSON object literal such as `{ "Name": "value", "Count": 1 }` creates a [Structure][], not a [Dictionary][] (dictionary literal syntax is not supported). +* Items can be accessed with [property expressions][] (for keys that follow C# identifier rules) or [index expressions][]. + +For creation examples, see [Create a Structure][] on the [Structure][] data type page. + +#### Accessing items ##### Properties +When the key is a valid C# identifier, use property syntax (for example `structure.Name` or `structure.Count`). See [Property expressions][] in the [Expression Editor][] documentation. + ##### Keys +Any string key can be used with index syntax (for example `structure["any-key"]`). Keys must be unique and cannot be `null`; see [Keys][]. + ### Lists -TODO: +A [List<TItem>][] is an ordered collection based on [System.Collections.Generic.List<TItem>][]. `TItem` is the data type of each [item][]. -- Talk about typed dictionaries (TItem) -- Talk about [] and dynamic vs object -- Talk about typed lists -- Talk about heterogenous vs homogenous lists (fix single data type vs multiple data types links in all list blocks) -- Items can be accessed as indexes -- Link to Data Type -- Creating a List +Lists can be created with `[]` literal syntax or `new List()` expressions. Using `[]` without other context creates a `List`. See [Create a List<TItem>][] on the [List<TItem>][] data type page. -#### Accessing Items +#### Homogenous and heterogenous lists + +A list is [homogenous][] when every item has the same data type (for example `List` or `List`). It is [heterogenous][] when items may have different data types (for example `List` or `List`). + +List blocks refer to these as lists containing items of a single data type versus multiple data types; see [Homogenous and heterogenous lists](#homogenous-and-heterogenous-lists) above. + +#### Accessing items ##### Indexes +Each item is accessed by a zero-based [index][]. Indexes are used in [index expressions][] in the Expression Editor and in [List][] block properties. See [Indexes][]. + ### Queues -TODO: +A [QueueWithPriority<TItem, TPriority>][] is a {{% ctx %}} queue type that orders items by [TPriority][], similar to [System.Collections.Generic.PriorityQueue<TElement, TPriority>][], while preserving first-in-first-out order among items that share the same priority. + +* Items with a lower priority value are dequeued before items with a higher priority value (as determined by the queue's priority comparer; see [QueueWithPriority<TItem, TPriority>][]). +* Among items with the same priority, the item that was enqueued first is dequeued first. + +`TItem` is the data type of each queued item. Queues are typically created with `new QueueWithPriority(...)`. See [QueueWithPriority<TItem, TPriority>][] for creation examples. + +#### Accessing items + +Items are not accessed by index or key. Use [Queue][] blocks to: -- Talk about first-in-first-out -- Talk about priority and order items in a queue -- Talk about typed queues -- Talk about how items can be accessed (e.g. dequeue/peek or get list of items) -- Link to Data Type -- Creating a queue +* [Enqueue][] items +* [Dequeue][] or [Peek][] the next item (without removing it when peeking) +* [Get count of all items][] or peek for inspection without changing queue order ### Data Storage Collection -TODO: +A Data Storage Collection is a named, persisted collection of key/data pairs stored by the [Data Storage Service][] for use across flow executions within a defined [collection scope][]. -- Talk about persistence -- Talk about about heterogenous property (takes any data type and can not be restricted) -- Talk about keys and uniqueness of keys (link to keys page) -- Items can only be accessed via key -- Mention high availability? -- Mention api access? +Unlike in-memory [Dictionary][] or [Structure][] variables: + +* Data survives after the execution that wrote it ends (subject to platform configuration and service availability). +* Each collection is identified by a [collection scope][] and [collection name][]. +* Each entry has a [String][] [key][] and a [dynamic][] `Data` value, so stored data is always [heterogenous][] and cannot be restricted to a single item type at the property level. +* Items are read and written only by [key][] using [Data Storage][] blocks (for example [Create Collection][], [Write Data With Key][], [Read Data With Key][]). + +Keys must be unique within a collection, cannot be `null`, and are case sensitive. Collection names are case insensitive within a scope. See [Keys][] and the [Write Data With Key][] block for key and naming behaviour. + +Operations may throw [ServiceDoesNotExistException][] or [ServiceUnavailableException][] when the Data Storage Service is not deployed or not healthy. For platform architecture context, see [Data Storage Service][]. ## Arrays vs Lists +[Array][] and [List<TItem>][] are both ordered collections that use zero-based [indexes][]. They are closely related in C# but behave differently in important ways. + ### Differences -### When To Use Arrays +| | [Array][] | [List<TItem>][] | +| --- | --- | --- | +| Size | Fixed when created | Grows and shrinks | +| Add or remove items | Not supported | Supported (blocks and C# methods) | +| Typical declaration | `new Int32[] { 1, 2, 3 }` | `new List() { 1, 2, 3 }` or `[1, 2, 3]` | +| Common in flow blocks | Rarely required as a property type | Widely used | + +See [Array][] and [List<TItem>][] for creation examples and interface support. + +### When to use arrays + +Use an array when the number of items is fixed and will not change during processing—for example, a predefined set of values returned from an API or a fixed batch size. -### When To Use Lists +### When to use lists -- TODO: Some info on when to use array vs list - https://www.educba.com/c-sharp-list-vs-array/ +Use a list when items are added, removed, or reordered during a flow, or when the count is not known in advance. Most [List][] blocks and literal `[]` syntax expect [List<TItem>][], not arrays. ## Remarks ### Known Limitations -#### Complex Keys do not show sho correctly in the Variable Details Viewer +#### Complex keys do not show correctly in the Variable Details Viewer -Currently, if a Dictionary is shown in the Variable Details Viewer and contains Complex Data types as its keys, the data within the variable will not be displayed correctly. +Currently, if a [Dictionary][] is shown in the Variable Details Viewer and contains complex data types as its keys, the data within the variable will not be displayed correctly. In the future this limitation may be removed. @@ -110,16 +161,102 @@ In the future this limitation may be removed. ### Related Concepts -TODO +* [Collections][] +* [Keys][] +* [Indexes][] +* [Items][] +* [Occurrences][] +* [Generics][] +* [Object Equality][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [List<TItem>][] +* [Array][] +* [QueueWithPriority<TItem, TPriority>][] +* [IDictionary<TKey, TItem>][] +* [IList<TItem>][] +* [IEnumerable][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][]) +* [List][] blocks (for example [Add Item At End][]) +* [Queue][] blocks (for example [Enqueue Item][], [Dequeue Item][], [Peek Item][]) +* [Data Storage][] blocks (for example [Create Collection][], [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [System.Collections.Generic.IDictionary<TKey, TItem>][MS IDictionary] +* [System.Collections.Generic.PriorityQueue<TElement, TPriority>][MS PriorityQueue] + +[items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[homogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Homogenous" >}} +[heterogenous]: {{< url path="Cortex.Reference.Glossary.F-J.Heterogenous" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Queue]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[IEnumerable]: {{< url path="Cortex.Reference.DataTypes.Collections.IEnumerable.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[TPriority]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} +[Create a Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.CreateNew" >}} +[Create a List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.CreateNew" >}} +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} +[Property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} + +[Data Storage Collection]: {{< ref "#data-storage-collection" >}} +[Data Storage Service]: {{< url path="Cortex.Guides.CortexInnovation.CoreApplication.Services.DataStorageService.MainDoc" >}} +[collection scope]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[collection name]: {{< url path="Cortex.Reference.Blocks.DataStorage.CreateCollection.CreateCollectionBlock.MainDoc" >}} + +[Create Collection]: {{< url path="Cortex.Reference.Blocks.DataStorage.CreateCollection.CreateCollectionBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock.MainDoc" >}} +[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEndBlock.MainDoc" >}} +[Enqueue Item]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue Item]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek Item]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Enqueue]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} +[Dequeue]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} +[Peek]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} +[Get count of all items]: {{< url path="Cortex.Reference.Blocks.Queues.GetCount.GetCountOfAllItemsBlock.MainDoc" >}} + +[ServiceDoesNotExistException]: {{< url path="Cortex.Reference.Exceptions.Services.ServiceDoesNotExistException.MainDoc" >}} +[ServiceUnavailableException]: {{< url path="Cortex.Reference.Exceptions.Services.ServiceUnavailableException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS IDictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IDictionary" >}} +[MS PriorityQueue]: {{< url path="MSDocs.DotNet.Api.System.PriorityQueue.MainDoc" >}} +[System.Collections.Generic.Dictionary<TKey, TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[System.Collections.Generic.PriorityQueue<TElement, TPriority>]: {{< url path="MSDocs.DotNet.Api.System.PriorityQueue.MainDoc" >}} diff --git a/data/urls.toml b/data/urls.toml index 78c01b9d3..d8f6400d2 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -1006,7 +1006,7 @@ LdapAuth = "/docs/guides/user-guides/user-interfaces/gateway/admin/authentication/#ldap" OidcAuth = "/docs/guides/user-guides/user-interfaces/gateway/admin/authentication/#openid-connect" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Authorisation] - MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/authorisation/" + MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/authorisation/" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Packages] MainDoc = "/docs/guides/user-guides/user-interfaces/gateway/admin/packages/" [Cortex.Guides.UserGuides.UserInterfaces.Gateway.Admin.Packages.Overview] @@ -1160,6 +1160,8 @@ [Cortex.Reference.Blocks.Dictionaries.AddItem] [Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey] MainDoc = "/docs/reference/blocks/dictionaries/add-item/add-item-with-key-block-3" + [Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock] + MainDoc = "/docs/reference/blocks/dictionaries/add-item/add-item-with-key-block-3" [Cortex.Reference.Blocks.Dictionaries.ContainsItem] [Cortex.Reference.Blocks.Dictionaries.ContainsItem.ContainsItemWithKeyAndValue] MainDoc = "/docs/reference/blocks/dictionaries/contains-item/contains-item-with-key-and-value-block-3" @@ -1372,6 +1374,8 @@ MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-beginning-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd] MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-end-block-2" + [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEndBlock] + MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-end-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtIndex] MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-index-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemsAtBeginning] @@ -1489,6 +1493,9 @@ MainDoc = "/docs/reference/blocks/queues/dequeue-item/dequeue-item-block-2" [Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemsBlock] MainDoc = "/docs/reference/blocks/queues/dequeue-item/dequeue-items-block-2" + [Cortex.Reference.Blocks.Queues.GetCount] + [Cortex.Reference.Blocks.Queues.GetCount.GetCountOfAllItemsBlock] + MainDoc = "/docs/reference/blocks/queues/get-count/get-count-of-all-items-block-2" [Cortex.Reference.Blocks.Queues.EnqueueItem] [Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock] MainDoc = "/docs/reference/blocks/queues/enqueue-item/enqueue-item-block-2" @@ -1757,6 +1764,7 @@ [Cortex.Reference.Concepts.WorkingWith.Collections] MainDoc = "/docs/reference/concepts/working-with/collections/" [Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection] + MainDoc = "/docs/reference/concepts/working-with/collections/what-is-a-collection" DataStorage = "/docs/reference/concepts/working-with/collections/what-is-a-collection/#data-storage-collection" ArraysVersusLists = "/docs/reference/concepts/working-with/collections/what-is-a-collection/#arrays-vs-lists" [Cortex.Reference.Concepts.WorkingWith.Collections.Items] @@ -1820,6 +1828,8 @@ MainDoc = "/docs/reference/concepts/working-with/objects/object-equality/" [Cortex.Reference.Concepts.WorkingWith.Scopes] MainDoc = "/docs/reference/concepts/working-with/scopes/" + [Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope] + MainDoc = "/docs/reference/concepts/working-with/scopes/" [Cortex.Reference.Concepts.WorkingWith.Tasks] MainDoc = "/docs/reference/concepts/working-with/tasks/" [Cortex.Reference.Concepts.WorkingWith.Text] From 0b6d2641288148cc7468a0adf6278f6b0d5a1d88 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 09:28:37 +0100 Subject: [PATCH 03/11] added additional documentation --- .../working-with/collections/indexes.md | 151 ++++++++++++++++-- data/urls.toml | 3 + 2 files changed, 137 insertions(+), 17 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md index f8f540589..79b7436b1 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md @@ -1,50 +1,167 @@ --- title: "Indexes" linkTitle: "Indexes" -description: "Information related to working with Indexes." +description: "Information related to working with zero-based indexes in ordered collections and strings." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +An **index** is a zero-based position that identifies which [item][] to read or update in an ordered collection, or which character to access in a [String][]. + +Indexes answer the question “which position?”—not “which matching value?”. When several items share the same value, use an [occurrence][] (and blocks such as [Get Index Of Item With Value][]) to select the nth match, then use the returned index if you need a position. See [Indexes vs occurrences](#indexes-vs-occurrences). + +| Data type | Uses indexes? | Notes | +| --- | --- | --- | +| [List<TItem>][] | Yes | Primary ordered collection in flows | +| [Array][] | Yes | Fixed size; same index rules as lists | +| [String][] | Yes | Index refers to a character position | +| [Dictionary<TKey, TItem>][] | No | Items are accessed by [key][], not index | +| [Structure][] | No | Items are accessed by [key][] or property name | +| [QueueWithPriority<TItem, TPriority>][] | No | Items are not randomly accessed by index | +| [Data Storage Collection][] | No | Items are accessed by [key][] only | + +For how collection types differ, see [What is a Collection?][]. + +## Zero-based numbering + +{{% ctx %}} indexes follow standard C# conventions ([zero based][]): + +* The first item (or character) is at index `0`. +* The second is at index `1`, and so on. +* For a list with `n` items, valid indexes are `0` through `n - 1` inclusive. + +For example, in `["A", "B", "C"]` the item `"B"` is at index `1`, and the last item is at index `2` (not `3`). + +Block properties named `Index` are typically [Int32][] values within this range. An index outside the range may cause [PropertyValueOutOfRangeException][] (for example on [Get Item At Index][] when the list is empty or the index is too large). + +## Accessing items by index + +### In the Expression Editor + +Use [index expressions][] to read or assign items in [List][] and [Array][] variables, and to work with [String][] characters. Examples: + +| Expression | Result (example list `[1, 2, 3, 4, 5]`) | +| --- | --- | +| `($)List[0]` | `1` (first item) | +| `($)List[2]` | `3` (third item) | +| `($)List[^1]` | `5` (last item) | +| `($)List[0..2]` | `[1, 2]` (first two items) | +| `($)List[2..]` | `[3, 4, 5]` (from third item to end) | + +[Dictionary][] and [Structure][] items use the same bracket syntax, but the value inside the brackets is a [key][], not a numeric index—for example `($)Dictionary["FirstKey"]`. + +Range and index-from-end syntax (`^1`, `..`) follow C# rules. For more examples and behaviour, see [Index expressions][] in the [Expression Editor][] documentation and [Indices and Ranges][] in the C# language reference. + +### With blocks + +Many [List][] and text blocks expose an `Index` property ([Int32][]) to select a position directly: + +* **Lists** — [Get Item At Index][], [Set Item At Index][], [Add Item At Index][], [Remove Item At Index][], and related blocks that operate on one or more indexes +* **Text** — [Get Text At Index][], [Add Text Before Index][], [Remove Text At Index][], and related blocks (indexes refer to character positions in the string) + +Some blocks return an index as output—for example [Get Index Of Item With Value][] writes the zero-based position of the specified [occurrence][] of a matching item, or `-1` if no match is found. + +## Indexes vs occurrences + +Indexes and [occurrences][] solve different problems: -- Overview/summary -- What is an Index? -- How are they accessed? -- Indexes are zero based -- Difference between occurrence and indexes +| | **Index** | **Occurrence** | +| --- | --- | --- | +| Meaning | Position in the collection (0-based) | Which matching item when searching by value (1-based from start, or negative from end) | +| Used when | You already know the position | Duplicate values exist and you need the 1st, 2nd, or last match | +| Typical use | `list[2]`, [Get Item At Index][] | [Get Index Of Item With Value][] with `Occurrence` = `1` or `-1` | +| Example list `[1, 2, 1]` | Index `0` → `1`, index `2` → `1` | 1st occurrence of value `1` → index `0`; 2nd occurrence → index `2` | -## Accessing an item using Indexes +After [Get Index Of Item With Value][] returns an index, you can pass that value to blocks or expressions that expect a position. -[Indexes][] can be used in the [Expression Editor][] to access items in a Collection. +For occurrence property values and behaviour when a match is missing, see [Occurrences][]. ## Remarks ### Known Limitations -TODO +None ## See Also ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][] +* [Keys][] +* [Occurrences][] +* [Working with Text][] ### Related Data Types -TODO +* [List<TItem>][] +* [Array][] +* [IList<TItem>][] +* [String][] +* [Int32][] ### Related Blocks -TODO +* [List][] blocks (for example [Get Item At Index][], [Set Item At Index][], [Add Item At Index][], [Remove Item At Index][]) +* [Get Index Of Item With Value][], [Get Indexes Of Items With Value][] +* Text blocks (for example [Get Text At Index][], [Get Text Between Indexes][], [Get Index Of Text][]) ### External Documentation -TODO +* [Indices and ranges (C#)][MS Indices and Ranges] +* [System.Collections.Generic.List<TItem>][MS List] +* [System.Array][MS Array] +* [System.String][MS String] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[item]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Working with Text]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.MainDoc" >}} +[zero based]: {{< url path="Cortex.Reference.Glossary.U-Z.ZeroBased" >}} + +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IList<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} -[Indexes]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} + +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Set Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemAtIndex.MainDoc" >}} +[Add Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtIndex.MainDoc" >}} +[Remove Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemAtIndex.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} +[Get Indexes Of Items With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexesOfItemsWithValue.MainDoc" >}} + +[Get Text At Index]: {{< url path="Cortex.Reference.Blocks.Text.GetText.GetTextAtIndex.MainDoc" >}} +[Get Text Between Indexes]: {{< url path="Cortex.Reference.Blocks.Text.GetText.GetTextBetweenIndexes.MainDoc" >}} +[Add Text Before Index]: {{< url path="Cortex.Reference.Blocks.Text.AddText.AddTextBeforeIndex.MainDoc" >}} +[Remove Text At Index]: {{< url path="Cortex.Reference.Blocks.Text.RemoveText.RemoveTextAtIndex.MainDoc" >}} +[Get Index Of Text]: {{< url path="Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText.MainDoc" >}} + +[PropertyValueOutOfRangeException]: {{< url path="Cortex.Reference.Exceptions.Common.Property.PropertyValueOutOfRangeException.MainDoc" >}} + +[MS Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} +[MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[MS String]: {{< url path="MSDocs.DotNet.Api.System.String.MainDoc" >}} +[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} +[System.Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} +[System.String]: {{< url path="MSDocs.DotNet.Api.System.String.MainDoc" >}} diff --git a/data/urls.toml b/data/urls.toml index d8f6400d2..c1b93222b 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -1576,6 +1576,9 @@ [Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues] MainDoc = "/docs/reference/blocks/text/format-text/format-text-with-values-block-1/" FormatTemplate = "/docs/reference/blocks/text/format-text/format-text-with-values-block-1/#format-template" + [Cortex.Reference.Blocks.Text.GetIndex] + [Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText] + MainDoc = "/docs/reference/blocks/text/get-index/get-index-of-text-block/" [Cortex.Reference.Blocks.Text.GetText] [Cortex.Reference.Blocks.Text.GetText.GetTextAtBeginning] MainDoc = "/docs/reference/blocks/text/get-text/get-text-at-beginning-block/" From 91aee25a1016d07392cfc158aaddd5d37e537805 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 09:59:09 +0100 Subject: [PATCH 04/11] fixed up some broken links and removed unused URL configurations --- .../working-with/collections/indexes.md | 3 - .../working-with/collections/items.md | 3 - .../Concepts/working-with/collections/keys.md | 193 ++++++++++++++++-- .../collections/what-is-a-collection.md | 8 +- 4 files changed, 175 insertions(+), 32 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md index 79b7436b1..6fa34e752 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/indexes.md @@ -162,6 +162,3 @@ None [MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} [MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} [MS String]: {{< url path="MSDocs.DotNet.Api.System.String.MainDoc" >}} -[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} -[System.Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} -[System.String]: {{< url path="MSDocs.DotNet.Api.System.String.MainDoc" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md index ad712b3f2..e2c530a46 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/items.md @@ -160,7 +160,6 @@ None [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} [Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} [property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} -[Property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} [Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} [Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} @@ -186,5 +185,3 @@ None [MS List]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} [MS Array]: {{< url path="MSDocs.DotNet.Api.System.Array" >}} [MS Equality]: {{< url path="MSDocs.CSharp.EqualityOperators" >}} -[System.Collections.Generic.Dictionary<TKey, TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} -[System.Collections.Generic.List<TItem>]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.List" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md index 7361f5a06..c6bd6cd20 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md @@ -1,37 +1,123 @@ --- title: "Keys" linkTitle: "Keys" -description: "Information related to working with Keys." +description: "Information related to working with keys in key/item collections such as dictionaries and structures." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +A **key** is the value used to look up a specific [item][] in a key/item collection. Keys answer the question “which entry?”—not “which position?” (that is an [index][]) and not “which matching value?” (that is an [occurrence][] when searching by item value). + +| Collection type | Key data type | Notes | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | `TKey` ([generic type parameter][]) | Each dictionary declares its own key type | +| [Structure][] | [String][] only | Behaves like a heterogenous string-keyed dictionary | +| [Data Storage Collection][] | [String][] only | Persisted; keys are case sensitive | +| [List<TItem>][] / [Array][] | N/A | Items are accessed by [index][], not key | +| [QueueWithPriority<TItem, TPriority>][] | N/A | Items are not randomly accessed by key | + +For how collection types differ, see [What is a Collection?][]. + +Keys are not the same as [items][]: the key identifies the entry; the item is the value stored at that entry. In expressions, both [Dictionary][] and [Structure][] use bracket syntax to supply a key—for example `dictionary["Key1"]`—even though the syntax is called an [index expression][] in the [Expression Editor][] documentation. See [Keys vs indexes](#keys-vs-indexes). + +## Key data types + +### Dictionary<TKey, TItem> + +In [Dictionary<TKey, TItem>][], `TKey` is the [generic type parameter][] that defines the data type of every key. `TItem` is the type of each stored item. For example: + +* `Dictionary` — keys are [String][], items are [Int32][] +* `Dictionary` — keys are [Int32][], items are [String][] + +When you create a dictionary in the [Expression Editor][], the key types are inferred from the keys you provide—for example `new Dictionary() { { "A", 1 } }`. See [Create a Dictionary<TKey, TItem>][]. + +{{% ctx %}} supports a defined set of types for `TKey` in practice (including [String][], [Int32][], [Double][], [Boolean][], and [DateTimeOffset][]). Some reference types (for example [IList][]<[Int32][]>) can also be used as keys; behaviour then depends on how that type implements equality. See [Uniqueness](#uniqueness-and-null) and the Known Limitations on [Dictionary<TKey, TItem>][]. + +### Structure + +A [Structure][] always uses [String][] keys. Items may be any data type. JSON object literals in the [Expression Editor][] create a [Structure][], not a [Dictionary][]. + +### Data Storage Collection + +Entries in a [Data Storage Collection][] are identified by a [String][] key and a [dynamic][] `Data` value. Keys are case sensitive (`"user"` and `"USER"` are different keys). Collection names are case insensitive within a scope. See [Write Data With Key][]. + +## Uniqueness and null + +### Keys cannot be null + +A key cannot be `null` when adding, reading, updating, or removing entries in [Dictionary][], [Structure][], or [Data Storage Collection][] variables. Blocks such as [Add Item With Key][] throw [PropertyNullException][] if the key property is `null`. + +### Keys must be unique + +Within a single collection instance, each key must be unique so that lookups are unambiguous. + +For typical scalar keys ([String][], [Int32][], [Boolean][], and similar), two keys with the same value are considered the same key. Adding a second entry with an equal key updates or conflicts with the existing entry (for example [KeyPresentException][] on [Add Item With Key][]). -- Overview/summary -- What is a Key? -- How are they accessed? -- The type of the Key depends on the data type -- Keys are unique -- Keys cannot be null -- If you are using C# syntax, then the equality is done using reference equality for reference types, and value equality for value types -- If you are using Dictionary blocks, then the equality is done using reference equality for reference types and falls back to value equality if no reference was found, and value equality for value types +For some reference-type keys, equality follows C# reference semantics: two keys are the same only if they refer to the same object, not merely if their contents match. That can allow multiple entries whose keys look the same when printed but are different object references—for example two separate `List` instances both containing `[1]`. Dictionary blocks that support [occurrence][] when removing by key document this behaviour; see [Remove Item With Key][]. -## Accessing an item using Keys +## Comparing keys -[Keys][] can be used in the [Expression Editor][] to access items in a Collection. +Blocks and expressions need a consistent rule for whether a key you supply **matches** a key already in the collection—for example when checking [Contains Item With Key][] or adding with [Add Item With Key][]. + +| Context | How key equality is determined | +| --- | --- | +| C# syntax in the [Expression Editor][] | [Reference equality][] for reference types and [value equality][] for value types, following standard C# rules (including `==` and `Equals` where applicable). | +| [Dictionary][] and [Data Storage][] blocks | For reference types, [reference equality][] is tried first; if no matching reference is found, comparison falls back to [value equality][]. For value types, [value equality][] is used. | + +This difference matters when keys are reference types. A block may treat two keys as equal when C# `==` would not, or when duplicate reference-type keys exist in the same dictionary. For examples and affected blocks, see [Object Equality][]. For general C# rules, see [Equality comparisons][MS Equality]. + +## Accessing items using keys + +### In the Expression Editor + +Use an [index expression][] to read or assign an item by key: + +| Collection type | Example | More information | +| --- | --- | --- | +| [Dictionary<TKey, TItem>][] | `($)Dictionary["Key1"]` | [Index expressions][] | +| [Structure][] | `($)Structure.Name` or `($)Structure["any-key"]` | [Property expressions][] (identifier keys), [Index expressions][] | + +[Structure][] keys that are valid C# identifiers can use property syntax (`structure.Count`). Any string key can use bracket syntax (`structure["any-key"]`). + +### With blocks + +Pass the key to the block’s `Key` property (type `TKey` or [String][] as appropriate): + +* **Dictionary** — [Add Item With Key][], [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Contains Item With Key][], and related blocks +* **Data Storage** — [Read Data With Key][], [Write Data With Key][] (overwrites existing data when the key already exists) + +For a full list of collection access patterns, see [Items][] and [What is a Collection?][]. + +## Keys vs indexes + +[Indexes][] are zero-based positions in **ordered** collections ([List][], [Array][], [String][] characters). **Keys** identify entries in **key/item** collections. + +Both use square brackets in the [Expression Editor][], but the expression inside the brackets means something different: + +| Syntax | Meaning | +| --- | --- | +| `list[0]` | First item (index `0`) | +| `dictionary["Key1"]` | Item whose key is `"Key1"` | + +Using a numeric index on a [Dictionary][] or [Structure][] is not valid. Using a string key on a [List][] is not valid. See [Indexes][]. ## Remarks ### Known Limitations -#### Complex Keys do not show sho correctly in the Variable Details Viewer +#### Supported key types + +Only certain data types can be used for `TKey` in [Dictionary<TKey, TItem>][] and [IDictionary<TKey, TItem>][] properties. These include, but are not limited to, [String][], [Int32][], [Double][], [Boolean][], and [DateTimeOffset][]. See the Known Limitations section on [Dictionary<TKey, TItem>][]. + +#### Non-String keys in Gateway + +If `TKey` is not [String][], Gateway displays the key using its `ToString()` representation (for example an [Int32][] key `1` may appear as `"1"`). + +#### Complex keys do not show correctly in the Variable Details Viewer -Currently, if a Dictionary is shown in the Variable Details Viewer and contains Complex Data types as its keys, the data within the variable will not be displayed correctly. +Currently, if a [Dictionary][] is shown in the Variable Details Viewer and contains complex data types as its keys, the data within the variable will not be displayed correctly. In the future this limitation may be removed. @@ -39,19 +125,84 @@ In the future this limitation may be removed. ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][] +* [Indexes][] +* [Occurrences][] +* [Generics][] +* [Object Equality][] ### Related Data Types -TODO +* [Dictionary<TKey, TItem>][] +* [Structure][] +* [IDictionary<TKey, TItem>][] +* [String][] +* [dynamic][] ### Related Blocks -TODO +* [Dictionary][] blocks (for example [Add Item With Key][], [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Contains Item With Key][]) +* [Data Storage][] blocks (for example [Read Data With Key][], [Write Data With Key][]) ### External Documentation -TODO +* [System.Collections.Generic.Dictionary<TKey, TItem>][MS Dictionary] +* [System.Collections.Generic.IDictionary<TKey, TItem>][MS IDictionary] +* [Equality comparisons (C#)][MS Equality] + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[Items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[item]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[occurrence]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} +[Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[generic type parameter]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[reference equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[value equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} + +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[IDictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.IDictionary.MainDoc" >}} +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[QueueWithPriority<TItem, TPriority>]: {{< url path="Cortex.Reference.DataTypes.Collections.QueueWithPriority.MainDoc" >}} +[IList]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} +[Double]: {{< url path="Cortex.Reference.DataTypes.Numbers.Double.MainDoc" >}} +[Boolean]: {{< url path="Cortex.Reference.DataTypes.ConditionalLogic.Boolean.MainDoc" >}} +[DateTimeOffset]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeOffset.MainDoc" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} + +[Create a Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.CreateNew" >}} [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} -[Keys]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[index expression]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[Index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} +[property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} + +[Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} +[Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} + +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock.MainDoc" >}} +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Remove Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithKey.MainDoc" >}} +[Contains Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.ContainsItem.ContainsItemWithKey.MainDoc" >}} +[Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} +[Write Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.WriteData.WriteDataWithKeyBlock.MainDoc" >}} + +[PropertyNullException]: {{< url path="Cortex.Reference.Exceptions.Common.Property.PropertyNullException.MainDoc" >}} +[KeyPresentException]: {{< url path="Cortex.Reference.Exceptions.Dictionaries.KeyPresentException.MainDoc" >}} + +[MS Dictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.Dictionary" >}} +[MS IDictionary]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IDictionary" >}} +[MS Equality]: {{< url path="MSDocs.CSharp.EqualityOperators" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md index 473bfda9c..5d68b6501 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md @@ -31,7 +31,7 @@ For more detail on [keys][], [indexes][], and [items][], see the related concept A [Dictionary<TKey, TItem>][] is a collection of key/item pairs based on [System.Collections.Generic.Dictionary<TKey, TItem>][]. * `TKey` is the data type of each [key][] used to access an item. -* `TItem` is the data type of each [item][] stored in the collection. +* `TItem` is the data type of each [item][items] stored in the collection. Each item is accessed by its key. In the [Expression Editor][], dictionary items are accessed using [index expressions][] (for example `dictionary["StringKey1"]`). @@ -51,7 +51,7 @@ Keys whose data type is not a simple scalar (complex keys) are supported for som ##### Keys -Use a [key][] in an [index expression][] or pass the key to a [Dictionary][] block property. Dictionary blocks that add, set, or remove items document key equality in their remarks; see [Object Equality][]. +Use a [key][] in an [index expression][index expressions] or pass the key to a [Dictionary][] block property. Dictionary blocks that add, set, or remove items document key equality in their remarks; see [Object Equality][]. ### Structures @@ -76,7 +76,7 @@ Any string key can be used with index syntax (for example `structure["any-key"]` ### Lists -A [List<TItem>][] is an ordered collection based on [System.Collections.Generic.List<TItem>][]. `TItem` is the data type of each [item][]. +A [List<TItem>][] is an ordered collection based on [System.Collections.Generic.List<TItem>][]. `TItem` is the data type of each [item][items]. Lists can be created with `[]` literal syntax or `new List()` expressions. Using `[]` without other context creates a `List`. See [Create a List<TItem>][] on the [List<TItem>][] data type page. @@ -200,7 +200,6 @@ In the future this limitation may be removed. [Key]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} [index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} [Indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} -[Index]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} [Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} [Occurrences]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Occurrences.MainDoc" >}} [Generics]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.Generics.MainDoc" >}} @@ -227,7 +226,6 @@ In the future this limitation may be removed. [Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} [index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} [property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} -[Property expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.PropertyExpressions" >}} [Data Storage Collection]: {{< ref "#data-storage-collection" >}} [Data Storage Service]: {{< url path="Cortex.Guides.CortexInnovation.CoreApplication.Services.DataStorageService.MainDoc" >}} From 05370b0a2d09e5685430d7f20ce6e589751927f3 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 10:05:12 +0100 Subject: [PATCH 05/11] fixed up some duplicated URL definitions --- .../Reference/Concepts/working-with/collections/keys.md | 2 +- .../Concepts/working-with/collections/what-is-a-collection.md | 4 ++-- data/urls.toml | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md index c6bd6cd20..245af4e15 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/keys.md @@ -192,7 +192,7 @@ In the future this limitation may be removed. [Data Storage Collection]: {{< ref "what-is-a-collection.md#data-storage-collection" >}} [Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} -[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock.MainDoc" >}} +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} [Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} [Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} [Remove Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithKey.MainDoc" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md index 5d68b6501..0cfdd7c16 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/what-is-a-collection.md @@ -237,8 +237,8 @@ In the future this limitation may be removed. [Read Data With Key]: {{< url path="Cortex.Reference.Blocks.DataStorage.ReadData.ReadDataWithKeyBlock.MainDoc" >}} [Data Storage]: {{< url path="Cortex.Reference.Blocks.DataStorage.MainDoc" >}} -[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock.MainDoc" >}} -[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEndBlock.MainDoc" >}} +[Add Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey.MainDoc" >}} +[Add Item At End]: {{< url path="Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd.MainDoc" >}} [Enqueue Item]: {{< url path="Cortex.Reference.Blocks.Queues.EnqueueItem.EnqueueItemBlock.MainDoc" >}} [Dequeue Item]: {{< url path="Cortex.Reference.Blocks.Queues.DequeueItem.DequeueItemBlock.MainDoc" >}} [Peek Item]: {{< url path="Cortex.Reference.Blocks.Queues.PeekItem.PeekItemBlock.MainDoc" >}} diff --git a/data/urls.toml b/data/urls.toml index c1b93222b..2372058ef 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -1160,8 +1160,6 @@ [Cortex.Reference.Blocks.Dictionaries.AddItem] [Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKey] MainDoc = "/docs/reference/blocks/dictionaries/add-item/add-item-with-key-block-3" - [Cortex.Reference.Blocks.Dictionaries.AddItem.AddItemWithKeyBlock] - MainDoc = "/docs/reference/blocks/dictionaries/add-item/add-item-with-key-block-3" [Cortex.Reference.Blocks.Dictionaries.ContainsItem] [Cortex.Reference.Blocks.Dictionaries.ContainsItem.ContainsItemWithKeyAndValue] MainDoc = "/docs/reference/blocks/dictionaries/contains-item/contains-item-with-key-and-value-block-3" @@ -1374,8 +1372,6 @@ MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-beginning-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEnd] MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-end-block-2" - [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtEndBlock] - MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-end-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemAtIndex] MainDoc = "/docs/reference/blocks/lists/add-item/add-item-at-index-block-2" [Cortex.Reference.Blocks.Lists.AddItem.AddItemsAtBeginning] From fc977775b55a9ba33a17750545c97c96f0f108d9 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 11:50:18 +0100 Subject: [PATCH 06/11] added occurrences documentation --- .../working-with/collections/occurrences.md | 200 +++++++++++++++--- data/urls.toml | 14 ++ 2 files changed, 189 insertions(+), 25 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md index ceec95a19..d523127c4 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/collections/occurrences.md @@ -1,47 +1,135 @@ --- title: "Occurrences" linkTitle: "Occurrences" -description: "Information related to working with Occurrences." +description: "Information related to selecting the nth matching item or text segment when duplicates exist, using one-based or negative occurrence values." --- # {{% param title %}} ## Summary -TODO +An **occurrence** selects which **match** you mean when the same value, key, or text appears more than once. Occurrences answer the question “which duplicate?”—not “which position?” (that is an [index][indexes]). -- Summary -- What is an Occurrence? -- How are they accessed? - - Positive or Negative int properties on blocks that operate on single items -- Difference between occurrence and indexes -- specified occurrence +In {{% ctx %}}, occurrence values are [Int32][] inputs on block properties named `Occurrence`. They are **one-based when counting from the start** (`1` = first match, `2` = second match) and **negative when counting from the end** (`-1` = last match, `-2` = second from last). The value `0` is not valid for blocks that require a specified occurrence; see [Occurrence not present](#occurrence-not-present). -## Positive Occurrences +| Context | What is counted | Order | +| --- | --- | --- | +| [List<TItem>][] matching by [item][items] value | Items equal to the supplied value ([Object Equality][]) | Collection order (left to right) | +| [Dictionary<TKey, TItem>][] matching by value | Items equal to the supplied value | See [Dictionaries and duplicate entries](#dictionaries-and-duplicate-entries) | +| [Dictionary<TKey, TItem>][] matching by [key][keys] | Entries with the same key (for example duplicate reference-type keys) | See [Dictionaries and duplicate entries](#dictionaries-and-duplicate-entries) | +| [String][] text search | Matches of [Text To Find][] in the string | Left to right in the text | -TODO: +After a block returns an [index][indexes] (for example [Get Index Of Item With Value][] or [Get Index Of Text][]), use that position with index-based blocks or [index expressions][]. See [Occurrences vs indexes](#occurrences-vs-indexes). -- Get First, Second, Third, Nth +## Positive occurrences -## Negative Occurrences +A **positive** occurrence counts from the **first** match: -TODO: +| Occurrence | Meaning | +| --- | --- | +| `1` | First match | +| `2` | Second match | +| `3` | Third match | +| `n` | nth match | -- Get Last, Second from Last, Third from Last, Nth from Last +### Example (list by value) + +For the list `[1, 2, 3, 3, 2, 1]` and value `1`: + +| Occurrence | Item at that occurrence | Zero-based [index][indexes] returned by [Get Index Of Item With Value][] | +| --- | --- | --- | +| `1` | First `1` | `0` | +| `2` | Second `1` | `5` | + +Block examples use the same list: [Get Index Of Item With Value][], [Set Item With Value][], and [Remove Item With Value][]. + +## Negative occurrences + +A **negative** occurrence counts from the **last** match: + +| Occurrence | Meaning | +| --- | --- | +| `-1` | Last match | +| `-2` | Second from last | +| `-3` | Third from last | +| `-n` | nth from last | + +### Example (list by value) + +For `[1, 2, 3, 3, 2, 1]` and value `1`: + +| Occurrence | Item at that occurrence | Zero-based index | +| --- | --- | --- | +| `-1` | Last `1` | `5` | +| `-2` | Second-to-last `1` | `0` | + +If there are `m` matches, valid negative occurrences are `-1` through `-m` inclusive (for example three matches allow `-1`, `-2`, and `-3`). + +## Accessing items using occurrences + +Occurrences are used in the **block editor**, not as a separate expression syntax. Pass an [Int32][] value to the block’s `Occurrence` property (often via the [Literal][] editor; default is commonly `1`). + +Typical patterns: + +* **Find position** — [Get Index Of Item With Value][], [Get Index Of Text][], then use the returned index elsewhere +* **Read** — [Get Item With Key][] when duplicate keys exist +* **Update** — [Set Item With Value][], [Set Item With Key][], [Set Dictionary Item With Value][], [Find And Replace Text][] +* **Remove** — [Remove Item With Value][], [Remove Item With Key][], [Remove Dictionary Item With Value][], [Find And Remove Text][] + +Blocks that return **all** matches (for example [Get Indexes Of Items With Value][], [Find All Text][]) do not take a single `Occurrence` property; use those when you need every index. + +For how items or text are considered a match, see [Object Equality][] (lists and dictionaries) and [Equality][] (text). + +## Occurrences vs indexes + +[Indexes][] and occurrences solve different problems. This table matches the discussion on [Indexes][indexes]: + +| | **Index** | **Occurrence** | +| --- | --- | --- | +| Meaning | Position in the collection or string (0-based) | Which matching item or text segment (1-based from start, or negative from end) | +| Used when | You already know the position | Duplicates exist and you need the 1st, 2nd, or last match | +| Typical use | `($)List[2]`, [Get Item At Index][] | [Get Index Of Item With Value][] with `Occurrence` = `1` or `-1` | +| Example list `[1, 2, 1]` | Index `0` → `1`, index `2` → `1` | 1st occurrence of value `1` → index `0`; 2nd occurrence → index `2` | + +In C#, [index expressions][] can use index-from-end syntax (`^1` for the last **element**). That still refers to **position**, not to the nth **value** duplicate. Use occurrences when matching by value, key, or search text. ## Remarks -### Occurrence Not Present +### Occurrence not present + +When the requested occurrence does not exist (or there are no matches), block behaviour falls into one of three categories. Always check the individual block’s **Remarks** and **Exceptions** sections. + +| Behaviour | When it applies | Examples | +| --- | --- | --- | +| **Predetermined output** | An output property is set to a sentinel value | [Get Index Of Item With Value][] and [Get Index Of Text][] set `Index` to `-1`; [Find Text][] sets `Match` to `null` | +| **No operation** | The collection or text is left unchanged | [Remove Item With Value][], [Set Item With Value][], [Remove Item With Key][] when there is nothing to remove or set | +| **Exception** | The flow stops with an error | [Get Item With Key][], [Set Item With Key][] throw [OccurrenceNotPresentException][] | + +[OccurrenceNotPresentException][] is also thrown when `Occurrence` is `0` on blocks that use that exception (see [Occurrence is zero](#occurrence-is-zero)). + +### Occurrence is zero + +`0` cannot mean “first” or “last” match. Blocks handle it as follows: + +| Behaviour | Blocks | +| --- | --- | +| [OccurrenceNotPresentException][] | [Get Item With Key][], [Set Item With Key][] | +| Output set to “not found” (`-1` or `null`) | [Get Index Of Item With Value][], [Get Index Of Text][], [Find Text][] | +| No operation | [Remove Item With Value][], [Set Item With Value][], and similar remove/set-by-value or remove-by-key blocks | + +### Dictionaries and duplicate entries + +[Dictionary<TKey, TItem>][] blocks that support `Occurrence` may operate on: -TODO: +* **Duplicate values** — several entries whose items match the same value ([Set Dictionary Item With Value][], [Remove Dictionary Item With Value][]) +* **Duplicate keys** — several entries that share the same key instance (for example multiple `List` keys that compare equal under [Object Equality][]); [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][] -Blocks will either throw an exception if the occurrence is not present (e.g. OccurrenceNotPresentException), or they will handle the occurrence not being present either by performing no operation or by returning a predetermined value. +Unlike [List][] and [Array][], dictionaries do not have a guaranteed user-visible order. For duplicate keys or values, **which entry is the 1st or 2nd occurrence** follows the underlying .NET implementation and is not documented as stable. Prefer lists when order must be predictable; see remarks on [Get Item With Key][]. -For example: +### Matching items and text -- Get Index Of Item With Value (returns predetermined value) -- Remove Item With Value (no operation performed) -- Get Item With Key (throws OccurrenceNotPresentException) +* **Lists and dictionaries (by value)** — whether an item matches uses block [Object Equality][] rules (reference equality first, then value equality for reference types). +* **Text** — whether a segment matches uses the block’s [Comparison Type][] and [Search Options][]; see [Equality][]. ### Known Limitations @@ -51,16 +139,78 @@ None ### Related Concepts -TODO +* [Collections][] +* [What is a Collection?][] +* [Items][items] +* [Keys][keys] +* [Indexes][indexes] +* [Object Equality][] +* [Working with Text][] ### Related Data Types -TODO +* [List<TItem>][] +* [Dictionary<TKey, TItem>][] +* [String][] +* [Int32][] ### Related Blocks -TODO +* [List][] — [Get Index Of Item With Value][], [Get Indexes Of Items With Value][], [Set Item With Value][], [Remove Item With Value][] +* [Dictionary][] — [Get Item With Key][], [Set Item With Key][], [Remove Item With Key][], [Set Dictionary Item With Value][], [Remove Dictionary Item With Value][] +* Text — [Find Text][], [Find All Text][], [Get Index Of Text][], [Find And Replace Text][], [Find And Remove Text][] ### External Documentation -TODO +* [Indices and ranges (C#)][MS Indices and Ranges] — index-from-end (`^n`) vs occurrence numbering +* [System.Collections.Generic.List<T>.IndexOf][MS List IndexOf] — returns the **first** 0-based index of a value (contrast with nth occurrence) +* [System.String.IndexOf][MS String IndexOf] — first match in a string +* [System.String.LastIndexOf][MS String LastIndexOf] — last match in a string + +[Collections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.MainDoc" >}} +[What is a Collection?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.WhatIsACollection.MainDoc" >}} +[items]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Items.MainDoc" >}} +[keys]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Keys.MainDoc" >}} +[indexes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Collections.Indexes.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[Working with Text]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Comparison Type]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.ComparisonTypes.MainDoc" >}} +[Search Options]: {{< url path="Cortex.Reference.DataTypes.Text.SearchOptions.MainDoc" >}} +[Text To Find]: {{< url path="Cortex.Reference.DataTypes.Text.TextToFind.MainDoc" >}} + +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[List<TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[Dictionary]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[Dictionary<TKey, TItem>]: {{< url path="Cortex.Reference.DataTypes.Collections.Dictionary.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} + +[Literal]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[index expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.IndexExpressions" >}} + +[Get Item At Index]: {{< url path="Cortex.Reference.Blocks.Lists.GetItem.GetItemAtIndex.MainDoc" >}} +[Get Index Of Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexOfItemWithValue.MainDoc" >}} +[Get Indexes Of Items With Value]: {{< url path="Cortex.Reference.Blocks.Lists.GetIndex.GetIndexesOfItemsWithValue.MainDoc" >}} +[Set Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.SetItem.SetItemWithValue.MainDoc" >}} +[Remove Item With Value]: {{< url path="Cortex.Reference.Blocks.Lists.RemoveItem.RemoveItemWithValue.MainDoc" >}} +[Set Dictionary Item With Value]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithValue.MainDoc" >}} +[Remove Dictionary Item With Value]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithValue.MainDoc" >}} + +[Get Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.GetItem.GetItemWithKey.MainDoc" >}} +[Set Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.SetItem.SetItemWithKey.MainDoc" >}} +[Remove Item With Key]: {{< url path="Cortex.Reference.Blocks.Dictionaries.RemoveItem.RemoveItemWithKey.MainDoc" >}} + +[Find Text]: {{< url path="Cortex.Reference.Blocks.Text.FindText.FindText.MainDoc" >}} +[Find All Text]: {{< url path="Cortex.Reference.Blocks.Text.FindText.FindAllText.MainDoc" >}} +[Get Index Of Text]: {{< url path="Cortex.Reference.Blocks.Text.GetIndex.GetIndexOfText.MainDoc" >}} +[Find And Replace Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText.MainDoc" >}} +[Find And Remove Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndRemoveText.FindAndRemoveText.MainDoc" >}} + +[OccurrenceNotPresentException]: {{< url path="Cortex.Reference.Exceptions.Collections.OccurrenceNotPresentException.MainDoc" >}} + +[MS Indices and Ranges]: {{< url path="MSDocs.CSharp.IndicesAndRanges" >}} +[MS List IndexOf]: {{< url path="MSDocs.DotNet.Api.System.Collections.Generic.IndexOfList" >}} +[MS String IndexOf]: {{< url path="MSDocs.DotNet.Api.System.String.IndexOf" >}} +[MS String LastIndexOf]: {{< url path="MSDocs.DotNet.Api.System.String.LastIndexOf" >}} diff --git a/data/urls.toml b/data/urls.toml index 2372058ef..02eaa1864 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -1565,6 +1565,17 @@ [Cortex.Reference.Blocks.Text.DecodeText] [Cortex.Reference.Blocks.Text.DecodeText.DecodeText] MainDoc = "/docs/reference/blocks/text/decode-text/decode-text-block" + [Cortex.Reference.Blocks.Text.FindText] + [Cortex.Reference.Blocks.Text.FindText.FindAllText] + MainDoc = "/docs/reference/blocks/text/find-text/find-all-text-block" + [Cortex.Reference.Blocks.Text.FindText.FindText] + MainDoc = "/docs/reference/blocks/text/find-text/find-text-block" + [Cortex.Reference.Blocks.Text.FindAndRemoveText] + [Cortex.Reference.Blocks.Text.FindAndRemoveText.FindAndRemoveText] + MainDoc = "/docs/reference/blocks/text/find-and-remove-text/find-and-remove-text-block" + [Cortex.Reference.Blocks.Text.FindAndReplaceText] + [Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText] + MainDoc = "/docs/reference/blocks/text/find-and-replace-text/find-and-replace-text-block" [Cortex.Reference.Blocks.Text.FormatText] [Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue] MainDoc = "/docs/reference/blocks/text/format-text/format-text-with-value-block-1/" @@ -2896,6 +2907,7 @@ Dictionary = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2" IEnumerable_TItem = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1" IList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1" + IndexOfList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.indexof#system-collections-generic-list-1.indexof" List = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1" IReadOnlyList = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlylist-1" IReadOnlyCollection = "https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlycollection-1" @@ -3019,8 +3031,10 @@ ConcatOperator = "https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#-and--operators" StringInterpolation = "https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#string-interpolation" Format = "https://learn.microsoft.com/en-us/dotnet/api/system.string.format" + IndexOf = "https://learn.microsoft.com/en-us/dotnet/api/system.string.indexof" Insert = "https://learn.microsoft.com/en-us/dotnet/api/system.string.insert" Join = "https://learn.microsoft.com/en-us/dotnet/api/system.string.join" + LastIndexOf = "https://learn.microsoft.com/en-us/dotnet/api/system.string.lastindexof" [MSDocs.DotNet.Api.System.TimeSpan] MainDoc = "https://learn.microsoft.com/en-us/dotnet/api/system.timespan" [MSDocs.DotNet.Api.System.Globalization] From faf18f2cf26534622d9fdca76889531edb4f3f2f Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 14:20:24 +0100 Subject: [PATCH 07/11] added documentation for What is a culture --- .../working-with/culture/what-is-a-culture.md | 129 ++++++++++++++++-- 1 file changed, 117 insertions(+), 12 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/what-is-a-culture.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/what-is-a-culture.md index 17a460f40..0b5caccb0 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/what-is-a-culture.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/what-is-a-culture.md @@ -1,45 +1,150 @@ --- title: "What is a Culture?" linkTitle: "What is a Culture?" -description: "Information regarding what a culture is." +description: "Information regarding what a culture is, how it affects formatting and comparison in flows, and the types of culture supported in CORTEX." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO +A **culture** is a set of language and regional conventions that control how text, dates, times, and numbers are formatted, parsed, sorted, and compared. Cultures define patterns such as date order (`1/7/2022` versus `7/1/2022`), decimal separators, currency symbols, calendar rules, and casing behaviour. + +In {{% ctx %}}, culture settings are represented by the [CultureInfo][] data type (`System.Globalization.CultureInfo`). A `CultureInfo` value is often passed as an [IFormatProvider][] to control formatting in blocks and expressions—for example when converting a [DateTime][] to text or formatting currency in a template. + +Which culture applies at runtime matters because the same value can produce different text on different systems. Flows that run on multiple servers should use an explicit culture when consistent output is required, or ensure that all execution environments share the same operating system regional settings when relying on the [Current Culture][]. + +| Area | What culture affects | More information | +| --- | --- | --- | +| Text | Casing, equality, sorting | [Casing][], [Equality][] | +| Dates and times | Format and parse patterns | [Date and Time Formatting][] | +| Numbers | Decimal, currency, and percentage formats | [Formatting][] (text and numeric format specifiers) | + +For how to create and pass `CultureInfo` values in blocks and the [Expression Editor][], see [Creating and using cultures](#creating-and-using-cultures) below and the [CultureInfo][] data type page. ## Types of Culture -TODO: +{{% ctx %}} supports the culture categories described on the [CultureInfo][] data type and in the pages in this section. The table below summarises each type; follow the links for detail and usage guidance. + +| Type | Typical source | Culture-sensitive? | When to use | +| --- | --- | --- | --- | +| [Invariant Culture][] | `CultureInfo.InvariantCulture` or `new CultureInfo("")` | No | Persisting or exchanging data in a culture-independent format; default for some formatting blocks when no provider is specified | +| [Current Culture][] | `CultureInfo.CurrentCulture` (operating system of the server running the flow) | Yes | Presenting dates, times, and numbers in the end user's regional format when the server locale matches that expectation | +| [Specific Cultures][] | `new CultureInfo("en-GB")`, `new CultureInfo("en-US")`, and similar language–region codes | Yes | Explicit control over formatting or comparison for a known locale, independent of server settings | +| [Custom Cultures][] | Cultures registered on the operating system beyond the built-in set | Yes | Organisations that define custom regional settings on Windows | + +### Invariant Culture + +The [Invariant Culture][] is culture-insensitive. It is associated with the English language but not with any country or region, so casing rules, string sort order, and date and number formats remain consistent across systems. This is useful when storing or transmitting data that must not depend on the server's regional settings. + +See [Invariant Culture][] for more detail. + +### Current Culture + +The [Current Culture][] reflects the regional settings of the operating system on the server where the flow execution runs. Casing, sorting, and format patterns therefore depend on how that server is configured and may differ between environments in a cluster. + +When flows rely on the current culture, all servers in the cluster should use the same operating system culture and related regional settings (and should be time synchronised) so that behaviour is predictable. See [Current Culture][] and [Operating System Settings][]. + +### Specific Cultures + +A **specific culture** identifies both a language and a region—for example `en-GB` (British English) or `en-US` (American English). Create one with a language tag such as `new CultureInfo("en-GB")`. + +A **neutral culture** identifies a language only (for example `new CultureInfo("en")`) and is not tied to a country or region. If resources for a specific culture are not available on the operating system, .NET falls back to the associated neutral culture. + +For a list of predefined cultures available on Windows systems, see [Supported Culture Codes][] (the `Language tag` column defines the code passed to `new CultureInfo(...)`). See also [Specific Cultures][]. + +### Custom Cultures + +**Custom cultures** are cultures defined and registered on the operating system in addition to the built-in set. They follow the same rules as other culture-sensitive cultures: formatting, parsing, and comparison depend on the custom definition installed on the server. + +See [Custom Cultures][]. + +## Creating and using cultures + +### In expressions + +Common ways to obtain a `CultureInfo` value in the [Expression Editor][]: + +| Culture | Expression | +| --- | --- | +| Invariant | `CultureInfo.InvariantCulture` or `new CultureInfo("")` | +| Current | `CultureInfo.CurrentCulture` | +| Specific | `new CultureInfo("en-GB")` | +| Neutral | `new CultureInfo("en")` | + +`CultureInfo` implements [IFormatProvider][]. Pass it to formatting APIs (for example `String.Format`, `DateTime.ToString`, or `Convert.ToString`) or to block properties named **Format Provider** or **Culture Info** so that the chosen rules apply to the operation. + +### In blocks + +Several blocks accept a [CultureInfo][] or [IFormatProvider][] property—for example [Format Text With Values][], [Format Text With Value][], [Convert Object To Text][], and [Convert To Lowercase][] / [Convert To Uppercase][] (via **Culture Info**). When a format provider is omitted or `null`, formatting blocks typically default to [Invariant Culture][]; see each block's remarks for its default. + +### Invalid cultures -- Link to Culture Pages (Invariant/Current/Specific/Custom) -- For a list of predefined cultures that can be returned on Windows systems see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c +If an invalid culture identifier is supplied where an [IFormatProvider][] is required (for example `new CultureInfo("enaa")`), a [CultureInfoNotFoundException][] is thrown. ## Remarks ### Known Limitations -TODO +* Culture behaviour depends on the cultures installed on the operating system of the server running the flow. A culture code that works on one machine may not be available on another unless the same culture is installed or registered. +* [Current Culture][] reflects the server locale, not necessarily the locale of the user viewing a Gateway form or report. Use an explicit [Specific Culture][Specific Cultures] when output must match a known locale regardless of server settings. +* [Custom Cultures][] are platform-specific; document and test them on every environment where flows execute. ## See Also ### Related Concepts -TODO: Also need sections in working with text (casing, comparisons/equality, O/S settings), dates and time (formats, O/S settings), numbers (formats, O/S settings) etc specifically for how culture affects things - we can then cross link from here +* [Casing][] — culture-specific rules for changing text case +* [Equality][] — culture-sensitive and culture-insensitive text comparison (`StringComparison`) +* [Formatting][] — composite formatting and format providers for text +* [Date and Time Formatting][] — standard and custom date/time patterns; [Operating System Settings][] ### Related Data Types -TODO +* [CultureInfo][] +* [IFormatProvider][] ### Related Blocks -TODO +* [Format Text With Values][] +* [Format Text With Value][] +* [Convert Object To Text][] +* [Convert To Lowercase][] +* [Convert To Uppercase][] ### External Documentation -TODO +* [Supported Culture Codes][] +* [System.Globalization.CultureInfo][] +* [System.IFormatProvider][] +* [Performing culture-insensitive string operations](https://learn.microsoft.com/en-us/dotnet/core/extensions/performing-culture-insensitive-string-operations) +* [Best practices for comparing strings in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings) + +[Invariant Culture]: {{< ref "invariant-culture.md" >}} +[Current Culture]: {{< ref "current-culture.md" >}} +[Specific Cultures]: {{< ref "specific-cultures.md" >}} +[Custom Cultures]: {{< ref "custom-cultures.md" >}} + +[Casing]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Casing.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} + +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[DateTime]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTime.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} + +[Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} +[Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert To Lowercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToLowerCase.MainDoc" >}} +[Convert To Uppercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToUpperCase.MainDoc" >}} + +[Supported Culture Codes]: {{< url path="MSDocs.CSharp.SupportedCultureCodes" >}} +[System.Globalization.CultureInfo]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfo" >}} +[System.IFormatProvider]: {{< url path="MSDocs.DotNet.Api.System.IFormatProvider" >}} From 7413bd6f80b99e962ca342a30a92a5ce74004d4a Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 14:33:51 +0100 Subject: [PATCH 08/11] added invariant culture documentation --- .../working-with/culture/invariant-culture.md | 169 +++++++++++++++++- data/urls.toml | 1 + 2 files changed, 161 insertions(+), 9 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/invariant-culture.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/invariant-culture.md index 2883b3106..768e25bfc 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/invariant-culture.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/invariant-culture.md @@ -1,38 +1,189 @@ --- title: "Invariant Culture" linkTitle: "Invariant Culture" -description: "Information related to working with Invariant Culture." +description: "How the invariant culture provides culture-independent formatting, parsing, casing, and comparison in CORTEX flows." weight: 100 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: summary +The **invariant culture** is a `CultureInfo` value that is **culture-insensitive**: it is associated with the English language but not with any country or region. Casing rules, string sort order, and patterns for dates, times, and numbers stay **consistent across servers** and are **not affected** when the operating system regional settings change. + +In {{% ctx %}} and .NET, the invariant culture is represented by `CultureInfo.InvariantCulture` (or `new CultureInfo("")`). It implements [IFormatProvider][] and is the typical default when a block's **Format Provider** or **Culture Info** property is omitted or set to `null`. + +Use the invariant culture when data must be stored, compared, or exchanged in a **culture-independent** form—for example log files, protocol payloads, identifiers, or values passed between flows on different servers. Use a [Current Culture][] or [Specific Culture][] when output is shown to users and must follow a particular locale. + +For an overview of all culture types, see [What is a Culture?][]. + +## Obtaining the invariant culture + +| Approach | Expression | Notes | +| --- | --- | --- | +| Static property | `CultureInfo.InvariantCulture` | Preferred; returns the shared invariant instance | +| Empty culture name | `new CultureInfo("")` | Equivalent culture; creates a new instance | + +Pass the value to formatting APIs (`String.Format`, `DateTime.ToString`, `Convert.ToString`) or to block properties named **Format Provider** or **Culture Info**. + +## What the invariant culture controls + +The invariant culture supplies the same `CultureInfo` data that other cultures provide—calendar, `DateTimeFormatInfo`, `NumberFormatInfo`, `TextInfo`, and sort rules—but those settings are **fixed** and cannot be customized through Windows regional options. The [glossary][Invariant Culture glossary] describes this as a stable, unchanging rule set. + +| Area | Behaviour with invariant culture | More information | +| --- | --- | --- | +| Dates and times | Fixed patterns (for example US-style month/day order in default `DateTimeOffset` output) | [Date and Time Formatting][], [Convert Date Time To Text][] | +| Numbers | Fixed decimal, group, and currency symbols | [Formatting][] | +| Text casing | Culture-insensitive casing rules (not tied to the server's current locale) | [Casing][] | +| Text comparison | `StringComparison.InvariantCulture` and `StringComparer.InvariantCulture` use the invariant culture's sort rules | [Equality][] | +| Composite formatting | Placeholders in format templates use invariant number and date formats | [Format Text With Values][], [Format Text With Value][] | + +### Dates and times + +When [Convert Date Time To Text][] runs with `CultureInfo.InvariantCulture` and no custom format template, the default text representation uses the invariant short date and time pattern (for example `12/31/2021 00:00:00 +00:00` for `2021-12-31T00:00:00+00:00`). Standard format specifiers such as `"F"` (full date long time) use invariant long patterns (for example `Friday, 31 December 2021 00:00:00`). + +Some standard date/time format strings (`"O"`, `"o"`, `"R"`, `"r"`, `"s"`, and `"u"`) are defined by the invariant culture and produce representations intended to be **identical across cultures**—useful for persistence and round-tripping. See [Date and Time Formatting][] and [standard date and time format strings][MS Standard DateTime Formats]. + +### Numbers and composite text + +Formatting numeric placeholders in a format template with `CultureInfo.InvariantCulture` uses invariant decimal separators and group sizes. Several text-formatting blocks default to invariant culture when **Format Provider** is not set; see [Defaults in blocks](#defaults-in-blocks). + +### Casing + +For the invariant culture, rules that change text case are **not** based on the server's [Current Culture][]. Blocks such as [Convert To Lowercase][] and [Convert To Uppercase][] document a **culture-insensitive** conversion with default `CultureInfo.InvariantCulture`. + +If you pass a different `CultureInfo` to a casing block, that culture's rules apply instead. + +### String comparison + +`StringComparison.InvariantCulture` and `StringComparison.InvariantCultureIgnoreCase` compare strings using the **invariant culture's linguistic sort rules**, not ordinal (byte) comparison. They are still useful when comparisons must not follow the server's current locale—for example internal identifiers—but must respect culture-independent sort semantics. + +For ordinal comparisons (no culture), use `StringComparison.Ordinal` or `StringComparison.OrdinalIgnoreCase`. See [Equality][]. + +## When to use the invariant culture + +Use `CultureInfo.InvariantCulture` when: + +* **Persisting or transmitting data** — file names, configuration, database fields, or message formats that must parse the same on every server +* **Internal logic** — comparing tokens, protocol values, or symbolic names that are not shown to end users +* **Consistent block defaults** — relying on the default **Format Provider** so flows behave the same in development, test, and production regardless of server locale +* **Security-sensitive operations** — string comparisons or case changes that must not change when `CurrentCulture` changes (see [Security and display](#security-and-display)) + +Prefer [Specific Cultures][] (for example `new CultureInfo("en-GB")`) or [Current Culture][] when presenting dates, times, numbers, or text to users in a particular regional format. + +## When not to use the invariant culture + +The invariant culture is **not** a substitute for localization. Results can be **linguistically incorrect** or **culturally inappropriate** for user-facing text—for example sorting or casing that does not match user expectations, or date formats that do not match the user's region. + +Do not use invariant culture for display-only formatting when the audience expects a specific locale; set an explicit culture instead. + +## Defaults in blocks + +Many blocks use `CultureInfo.InvariantCulture` when no format provider is supplied or when the property is `null`: + +| Block / property | Default or null behaviour | +| --- | --- | +| [Format Text With Values][] — **Format Provider** | `CultureInfo.InvariantCulture` | +| [Format Text With Value][] — **Format Provider** | `CultureInfo.InvariantCulture` | +| [Convert Object To Text][] — **Format Provider** | `CultureInfo.InvariantCulture` | +| [Convert Date Time To Text][] — **Format Provider** | `CultureInfo.InvariantCulture` | +| [Convert To Lowercase][], [Convert To Uppercase][], [Convert To Camel Case][], [Convert To Pascal Case][], [Convert To Title Case][] — **Culture Info** | `CultureInfo.InvariantCulture`; `null` is treated as invariant | + +Always check a block's **Format Provider** or **Culture Info** remarks if behaviour must differ from these defaults. + +## Security and display + +Microsoft guidance for .NET applies to {{% ctx %}} flows as well: + +* **Display to users** — sorting and formatting shown in Gateway or reports should usually be **culture-sensitive** (often [Current Culture][] or a chosen [Specific Culture][]). +* **Internal or security use** — comparisons and case changes that affect security or stored identifiers should be **culture-insensitive**; pass `CultureInfo.InvariantCulture` (or use `StringComparison.InvariantCulture` / `StringComparer.InvariantCulture` in expressions) so results do not depend on the server's regional settings. + +If a security decision depends on a string comparison or case-change operation, use the invariant culture (or ordinal comparison where appropriate) so behaviour stays consistent. Restrict invariant culture to operations that genuinely require culture-independent results; for general user-visible text, use the appropriate user or regional culture. + +## Invariant culture vs other cultures + +| | Invariant | [Current Culture][] | [Specific Culture][] | +| --- | --- | --- | --- | +| Source | `CultureInfo.InvariantCulture` | `CultureInfo.CurrentCulture` | `new CultureInfo("en-GB")`, etc. | +| Tied to server OS settings | No | Yes | No (fixed by culture name) | +| Customizable via Control Panel | No | Yes (overrides possible) | Per-culture install | +| Typical use | Persistence, protocols, defaults | Server-local presentation | Known locale for users | ## Remarks ### Known Limitations -TODO +* The invariant culture reflects **English-based, region-neutral** conventions. It does not represent "no culture"—ordinal comparisons and some Unicode rules are separate concerns; see [Equality][]. +* **User locale ≠ server locale.** Invariant or current server culture does not automatically match a Gateway user's language or region; pass an explicit [Specific Culture][] when that matters. +* Invalid culture identifiers (for example `new CultureInfo("enaa")`) still throw [CultureInfoNotFoundException][]; only `""` and `InvariantCulture` refer to the invariant culture. ## See Also ### Related Concepts -TODO +* [What is a Culture?][] +* [Current Culture][] +* [Specific Cultures][] +* [Custom Cultures][] +* [Casing][] +* [Equality][] +* [Formatting][] +* [Date and Time Formatting][] ### Related Data Types -TODO +* [CultureInfo][] +* [IFormatProvider][] +* [StringComparison][] +* [StringComparer][] ### Related Blocks -TODO +* [Format Text With Values][] +* [Format Text With Value][] +* [Convert Object To Text][] +* [Convert Date Time To Text][] +* [Convert To Lowercase][] +* [Convert To Uppercase][] +* [Convert To Camel Case][] +* [Convert To Pascal Case][] +* [Convert To Title Case][] ### External Documentation -TODO +* [CultureInfo.InvariantCulture](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.invariantculture) +* [Performing culture-insensitive string operations](https://learn.microsoft.com/en-us/dotnet/core/extensions/performing-culture-insensitive-string-operations) +* [Best practices for comparing strings in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings) +* [Standard date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) +* [System.Globalization.CultureInfo][MS CultureInfo] + +[What is a Culture?]: {{< ref "what-is-a-culture.md" >}} +[Current Culture]: {{< ref "current-culture.md" >}} +[Specific Cultures]: {{< ref "specific-cultures.md" >}} +[Specific Culture]: {{< ref "specific-cultures.md" >}} +[Custom Cultures]: {{< ref "custom-cultures.md" >}} + +[Casing]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Casing.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} + +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[StringComparison]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparison.MainDoc" >}} +[StringComparer]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparer.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} +[Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Convert To Lowercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToLowerCase.MainDoc" >}} +[Convert To Uppercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToUpperCase.MainDoc" >}} +[Convert To Camel Case]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToCamelCase.MainDoc" >}} +[Convert To Pascal Case]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToPascalCase.MainDoc" >}} +[Convert To Title Case]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToTitleCase.MainDoc" >}} + +[Invariant Culture glossary]: {{< url path="Cortex.Reference.Glossary.F-J.InvariantCulture" >}} +[MS CultureInfo]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfo" >}} +[MS Standard DateTime Formats]: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings diff --git a/data/urls.toml b/data/urls.toml index 02eaa1864..b97f0d8d4 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -2544,6 +2544,7 @@ Item = "/docs/reference/glossary/f-j/#item" Index = "/docs/reference/glossary/f-j/#index" Intellisense = "/docs/reference/glossary/f-j/#intellisense" + InvariantCulture = "/docs/reference/glossary/f-j/#invariant-culture" Json = "/docs/reference/glossary/f-j/#json" [Cortex.Reference.Glossary.K-O] List = "/docs/reference/glossary/k-o/#list" From 61117d5ef3ad2661abdff7d1bfd7fbd0e7dee7db Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 15:20:28 +0100 Subject: [PATCH 09/11] Added current and specific culture documentation --- .../working-with/culture/current-culture.md | 203 +++++++++++++++- .../working-with/culture/specific-cultures.md | 219 +++++++++++++++++- data/urls.toml | 4 + 3 files changed, 407 insertions(+), 19 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md index fe690e697..ffd638cdc 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md @@ -1,38 +1,223 @@ --- title: "Current Culture" linkTitle: "Current Culture" -description: "Information related to working with Current Culture." +description: "How the current culture reflects server regional settings and affects formatting, parsing, casing, and comparison in CORTEX flows." weight: 200 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: summary +The **current culture** is the `CultureInfo` value that represents the **culture-sensitive regional settings of the server** where a flow runs. In .NET and {{% ctx %}} it is exposed as `CultureInfo.CurrentCulture`. + +The current culture controls default patterns for dates, times, numbers, and currency; the sort order of text; casing conventions; and culture-sensitive string comparisons. Those rules come from the operating system (and, on Windows, may include user overrides from **Regional settings**). They can differ between development, test, and production servers, and between nodes in a cluster. + +Use the current culture when formatted output or comparisons should follow the **server's locale** and that locale is known to match what users expect—for example a single-server deployment configured for the target region. Use an explicit [Specific Culture][] when the locale must be fixed regardless of server settings, or [Invariant Culture][] when behaviour must not depend on regional settings. + +For an overview of all culture types, see [What is a Culture?][]. + +## Obtaining the current culture + +| Approach | Expression | Notes | +| --- | --- | --- | +| Static property | `CultureInfo.CurrentCulture` | Returns the culture for the executing thread | + +Pass the value to formatting APIs (`String.Format`, `DateTime.ToString`, `Convert.ToString`) or to block properties named **Format Provider** or **Culture Info** when you want operations to use the server's regional rules. + +{{% ctx %}} does not change `CultureInfo.CurrentCulture` for you. Unless a flow or expression sets the culture explicitly, it reflects how the **server operating system** is configured at runtime. + +## What the current culture controls + +The object returned by `CultureInfo.CurrentCulture` supplies the same kinds of data as other cultures—`DateTimeFormatInfo`, `NumberFormatInfo`, `TextInfo`, compare info, and calendar settings—but those values follow the **installed and configured locale** of the server (subject to .NET thread and user-override rules described below). + +| Area | Behaviour with current culture | More information | +| --- | --- | --- | +| Dates and times | Short/long patterns and separators from the server locale (for example `dd/MM/yyyy` versus `MM/dd/yyyy`) | [Date and Time Formatting][], [Operating System Settings][] | +| Numbers | Decimal separator, group sizes, and currency symbols from the server locale | [Formatting][] | +| Text casing | Rules from the server's culture (for example Turkish *I* / *ı*) | [Casing][] | +| Text comparison | `StringComparison.CurrentCulture` and `StringComparer.CurrentCulture` use the current culture's sort rules | [Equality][] | +| Composite formatting | Placeholders in format templates use the current culture's number and date formats | [Format Text With Values][], [Format Text With Value][] | + +### Dates and times + +When you pass `CultureInfo.CurrentCulture` to [Convert Date Time To Text][] or use it as a **Format Provider**, standard format specifiers such as `"d"` (short date) and `"G"` (general date long time) use patterns defined for the **current culture**, which on Windows can reflect **Control Panel** regional customizations when they apply to `CurrentCulture`. See [Operating System Settings][] and [standard date and time format strings][MS Standard DateTime Formats]. + +Formats intended to be identical across cultures (`"O"`, `"s"`, `"u"`, and similar) are still defined in terms of the invariant culture; using `CurrentCulture` does not change those specifiers' cross-culture definitions. + +### Numbers and composite text + +Numeric placeholders in a format template respect the current culture's decimal and group separators. To format for the server locale, set **Format Provider** to `CultureInfo.CurrentCulture` on blocks such as [Format Text With Values][] or [Convert Object To Text][]. + +Many blocks default to [Invariant Culture][] when **Format Provider** is omitted or `null`; they do **not** automatically use the current culture. See [Using the current culture in blocks](#using-the-current-culture-in-blocks). + +### Casing + +When you pass `CultureInfo.CurrentCulture` to blocks such as [Convert To Lowercase][] or [Convert To Uppercase][], casing follows the **culture-sensitive** rules of the server's current culture (for example `TextInfo.ToUpper` behaviour). + +The default **Culture Info** on those blocks is `CultureInfo.InvariantCulture`, not the current culture. + +### String comparison + +`StringComparison.CurrentCulture` and `StringComparison.CurrentCultureIgnoreCase` compare strings using the **current culture's linguistic sort rules**, with or without case sensitivity. Text blocks that expose **Comparison Type** (for example [Contains Any Text][], [Find And Replace Text][]) can use these values when comparisons should follow the server locale. + +With **Regex** or **Pattern matching** search options, some character equivalences (for example `æ` and `ae`) may not match when **Comparison Type** is `StringComparison.CurrentCulture`; see each block's remarks. + +For comparisons that must not change when server regional settings change, use [Invariant Culture][] or ordinal comparison instead. See [Equality][] and [Security and display](#security-and-display). + +## When to use the current culture + +Use `CultureInfo.CurrentCulture` when: + +* **Server locale matches users** — the machine running the flow is configured for the same region as the audience (for example a UK-hosted server for `en-GB` output). +* **Ad-hoc locale alignment** — you intentionally want "whatever this server is set to" without hard-coding a culture name. +* **Culture-sensitive comparisons** — text search or equality should follow the server's sort and equivalence rules (`StringComparison.CurrentCulture` or `StringComparer.CurrentCulture`). + +In clustered deployments, align **operating system culture and regional settings** (and time synchronisation) on every node that runs the flow so that `CultureInfo.CurrentCulture` behaves the same everywhere. See [Clusters and operating system settings](#clusters-and-operating-system-settings). + +## When not to use the current culture + +Avoid relying on the current culture when: + +* **Output must be identical on every server** — use [Invariant Culture][] or an explicit [Specific Culture][]. +* **User locale ≠ server locale** — Gateway forms, reports, or integrations may be viewed in a different region than the flow server; pass `new CultureInfo("en-GB")` (or another known code) instead. +* **Security or internal identifiers** — comparisons or case changes that affect authorization, tokens, or stored keys should use [Invariant Culture][] or ordinal rules so behaviour does not change when an administrator adjusts regional settings. +* **Persistence or protocols** — logs, file names, APIs, and database fields that must parse the same everywhere should use [Invariant Culture][]. + +If the required locale is known in advance, prefer an explicit [Specific Culture][] over the current culture so behaviour does not depend on how each server is configured. + +## Using the current culture in blocks + +The current culture is **opt-in** in most blocks: + +| Scenario | Typical approach | +| --- | --- | +| Format dates, numbers, or composite text for the server locale | Set **Format Provider** to `CultureInfo.CurrentCulture` | +| Change case using server rules | Set **Culture Info** to `CultureInfo.CurrentCulture` | +| Compare or search text using server sort rules | Set **Comparison Type** to `StringComparison.CurrentCulture` or `StringComparison.CurrentCultureIgnoreCase` | +| Default when property omitted or `null` | Usually [Invariant Culture][] — check the block's remarks | + +Always confirm a block's default when **Format Provider** or **Culture Info** is left empty. + +## Server locale vs user locale + +`CultureInfo.CurrentCulture` reflects the **flow execution server**, not the browser or desktop of someone using Gateway. A user in one country may see data formatted with another region's patterns if the server culture differs. + +When output must match a **known user or business locale**, create and pass an explicit culture—for example `new CultureInfo("en-GB")`—documented on [Specific Cultures][]. Use the current culture only when the server's regional settings are the intended source of truth. + +## Clusters and operating system settings + +Flows that depend on `CultureInfo.CurrentCulture` inherit whatever culture .NET assigns to the executing thread. On Windows that is typically the system default locale; on Linux it follows ICU / `LC_MESSAGES` behaviour as described in .NET documentation. + +For predictable results in a **cluster**: + +* Use the **same culture and regional settings** on every server that can run the flow. +* Keep servers **time synchronised** when date and time values are compared or formatted across nodes. +* Document any **custom** or **user-overridden** regional settings; see [Operating System Settings][]. + +Changing Control Panel or system locale on one node without updating the others can cause formatting, parsing, or comparison differences between environments. + +### User overrides (Windows) + +On Windows, users can customize date, time, and number formats in **Regional settings**. Those overrides can flow into `CultureInfo.CurrentCulture` when the thread culture is tied to the system culture. Server applications that require **standard** patterns for a culture name may need `new CultureInfo("en-GB", false)` or `CultureInfo.GetCultureInfo("en-GB")` instead of `CurrentCulture`—see [CultureInfo(String, Boolean)][MS CultureInfo ctor] in .NET documentation. + +## Security and display + +Microsoft guidance for .NET applies to {{% ctx %}} flows: + +* **Display to users** — formatting and sorting shown in Gateway or reports is often **culture-sensitive**. The current culture is appropriate only when the server locale matches the audience; otherwise use a [Specific Culture][]. +* **Internal or security use** — do not use `CurrentCulture` or `StringComparison.CurrentCulture` for security decisions, stored identifiers, or protocol values. Use [Invariant Culture][] or ordinal comparison so results stay stable when regional settings change. + +If a security decision depends on string comparison or case change, use culture-independent options described on [Invariant Culture][]. + +## Current culture vs other cultures + +| | [Current Culture][] | [Invariant Culture][] | [Specific Culture][] | +| --- | --- | --- | --- | +| Source | `CultureInfo.CurrentCulture` | `CultureInfo.InvariantCulture` | `new CultureInfo("en-GB")`, etc. | +| Tied to server OS settings | Yes | No | No (fixed by culture name) | +| Changes when OS regional settings change | Yes | No | Only if culture definition on OS changes | +| Typical block default when provider is `null` | No (usually invariant) | Yes (many formatting/casing blocks) | Only when you set it | +| Typical use | Server-local presentation when locale is intentional | Persistence, protocols, defaults | Known locale for users | ## Remarks ### Known Limitations -TODO +* **Per-thread in .NET** — `CultureInfo.CurrentCulture` is a per-thread setting. In {{% ctx %}}, culture generally follows the server and thread that executes the flow; do not assume you can set culture in one block and have unrelated parallel work use a different culture unless you control thread culture explicitly (uncommon in flows). +* **Not installed cultures** — `CurrentCulture` reflects the server's configured locale; creating `new CultureInfo("xx-YY")` for a culture that is not installed still throws [CultureInfoNotFoundException][]. That is separate from reading `CurrentCulture`. +* **User locale ≠ server locale** — see [Server locale vs user locale](#server-locale-vs-user-locale). +* **Current UI culture** — .NET also exposes `CultureInfo.CurrentUICulture` for resource loading. {{% ctx %}} formatting and comparison documentation focus on `CurrentCulture`; UI resource culture is rarely set in flows. ## See Also ### Related Concepts -TODO +* [What is a Culture?][] +* [Invariant Culture][] +* [Specific Cultures][] +* [Custom Cultures][] +* [Casing][] +* [Equality][] +* [Formatting][] +* [Date and Time Formatting][] ### Related Data Types -TODO +* [CultureInfo][] +* [IFormatProvider][] +* [StringComparison][] +* [StringComparer][] ### Related Blocks -TODO +* [Format Text With Values][] +* [Format Text With Value][] +* [Convert Object To Text][] +* [Convert Date Time To Text][] +* [Convert To Lowercase][] +* [Convert To Uppercase][] +* [Contains Any Text][] +* [Find And Replace Text][] ### External Documentation -TODO +* [CultureInfo.CurrentCulture][MS CurrentCulture] +* [CultureInfo.CurrentCulture property remarks](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-globalization-cultureinfo-currentculture) +* [Standard date and time format strings — Control Panel settings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#control-panel-settings) +* [Best practices for comparing strings in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings) +* [Performing culture-insensitive string operations](https://learn.microsoft.com/en-us/dotnet/core/extensions/performing-culture-insensitive-string-operations) +* [System.Globalization.CultureInfo][MS CultureInfo] + +[What is a Culture?]: {{< ref "what-is-a-culture.md" >}} +[Invariant Culture]: {{< ref "invariant-culture.md" >}} +[Current Culture]: {{< ref "current-culture.md" >}} +[Specific Cultures]: {{< ref "specific-cultures.md" >}} +[Specific Culture]: {{< ref "specific-cultures.md" >}} +[Custom Cultures]: {{< ref "custom-cultures.md" >}} + +[Casing]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Casing.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} + +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[StringComparison]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparison.MainDoc" >}} +[StringComparer]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparer.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} +[Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Convert To Lowercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToLowerCase.MainDoc" >}} +[Convert To Uppercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToUpperCase.MainDoc" >}} +[Contains Any Text]: {{< url path="Cortex.Reference.Blocks.Text.ContainsText.ContainsAnyText.MainDoc" >}} +[Find And Replace Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText.MainDoc" >}} + +[MS CultureInfo]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfo" >}} +[MS CultureInfo ctor]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoCtor" >}} +[MS CurrentCulture]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CurrentCulture" >}} +[MS Standard DateTime Formats]: {{< url path="MSDocs.DotNet.Api.System.DateTimeOffset.FormatStrings" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md index 65bd9cb78..e1a305a4e 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md @@ -1,40 +1,239 @@ --- title: "Specific Cultures" linkTitle: "Specific Cultures" -description: "Information related to working with specific cultures." +description: "How specific and neutral cultures provide explicit locale control for formatting, parsing, casing, and comparison in CORTEX flows." weight: 300 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: summary +A **specific culture** is a `CultureInfo` value that identifies both a **language and a region**—for example `en-GB` (British English) or `en-US` (American English). It supplies culture-sensitive rules for dates, times, numbers, currency, text casing, and string sorting that are **fixed by the culture name**, not by whatever regional settings happen to be on the server. + +In {{% ctx %}} and .NET, create a specific culture with a language tag such as `new CultureInfo("en-GB")` and pass it as an [IFormatProvider][] to formatting APIs or to block properties named **Format Provider** or **Culture Info**. + +Use a specific culture when output or comparisons must follow a **known locale** regardless of server configuration—for example Gateway users in `en-GB` while the flow server runs `en-US`, or multi-node clusters where every node must format the same way. Use [Current Culture][] when the server's locale is intentionally the source of truth, or [Invariant Culture][] when behaviour must not depend on any locale. + +A **neutral culture** identifies a language only (for example `new CultureInfo("en")`). Neutral cultures are culture-sensitive but not tied to a country or region. If a specific culture is not available on the operating system, .NET can fall back to the associated neutral culture. + +For an overview of all culture types, see [What is a Culture?][]. + +## Specific cultures and neutral cultures + +| | Specific culture | Neutral culture | +| --- | --- | --- | +| Example name | `en-GB`, `fr-FR`, `ja-JP` | `en`, `fr`, `ja` | +| Identifies | Language **and** region | Language only | +| Typical creation | `new CultureInfo("en-GB")` | `new CultureInfo("en")` | +| Formatting | Region-specific date, time, and number patterns | Language defaults; less precise for regional display | +| Fallback | If `en-GB` is missing, .NET may use `en` | Parent of specific cultures in the culture hierarchy | + +Specific cultures are the usual choice for **user-facing** regional formatting when you know the target locale. Neutral cultures are appropriate when only the language matters, or when you rely on .NET fallback behaviour. + +To obtain a specific culture from a neutral name, .NET provides `CultureInfo.CreateSpecificCulture("en")` (the exact result depends on the operating system—for example `en-US` on many Windows installations). Prefer an explicit tag such as `en-GB` when the region is known. + +## Obtaining a specific culture + +| Approach | Expression | Notes | +| --- | --- | --- | +| Constructor | `new CultureInfo("en-GB")` | Creates a new instance; may reflect user overrides on Windows when the second parameter is omitted or `true` | +| Constructor without user overrides | `new CultureInfo("en-GB", false)` | Uses standard patterns for the culture name, not Control Panel customizations | +| Cached instance | `CultureInfo.GetCultureInfo("en-GB")` | Returns a read-only, cached `CultureInfo`; same culture name returns the same instance | +| From neutral name | `CultureInfo.CreateSpecificCulture("en")` | Maps a neutral culture to a default specific culture for the platform | + +Pass the value to `String.Format`, `DateTime.ToString`, `Convert.ToString`, or to block **Format Provider** / **Culture Info** properties. + +### Culture names and supported codes + +Culture names follow [BCP 47](https://www.rfc-editor.org/info/bcp47) language tags: a language subtag, optionally followed by a region (for example `en-GB`, `pt-BR`). The name passed to `new CultureInfo(...)` is case-insensitive in .NET. + +For a list of predefined cultures on Windows systems, see [Supported Culture Codes][]—the **Language tag** column is the string passed to the constructor. Cultures must be **installed or available** on the server where the flow runs; an unknown or unavailable name throws [CultureInfoNotFoundException][]. + +See also [Custom Cultures][] for cultures registered on the operating system beyond the built-in set. + +## What a specific culture controls + +A specific culture provides the same kinds of data as [Current Culture][]—`DateTimeFormatInfo`, `NumberFormatInfo`, `TextInfo`, compare info, and calendar settings—but values come from the **named culture definition** on the server, not from the thread's current locale (unless that locale happens to match). + +| Area | Behaviour with a specific culture | More information | +| --- | --- | --- | +| Dates and times | Short/long patterns and separators for the named locale (for example `dd/MM/yyyy` for `en-GB` versus `MM/dd/yyyy` for `en-US`) | [Date and Time Formatting][], [Operating System Settings][] | +| Numbers | Decimal separator, group sizes, and currency symbols for the named locale | [Formatting][] | +| Text casing | Culture-sensitive rules for that locale (for example Turkish *I* / *ı* with `tr-TR`) | [Casing][] | +| Text comparison | `StringComparison` and `StringComparer` values that use a supplied `CultureInfo` follow that culture's sort rules | [Equality][] | +| Composite formatting | Placeholders in format templates use the supplied culture's number and date formats | [Format Text With Values][], [Format Text With Value][] | + +### Dates and times + +When you pass `new CultureInfo("en-GB")` to [Convert Date Time To Text][] or set it as **Format Provider**, standard format specifiers such as `"d"` (short date) use patterns defined for **that culture**. Cross-culture invariant specifiers (`"O"`, `"s"`, `"u"`, and similar) keep their invariant definitions regardless of which `CultureInfo` you pass. + +### Numbers and composite text + +Numeric placeholders in a format string use the specific culture's decimal and group separators. Set **Format Provider** to `new CultureInfo("en-GB")` on [Format Text With Values][] or [Convert Object To Text][] when output must match that locale. + +Many blocks default to [Invariant Culture][] when **Format Provider** is omitted or `null`; they do **not** automatically use a specific culture. + +### Casing + +Pass a specific `CultureInfo` to [Convert To Lowercase][] or [Convert To Uppercase][] so casing follows that locale's rules—for example `new CultureInfo("tr-TR")` for Turkish casing behaviour. The default **Culture Info** on those blocks is `CultureInfo.InvariantCulture`. + +### String comparison + +To compare or search text using a fixed locale's sort rules, use a `StringComparer` or `StringComparison` option that is tied to the intended culture (see [Equality][]), or pass the culture into APIs that accept `CultureInfo`. Text blocks with **Comparison Type** (for example [Contains Any Text][], [Find And Replace Text][]) can use culture-sensitive options when comparisons must follow a named locale rather than the server default. + +For identifiers, security checks, or protocol values, prefer [Invariant Culture][] or ordinal comparison so results stay stable. + +## When to use a specific culture + +Use an explicit specific culture when: + +* **User or business locale is known** — Gateway forms, reports, or integrations target `en-GB`, `de-DE`, or another fixed region. +* **Server locale ≠ audience locale** — the flow server and the user are in different regions; do not rely on [Current Culture][]. +* **Consistent output on every server** — all cluster nodes format and compare the same way without depending on each machine's regional settings. +* **Tests and documentation** — flows should behave the same in development, test, and production for a named locale. +* **Regional differences within a language** — `en-US` and `en-GB` differ in date order, currency, and other patterns; pick the tag that matches the audience. + +## When not to use a specific culture + +Avoid assuming a specific culture when: + +* **Culture-independent storage or protocols** — use [Invariant Culture][] for logs, file names, APIs, and database fields that must parse identically everywhere. +* **Security or internal identifiers** — use [Invariant Culture][] or ordinal rules so behaviour does not change with locale choice. +* **Server locale is the intended source** — if the machine is configured for the target region and that is acceptable, [Current Culture][] may be enough; still document the dependency on OS settings. +* **Culture is not installed** — `new CultureInfo("xx-YY")` throws if the culture is unavailable; verify cultures on every execution environment or handle [CultureInfoNotFoundException][]. + +If you only need a language without a region, a neutral culture (`new CultureInfo("en")`) or `CreateSpecificCulture` may suffice; for user-visible regional formatting, prefer an explicit specific tag. + +## Using a specific culture in blocks + +The specific culture is **opt-in** in most blocks: + +| Scenario | Typical approach | +| --- | --- | +| Format dates, numbers, or composite text for a known locale | Set **Format Provider** to `new CultureInfo("en-GB")` (or a variable holding that value) | +| Change case using a locale's rules | Set **Culture Info** to `new CultureInfo("tr-TR")` (or the required culture) | +| Compare or search text using a fixed sort order | Set **Comparison Type** or culture-related properties per the block's remarks | +| Default when property omitted or `null` | Usually [Invariant Culture][] — check each block | + +Store the culture in a variable when the same locale is used in many blocks—for example `var uk = new CultureInfo("en-GB");` in the [Expression Editor][]. + +## User overrides (Windows) + +On Windows, end users can customize date, time, and number formats in **Regional settings**. When `new CultureInfo("en-GB")` is called with default constructor behaviour, those **user overrides** can apply to formatting for that culture. + +When flows require **standard** patterns for the culture name (for example consistent `en-GB` short dates across all servers), use: + +* `new CultureInfo("en-GB", false)` — disable user overrides for that instance, or +* `CultureInfo.GetCultureInfo("en-GB")` — cached culture without user customizations. + +This differs from [Current Culture][], which reflects the server's active locale and may include system-wide or user-specific overrides. See [CultureInfo(String, Boolean)][MS CultureInfo ctor] in .NET documentation. + +## Fallback and parent cultures + +If resources for a specific culture are not available on the operating system, .NET falls back to the associated **neutral culture** (for example from `en-GB` to `en`). The [CultureInfo.Parent][] property exposes this hierarchy. + +Fallback affects installed cultures and resources; it does not turn a specific culture into [Invariant Culture][]. For culture-insensitive behaviour, pass `CultureInfo.InvariantCulture` explicitly. + +## Security and display + +Microsoft guidance for .NET applies to {{% ctx %}} flows: + +* **Display to users** — formatting and sorting in Gateway or reports should use a **culture-sensitive** culture that matches the audience, usually a specific culture you choose explicitly. +* **Internal or security use** — do not use culture-sensitive formatting or `StringComparison.CurrentCulture` for security decisions, stored keys, or protocol values when the culture might not match the security domain; use [Invariant Culture][] or ordinal comparison. + +## Specific culture vs other cultures -- For a list of cultures that can be returned on Windows systems see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c under The Language table (`Language tag` defines the code required to create the culture) +| | [Specific Culture][] | [Current Culture][] | [Invariant Culture][] | +| --- | --- | --- | --- | +| Source | `new CultureInfo("en-GB")`, etc. | `CultureInfo.CurrentCulture` | `CultureInfo.InvariantCulture` | +| Tied to server OS settings | No (fixed by name) | Yes | No | +| Changes when OS regional settings change | Only if culture definition or overrides for that name change | Yes | No | +| Typical block default when provider is `null` | No (usually invariant) | No (usually invariant) | Yes (many formatting/casing blocks) | +| Typical use | Known locale for users or contracts | Server-local presentation when locale is intentional | Persistence, protocols, defaults | ## Remarks ### Known Limitations -TODO +* **Installed cultures** — A culture code that works on one machine may throw [CultureInfoNotFoundException][] on another unless the same culture is installed or registered. Test on every environment where flows execute. +* **Neutral vs specific** — `new CultureInfo("en")` is a neutral culture; date and number patterns may not match a particular country. Use `en-GB`, `en-US`, or another specific tag when the region matters. +* **User locale ≠ server locale** — An explicit specific culture fixes formatting for the chosen name; it does not automatically detect a Gateway user's browser locale unless your flow supplies that code. +* **Custom cultures** — Organisations may register [Custom Cultures][] on Windows; behaviour depends on that registration on each server. +* **Platform differences** — `CreateSpecificCulture("en")` may return different specific cultures on different operating systems; prefer explicit tags when consistency is required. ## See Also ### Related Concepts -TODO +* [What is a Culture?][] +* [Invariant Culture][] +* [Current Culture][] +* [Custom Cultures][] +* [Casing][] +* [Equality][] +* [Formatting][] +* [Date and Time Formatting][] ### Related Data Types -TODO +* [CultureInfo][] +* [IFormatProvider][] +* [StringComparison][] +* [StringComparer][] ### Related Blocks -TODO +* [Format Text With Values][] +* [Format Text With Value][] +* [Convert Object To Text][] +* [Convert Date Time To Text][] +* [Convert To Lowercase][] +* [Convert To Uppercase][] +* [Contains Any Text][] +* [Find And Replace Text][] ### External Documentation -TODO +* [CultureInfo class](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo) +* [CultureInfo.GetCultureInfo](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.getcultureinfo) +* [CultureInfo.CreateSpecificCulture](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.createspecificculture) +* [CultureInfo.Parent property](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.parent) +* [Best practices for comparing strings in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings) +* [Performing culture-insensitive string operations](https://learn.microsoft.com/en-us/dotnet/core/extensions/performing-culture-insensitive-string-operations) +* [Supported Culture Codes][] +* [System.Globalization.CultureInfo][MS CultureInfo] + +[What is a Culture?]: {{< ref "what-is-a-culture.md" >}} +[Invariant Culture]: {{< ref "invariant-culture.md" >}} +[Current Culture]: {{< ref "current-culture.md" >}} +[Specific Culture]: {{< ref "specific-cultures.md" >}} +[Custom Cultures]: {{< ref "custom-cultures.md" >}} + +[Casing]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Casing.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} + +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[StringComparison]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparison.MainDoc" >}} +[StringComparer]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparer.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} + +[Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} +[Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Convert To Lowercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToLowerCase.MainDoc" >}} +[Convert To Uppercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToUpperCase.MainDoc" >}} +[Contains Any Text]: {{< url path="Cortex.Reference.Blocks.Text.ContainsText.ContainsAnyText.MainDoc" >}} +[Find And Replace Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText.MainDoc" >}} + +[Supported Culture Codes]: {{< url path="MSDocs.CSharp.SupportedCultureCodes" >}} +[MS CultureInfo]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfo" >}} +[MS CultureInfo ctor]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoCtor" >}} +[CultureInfo.Parent]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoParent" >}} diff --git a/data/urls.toml b/data/urls.toml index b97f0d8d4..9ac920aaf 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -2950,6 +2950,7 @@ UtcDateTime = "https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.utcdatetime" MinValue = "https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.minvalue" ToString = "https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.ToString" + FormatStrings = "https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings" ShortDateFormat = "https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#ShortDate" ShortTimeFormat = "https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#ShortTime" LongDateFormat = "https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#LongDate" @@ -3040,7 +3041,10 @@ MainDoc = "https://learn.microsoft.com/en-us/dotnet/api/system.timespan" [MSDocs.DotNet.Api.System.Globalization] CultureInfo = "https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo" + CultureInfoCtor = "https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.-ctor" CultureInfoNotFoundException = "https://learn.microsoft.com/en-us/dotnet/api/system.globalization.culturenotfoundexception" + CultureInfoParent = "https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.parent" + CurrentCulture = "https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.currentculture" [MSDocs.DotNet.Api.System.Text] Encoding = "https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding" UnicodeEncoding = "https://learn.microsoft.com/en-us/dotnet/api/system.text.unicodeencoding" From 0abfb9ccdde104fabab7f93e5ef8510d25c6b4aa Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Wed, 3 Jun 2026 15:34:46 +0100 Subject: [PATCH 10/11] added Custom Culture Documentation --- .../working-with/culture/custom-cultures.md | 237 +++++++++++++++++- data/urls.toml | 4 + 2 files changed, 232 insertions(+), 9 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md index a15883e92..754120b73 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md @@ -1,38 +1,257 @@ --- title: "Custom Cultures" linkTitle: "Custom Cultures" -description: "Information related to working with custom cultures." +description: "How custom cultures registered on Windows extend or replace built-in locales for formatting, parsing, casing, and comparison in CORTEX flows." weight: 400 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: summary +A **custom culture** is a `CultureInfo` value whose definition is **registered on the operating system** in addition to (or instead of) the built-in cultures that .NET ships with. After registration, a custom culture behaves like any other culture-sensitive culture: it supplies rules for dates, times, numbers, currency, text casing, and string sorting according to the definition stored on that computer. + +In {{% ctx %}} and .NET you **do not** define or register custom cultures inside a flow. An administrator (or deployment tooling) registers the culture on each Windows server using [CultureAndRegionInfoBuilder][MS CultureAndRegionInfoBuilder] in the `sysglobl` assembly. Flows and expressions then use the culture by name—for example `new CultureInfo("x-en-US-sample")` or `CultureInfo.GetCultureInfo("x-en-US-sample")`—and pass it as an [IFormatProvider][] to formatting APIs or to block properties named **Format Provider** or **Culture Info**. + +Use custom cultures when your organisation needs a **locale that is not in the predefined set**, or when you must **override** a built-in culture (for example a replacement `en-GB` with different date or time patterns) and that override is installed consistently on every server that runs flows. Use a [Specific Culture][Specific Cultures] when a standard language tag such as `en-GB` is sufficient and installed on the server. Use [Invariant Culture][] when behaviour must not depend on any locale. + +For an overview of all culture types, see [What is a Culture?][]. + +## How custom cultures differ from built-in cultures + +| | Built-in ([Specific Culture][Specific Cultures]) | Custom culture | +| --- | --- | --- | +| Definition | Preinstalled with Windows / .NET | Created with `CultureAndRegionInfoBuilder` and registered by an administrator | +| Culture name | Standard BCP 47 tags (`en-GB`, `de-DE`) | Often uses a private subtag (for example `x-en-US-sample`) or **replaces** an existing name | +| Availability | Listed in [Supported Culture Codes][] when installed | Available only on computers where the culture was registered | +| Use in flows | `new CultureInfo("en-GB")` | `new CultureInfo("your-registered-name")` after registration | +| Registration in flows | Not required | **Not supported** — registration requires administrative privileges outside {{% ctx %}} | + +Once registered, a custom culture is **indistinguishable** from predefined cultures for `CultureInfo` construction, `GetCultureInfo`, and `GetCultures`. Formatting, parsing, casing, and comparison in blocks follow the same patterns as for [Specific Cultures][]. + +## Creating and registering custom cultures (administrators) + +Custom cultures are a **Windows and .NET Framework / NLS** feature. The [CultureAndRegionInfoBuilder][MS CultureAndRegionInfoBuilder] class: + +* Lives in **sysglobl.dll** (add a reference to `sysglobl` in registration tools; it is not in the default `System.Globalization` assemblies used by most apps). +* Writes an **.nlp** file to `%windir%\Globalization` when you call `Register()`. +* Requires **administrative privileges** on the target machine; otherwise `Register()` throws `UnauthorizedAccessException`. + +{{% ctx %}} flows should **not** call `Register()` or `Unregister()` at runtime. Use a separate installer or script that administrators run during server provisioning, then reference the culture by name in flows. + +### Kinds of custom culture + +The `CultureAndRegionModifiers` value passed to the `CultureAndRegionInfoBuilder` constructor determines the kind of culture: + +| Modifier | Purpose | +| --- | --- | +| `CultureAndRegionModifiers.None` | New **specific** custom culture with a unique name | +| `CultureAndRegionModifiers.Neutral` | New **neutral** custom culture | +| `CultureAndRegionModifiers.Replacement` | **Replaces** an existing built-in culture (same name—for example `en-GB`) with custom date, number, or casing rules on that machine | + +For a **replacement** culture, the builder is constructed with the existing culture name and `Replacement`; properties are populated from the culture being replaced, then modified and registered. + +For a **new** or **supplemental** culture, typical steps are: + +1. `new CultureAndRegionInfoBuilder("x-my-culture", CultureAndRegionModifiers.None)` +2. `LoadDataFromCultureInfo(...)` and `LoadDataFromRegionInfo(...)` to copy from similar built-in cultures +3. Adjust properties (for example `GregorianDateTimeFormat`, `NumberFormat`, `CompareInfo`, `TextInfo`) +4. `Register()` on each execution server (or `Save()` to LDML XML and register from a separate tool using `CreateFromLdml`) + +Microsoft recommends building the culture on one machine, saving to XML, and registering from that file on other machines so definitions stay **uniform across the cluster**. + +### Platform support + +Custom cultures are **not** portable to every platform {{% ctx %}} might run on: + +* **Windows only** — `.nlp` files are not supported on non-Windows operating systems. +* **Globalization mode** — on .NET Core and later, registered `.nlp` cultures apply when the process uses **NLS** globalization mode, not necessarily under ICU-only configurations. + +Document which servers have which custom cultures registered, and verify behaviour in each environment (development, test, production, and every cluster node). + +## Obtaining a custom culture in flows + +After registration on the server, use the same APIs as for [Specific Cultures][]: + +| Approach | Expression | Notes | +| --- | --- | --- | +| Constructor | `new CultureInfo("x-en-US-sample")` | Throws [CultureInfoNotFoundException][] if the culture is not registered on that server | +| Cached instance | `CultureInfo.GetCultureInfo("x-en-US-sample")` | Read-only, cached instance | +| Discovery | `CultureInfo.GetCultures(CultureTypes.AllCultures)` | Includes registered custom cultures | + +Pass the value to `String.Format`, `DateTime.ToString`, `Convert.ToString`, or to block **Format Provider** / **Culture Info** properties. + +If the culture name matches a **replacement** culture (for example registered `en-GB`), `new CultureInfo("en-GB")` on that server uses the **custom** definition, not the original built-in patterns. On a server without that replacement, behaviour follows the standard `en-GB` install. That is why replacement cultures must be registered identically everywhere flows rely on them. + +## What a custom culture controls + +A registered custom culture exposes the same `CultureInfo` surface as other culture-sensitive cultures—`DateTimeFormatInfo`, `NumberFormatInfo`, `TextInfo`, `CompareInfo`, and calendars—using the values defined at registration time. + +| Area | Behaviour | More information | +| --- | --- | --- | +| Dates and times | Patterns and separators from the custom definition | [Date and Time Formatting][], [Operating System Settings][] | +| Numbers | Decimal, group, and currency formats from the custom definition | [Formatting][] | +| Text casing | Rules from the custom culture's `TextInfo` (can differ from built-in cultures with the same language) | [Casing][] | +| Text comparison | Sort and equivalence rules from the custom `CompareInfo` | [Equality][] | +| Composite formatting | Placeholders use the custom culture's number and date formats | [Format Text With Values][], [Format Text With Value][] | + +### Dates and times + +When you pass a custom `CultureInfo` to [Convert Date Time To Text][] or set it as **Format Provider**, standard format specifiers (`"d"`, `"G"`, and so on) use the **registered** patterns. Replacement cultures can change how a familiar name such as `en-GB` formats dates on that server without changing flow code. + +For strings produced under a custom culture, Microsoft recommends **parsing with explicit patterns**—`DateTime.ParseExact` or `DateTime.TryParseExact`—because custom date/time strings can be more complex than built-in cultures. See [Date and time parsing](#date-and-time-parsing) below. + +### Casing + +Custom cultures can define their own casing rules through `TextInfo`. Pass the registered culture to [Convert To Lowercase][] or [Convert To Uppercase][] when casing must follow that definition. Default **Culture Info** on those blocks remains [Invariant Culture][] unless you set it. + +### String comparison + +Comparisons that use a supplied `CultureInfo` or culture-based `StringComparison` follow the custom culture's sort rules. For identifiers, security checks, or protocol values, prefer [Invariant Culture][] or ordinal comparison. + +## When to use a custom culture + +Use a registered custom culture when: + +* **Organisation-defined locale** — legal, branding, or regional rules require formats or sort orders that built-in cultures do not provide. +* **Replacement culture on all servers** — every node must use the same overridden `en-GB` (or other) definition via `CultureAndRegionModifiers.Replacement`. +* **Supplemental culture** — you need an extra culture name (often with an `x-` prefix) without replacing a built-in tag. + +## When not to use a custom culture + +Avoid custom cultures when: + +* A **standard specific culture** is enough — prefer `new CultureInfo("en-GB")` and documented OS regional settings instead of maintaining `.nlp` files. +* **Culture-independent storage or APIs** — use [Invariant Culture][]. +* **Linux or mixed OS clusters** — custom `.nlp` cultures may be unavailable; use [Specific Cultures][] or [Invariant Culture][] instead. +* **Flows cannot assume registration** — `new CultureInfo("x-my-culture")` throws [CultureInfoNotFoundException][] on servers where the culture was never registered. +* **You need to register at runtime** — registration belongs in admin tooling, not in flow logic. + +## Using a custom culture in blocks + +Custom cultures are **opt-in**, like [Specific Cultures][]: + +| Scenario | Typical approach | +| --- | --- | +| Format dates, numbers, or composite text with org rules | Set **Format Provider** to `new CultureInfo("x-my-culture")` (or a variable holding that value) | +| Casing under custom rules | Set **Culture Info** on casing blocks to the registered culture | +| Compare or search text with custom sort rules | Set **Comparison Type** or culture-related properties per the block's remarks | +| Default when property omitted or `null` | Usually [Invariant Culture][] — check each block | + +Store the culture in a variable when the same custom locale is used in many blocks—for example `var org = new CultureInfo("x-en-US-sample");` in the [Expression Editor][]. + +## Date and time parsing + +Custom cultures can produce date and time strings that are harder to parse implicitly than built-in locales. When reading values formatted with a custom culture, prefer explicit parse patterns in expressions (for example `DateTime.ParseExact` with a format string that matches how the value was written) rather than `DateTime.Parse` alone. See Microsoft guidance in [CultureAndRegionInfoBuilder remarks][MS CultureAndRegionInfoBuilder]. + +## Clusters and deployment + +Flows that depend on a custom culture require: + +* The **same culture name** registered on **every** server that can execute the flow. +* **Identical** `.nlp` definitions (use save/register from a single LDML source where possible). +* Verification after OS upgrades or globalization updates, which can affect NLS behaviour. + +If one node lacks the registration, flows throw [CultureInfoNotFoundException][] or format data differently from other nodes. + +## Security and display + +The same guidance as for [Specific Cultures][] applies: + +* **Display to users** — use a culture-sensitive culture that matches the audience; a custom culture is appropriate when that audience is defined by your registered locale. +* **Internal or security use** — do not rely on culture-sensitive comparison or casing for security decisions unless the custom culture is guaranteed on every execution path; prefer [Invariant Culture][] or ordinal comparison for stable identifiers. + +Replacement cultures change behaviour for **all** applications on the server that use the replaced name (for example `en-GB`), not only {{% ctx %}}. Coordinate with infrastructure owners before registering replacements. + +## Custom culture vs other cultures + +| | Custom Culture | [Specific Culture][Specific Cultures] | [Current Culture][] | [Invariant Culture][] | +| --- | --- | --- | --- | --- | +| Source | Registered name on Windows | `new CultureInfo("en-GB")`, etc. | `CultureInfo.CurrentCulture` | `CultureInfo.InvariantCulture` | +| Defined in flow | No (name only) | Name only | N/A | N/A | +| Tied to server OS | Yes (registration per machine) | Culture must be installed/registered | Yes | No | +| Typical use | Org-specific or replacement locale | Known standard locale | Server default locale | Persistence, protocols, defaults | ## Remarks ### Known Limitations -TODO +* **Windows and NLS** — `CultureAndRegionInfoBuilder` and `.nlp` registration apply to Windows with supported globalization modes; do not assume custom cultures exist on Linux servers. +* **Administrative registration** — Flows cannot register or unregister cultures; missing registration causes [CultureInfoNotFoundException][]. +* **Replacement cultures** — Using the same name as a built-in culture (for example `en-GB`) affects every app on that server; behaviour differs from servers without the replacement. +* **Cluster consistency** — Each node must have the same custom culture definition; drift causes intermittent formatting or runtime errors. +* **sysglobl.dll** — Only registration tools need this assembly; {{% ctx %}} expressions use `CultureInfo` after registration. +* **Discovery** — `CultureInfo.GetCultures` lists custom cultures only on machines where they are registered; do not hard-code assumptions about names present in development but absent in production. ## See Also ### Related Concepts -TODO +* [What is a Culture?][] +* [Specific Cultures][] +* [Current Culture][] +* [Invariant Culture][] +* [Casing][] +* [Equality][] +* [Formatting][] +* [Date and Time Formatting][] ### Related Data Types -TODO +* [CultureInfo][] +* [IFormatProvider][] +* [StringComparison][] +* [StringComparer][] ### Related Blocks -TODO +* [Format Text With Values][] +* [Format Text With Value][] +* [Convert Object To Text][] +* [Convert Date Time To Text][] +* [Convert To Lowercase][] +* [Convert To Uppercase][] +* [Contains Any Text][] +* [Find And Replace Text][] ### External Documentation -TODO +* [CultureAndRegionInfoBuilder class](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureandregioninfobuilder) +* [CultureAndRegionInfoBuilder remarks (define, register, instantiate)](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-globalization-cultureandregioninfobuilder) +* [CultureAndRegionInfoBuilder.Register method](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureandregioninfobuilder.register) +* [CultureInfo class](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo) +* [CultureInfo.GetCultures method](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.getcultures) +* [Best practices for comparing strings in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings) +* [Performing culture-insensitive string operations](https://learn.microsoft.com/en-us/dotnet/core/extensions/performing-culture-insensitive-string-operations) +* [Supported Culture Codes][] + +[What is a Culture?]: {{< ref "what-is-a-culture.md" >}} +[Invariant Culture]: {{< ref "invariant-culture.md" >}} +[Current Culture]: {{< ref "current-culture.md" >}} +[Specific Cultures]: {{< ref "specific-cultures.md" >}} + +[Casing]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Casing.MainDoc" >}} +[Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Equality.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} + +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[StringComparison]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparison.MainDoc" >}} +[StringComparer]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparer.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} + +[Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} +[Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Convert To Lowercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToLowerCase.MainDoc" >}} +[Convert To Uppercase]: {{< url path="Cortex.Reference.Blocks.Text.ConvertTo.ConvertToUpperCase.MainDoc" >}} +[Contains Any Text]: {{< url path="Cortex.Reference.Blocks.Text.ContainsText.ContainsAnyText.MainDoc" >}} +[Find And Replace Text]: {{< url path="Cortex.Reference.Blocks.Text.FindAndReplaceText.FindAndReplaceText.MainDoc" >}} + +[Supported Culture Codes]: {{< url path="MSDocs.CSharp.SupportedCultureCodes" >}} +[MS CultureAndRegionInfoBuilder]: {{< url path="MSDocs.DotNet.Fundamentals.RuntimeLibraries.CultureAndRegionInfoBuilder" >}} diff --git a/data/urls.toml b/data/urls.toml index 9ac920aaf..7d7c5adc4 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -3057,6 +3057,10 @@ [MSDocs.DotNet.Api.System.Net] NetworkCredential = "https://learn.microsoft.com/en-us/dotnet/api/system.net.networkcredential" HttpStatusCode = "https://learn.microsoft.com/en-us/dotnet/api/system.net.httpstatuscode" + [MSDocs.DotNet.Fundamentals] + [MSDocs.DotNet.Fundamentals.RuntimeLibraries] + CultureAndRegionInfoBuilder = "https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-globalization-cultureandregioninfobuilder" + [MSDocs.PowerShell] WhatIsPowerShell = "https://learn.microsoft.com/en-us/powershell/scripting/overview?view=powershell-5.1" InstallModule = "https://learn.microsoft.com/en-us/powershell/scripting/developer/module/installing-a-powershell-module?view=powershell-7.2" From cb0a6ce82ced46ba8a1cd942bbaac15e25a8add7 Mon Sep 17 00:00:00 2001 From: Donna-Marie Smith Date: Tue, 9 Jun 2026 15:54:08 +0100 Subject: [PATCH 11/11] changes following PO Review --- .../Concepts/working-with/culture/current-culture.md | 2 +- .../Concepts/working-with/culture/custom-cultures.md | 2 -- .../Concepts/working-with/culture/specific-cultures.md | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md index ffd638cdc..26a64f0bc 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/current-culture.md @@ -107,7 +107,7 @@ When output must match a **known user or business locale**, create and pass an e ## Clusters and operating system settings -Flows that depend on `CultureInfo.CurrentCulture` inherit whatever culture .NET assigns to the executing thread. On Windows that is typically the system default locale; on Linux it follows ICU / `LC_MESSAGES` behaviour as described in .NET documentation. +Flows that depend on `CultureInfo.CurrentCulture` inherit whatever culture .NET assigns to the executing thread; on Windows that is typically the system default locale. For predictable results in a **cluster**: diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md index 754120b73..9fb8882b9 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/custom-cultures.md @@ -123,7 +123,6 @@ Avoid custom cultures when: * A **standard specific culture** is enough — prefer `new CultureInfo("en-GB")` and documented OS regional settings instead of maintaining `.nlp` files. * **Culture-independent storage or APIs** — use [Invariant Culture][]. -* **Linux or mixed OS clusters** — custom `.nlp` cultures may be unavailable; use [Specific Cultures][] or [Invariant Culture][] instead. * **Flows cannot assume registration** — `new CultureInfo("x-my-culture")` throws [CultureInfoNotFoundException][] on servers where the culture was never registered. * **You need to register at runtime** — registration belongs in admin tooling, not in flow logic. @@ -176,7 +175,6 @@ Replacement cultures change behaviour for **all** applications on the server tha ### Known Limitations -* **Windows and NLS** — `CultureAndRegionInfoBuilder` and `.nlp` registration apply to Windows with supported globalization modes; do not assume custom cultures exist on Linux servers. * **Administrative registration** — Flows cannot register or unregister cultures; missing registration causes [CultureInfoNotFoundException][]. * **Replacement cultures** — Using the same name as a built-in culture (for example `en-GB`) affects every app on that server; behaviour differs from servers without the replacement. * **Cluster consistency** — Each node must have the same custom culture definition; drift causes intermittent formatting or runtime errors. diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md index e1a305a4e..08bca120c 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/culture/specific-cultures.md @@ -116,7 +116,7 @@ The specific culture is **opt-in** in most blocks: | Compare or search text using a fixed sort order | Set **Comparison Type** or culture-related properties per the block's remarks | | Default when property omitted or `null` | Usually [Invariant Culture][] — check each block | -Store the culture in a variable when the same locale is used in many blocks—for example `var uk = new CultureInfo("en-GB");` in the [Expression Editor][]. +Store the culture in a variable when the same locale is used in many blocks. ## User overrides (Windows) @@ -222,8 +222,6 @@ Microsoft guidance for .NET applies to {{% ctx %}} flows: [StringComparer]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparer.MainDoc" >}} [CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} -[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} - [Format Text With Values]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValues.MainDoc" >}} [Format Text With Value]: {{< url path="Cortex.Reference.Blocks.Text.FormatText.FormatTextWithValue.MainDoc" >}} [Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}}