@spree/docs 0.1.181 → 0.1.183

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/api-reference/seller-api/errors.md +1 -1
  2. package/dist/api-reference/store.yaml +78 -91
  3. package/dist/developer/core-concepts/addresses.md +106 -198
  4. package/dist/developer/core-concepts/architecture.md +97 -126
  5. package/dist/developer/core-concepts/calculators.md +75 -252
  6. package/dist/developer/core-concepts/carts.md +1 -1
  7. package/dist/developer/core-concepts/channels.md +0 -4
  8. package/dist/developer/core-concepts/companies-and-catalogs.md +1 -1
  9. package/dist/developer/core-concepts/customers.md +0 -3
  10. package/dist/developer/core-concepts/discounts.md +133 -0
  11. package/dist/developer/core-concepts/events.md +83 -576
  12. package/dist/developer/core-concepts/fees.md +144 -0
  13. package/dist/developer/core-concepts/imports-exports.md +105 -679
  14. package/dist/developer/core-concepts/inventory.md +114 -248
  15. package/dist/developer/core-concepts/markets.md +9 -12
  16. package/dist/developer/core-concepts/media.md +9 -11
  17. package/dist/developer/core-concepts/metafields.md +123 -200
  18. package/dist/developer/core-concepts/order-totals.md +110 -0
  19. package/dist/developer/core-concepts/orders.md +1 -1
  20. package/dist/developer/core-concepts/payments.md +11 -14
  21. package/dist/developer/core-concepts/pricing.md +11 -13
  22. package/dist/developer/core-concepts/products.md +173 -19
  23. package/dist/developer/core-concepts/promotions.md +12 -11
  24. package/dist/developer/core-concepts/search-filtering.md +2 -4
  25. package/dist/developer/core-concepts/store-credits-gift-cards.md +0 -3
  26. package/dist/developer/core-concepts/taxes.md +125 -113
  27. package/dist/developer/core-concepts/translations.md +61 -68
  28. package/dist/developer/core-concepts/webhooks.md +25 -59
  29. package/dist/developer/how-to/custom-promotion.md +3 -3
  30. package/package.json +1 -1
  31. package/dist/developer/core-concepts/taxes-discounts-fees.md +0 -199
@@ -1,163 +1,175 @@
1
1
  ---
2
2
  title: Taxes
3
- description: Tax categories, tax rates, zones, and how Spree calculates taxes at checkout
3
+ description: How Spree works out tax — tax categories and rates, inclusive versus added-on pricing, and connecting an external tax service.
4
4
  ---
5
5
 
6
6
  ## Overview
7
7
 
8
- Spree uses Tax Categories and Tax Rates to calculate taxes for orders. Products are assigned to tax categories, and tax rates define the percentage charged within specific geographic [Zones](addresses.md#zones).
8
+ Tax is the part of commerce most likely to differ from what you assumed. A US shopper expects `$17.99` on the shelf and a little more at the till. A German shopper expects `€17.99` to be the whole story, VAT already inside it. Both are correct, and a store selling to both has to do both.
9
+
10
+ Spree handles this by keeping three things separate:
11
+
12
+ - **What is being sold** — a [tax category](#tax-categories) groups products taxed the same way
13
+ - **Where the customer is** — their [market](markets.md) and shipping address
14
+ - **Who works out the number** — Spree's own rates, or an external tax service
15
+
16
+ The result of all this is a set of **tax lines** on the order — one per charge, recording what was taxed and at what rate. This page is about how they get their numbers.
9
17
 
10
18
  ```mermaid
11
- erDiagram
12
- TaxCategory ||--o{ TaxRate : "has many"
13
- TaxCategory ||--o{ Product : "has many"
14
- TaxRate }o--|| Zone : "applies in"
15
- TaxRate ||--o{ Adjustment : "creates"
16
- Zone ||--o{ ZoneMember : "has many"
17
-
18
- TaxCategory {
19
- string name
20
- boolean is_default
21
- string tax_code
22
- }
23
-
24
- TaxRate {
25
- string name
26
- decimal amount
27
- boolean included_in_price
28
- }
29
-
30
- Zone {
31
- string name
32
- string kind
33
- }
19
+ flowchart LR
20
+ Product -->|"has a"| TaxCategory
21
+ Address -->|"determines"| Market
22
+ Market -->|"selects"| Provider["Tax provider"]
23
+ TaxCategory --> Provider
24
+ Provider -->|"writes"| TaxLine["Tax lines on the order"]
34
25
  ```
35
26
 
36
- **Key relationships:**
37
- - **Tax Category** groups products for tax purposes (e.g., "Clothing", "Food", "Digital")
38
- - **Tax Rate** defines the percentage and rules for a Tax Category within a [Zone](addresses.md#zones)
39
- - **Zone** defines geographic regions (countries or states) where taxes apply
40
- - **[Adjustments](taxes-discounts-fees.md)** are created on orders/line items to apply taxes
27
+ ## Tax categories
41
28
 
42
- ## Tax Categories
29
+ A tax category is how you say "these things are taxed alike". Children's clothing, books and hot food are treated differently from general goods in many countries, and a tax category is where that distinction lives.
43
30
 
44
- Tax categories group products by how they're taxed. Common examples:
31
+ Every product carries one. If you don't choose, the default is used.
45
32
 
46
- - **Clothing** standard tax rate
47
- - **Food** — reduced or exempt rate
48
- - **Digital** different rules per jurisdiction
33
+ | Field | Description | Example |
34
+ |---|---|---|
35
+ | `name` | What it's called | `Standard`, `Reduced`, `Digital` |
36
+ | `is_default` | Used by products that don't pick one | `true` |
37
+ | `tax_code` | The code your tax service knows it by | `1257L` |
49
38
 
50
- Each product is assigned a tax category. One category can be set as the default for products that don't have an explicit assignment.
39
+ ```typescript Admin SDK
40
+ const reduced = await adminClient.taxCategories.create({
41
+ name: 'Reduced rate',
42
+ tax_code: '1257L',
43
+ })
44
+ ```
51
45
 
52
- | Attribute | Description | Example |
53
- |-----------|-------------|---------|
54
- | `name` | Category name | `Clothing` |
55
- | `is_default` | Whether this is the default category | `true` |
56
- | `tax_code` | Code from your tax provider (e.g., Stripe, Avalara) | `1257L` |
46
+ `tax_code` matters once you connect an external service — it is how you tell that service what kind of thing this is, in the vocabulary it already uses.
57
47
 
58
- Manage tax categories via the [Admin API](../../api-reference/admin-api/introduction.md), using the [Admin SDK resource clients](../sdk/admin/resources.md):
48
+ ## Tax rates
59
49
 
50
+ A tax rate is the number itself, plus where it applies and whether it's already in the price.
60
51
 
61
- ```typescript Admin SDK
62
- import { createAdminClient } from '@spree/admin-sdk'
52
+ | Field | Description | Example |
53
+ |---|---|---|
54
+ | `name` | What appears on the invoice | `VAT 20%` |
55
+ | `amount` | The rate as a decimal | `0.2` for 20% |
56
+ | `included_in_price` | Whether the price already contains it | `true` in the EU |
57
+ | `country_code` / `state_code` | Where it applies | `DE`, or `US` + `CA` |
58
+ | `tax_category` | What it applies to | `Standard` |
63
59
 
64
- const client = createAdminClient({
65
- baseUrl: 'https://store.example.com',
66
- secretKey: 'sk_xxx',
60
+ ```typescript Admin SDK
61
+ const { data: rates } = await adminClient.taxRates.list()
62
+
63
+ await adminClient.taxRates.create({
64
+ name: 'VAT 20%',
65
+ amount: '0.2',
66
+ included_in_price: true,
67
+ country_code: 'GB',
68
+ tax_category_id: 'taxcat_xxx',
67
69
  })
70
+ ```
68
71
 
69
- const clothing = await client.taxCategories.create({
70
- name: 'Clothing',
71
- tax_code: '1257L',
72
- is_default: true,
73
- })
72
+ Rates name their country and state by **ISO code**, not by an ID — `DE`, `US` + `CA`. There's no separate region record to create first.
74
73
 
75
- await client.taxCategories.update('taxcat_xxx', { is_default: false })
76
- await client.taxCategories.delete('taxcat_xxx')
77
- ```
74
+ ## Included or added on
78
75
 
79
- ```bash CLI
80
- spree api post /tax_categories -d '{"name": "Clothing", "tax_code": "1257L", "is_default": true}'
81
- spree api get /tax_categories
82
- ```
76
+ `included_in_price` is the single most consequential setting here, so it's worth being precise about what it does.
83
77
 
78
+ **Added on (US style):**
84
79
 
85
- ## Tax Rates
80
+ The shelf price is what the product costs. Tax is worked out at checkout, once the address is known, and added.
86
81
 
87
- Tax rates define the percentage charged for a specific tax category within a geographic zone.
82
+ A $17.99 item at 5%:
88
83
 
89
- | Attribute | Description | Example |
90
- |-----------|-------------|---------|
91
- | `name` | Rate name | `California Sales Tax` |
92
- | `amount` | Tax percentage as a decimal | `0.08` (8%) |
93
- | `included_in_price` | Whether tax is included in the displayed price | `false` |
94
- | `zone` | Geographic region where this rate applies | `US` |
95
- | `tax_category` | Products this rate applies to | `Clothing` |
84
+ | | |
85
+ |---|---|
86
+ | Price shown | **$17.99** |
87
+ | Tax added | $0.90 |
88
+ | Customer pays | **$18.89** |
96
89
 
97
- ## Tax Types
90
+ Set `included_in_price: false`.
98
91
 
99
- ### Sales Tax (tax-exclusive)
92
+ **Included (EU style):**
100
93
 
101
- Common in the United States. The displayed price does **not** include taxtax is added at checkout based on the shipping address.
94
+ The shelf price is the whole price. The tax inside it is shown for information the customer pays the number they saw.
102
95
 
103
- **Example:** A $17.99 item with 5% sales tax:
104
- - Displayed price: **$17.99**
105
- - Tax at checkout: **$0.90**
106
- - Order total: **$18.89**
96
+ A £17.99 item at 20% VAT:
107
97
 
108
- ### Value Added Tax / VAT (tax-inclusive)
98
+ | | |
99
+ |---|---|
100
+ | Price shown | **£17.99** |
101
+ | VAT inside it | £3.00 |
102
+ | Customer pays | **£17.99** |
109
103
 
110
- Common in Europe and many other countries. The displayed price **already includes** tax. When shipping outside the tax zone, the tax is removed from the price.
104
+ Set `included_in_price: true`.
111
105
 
112
- **Example:** A £17.99 item with 5% VAT included:
113
- - Displayed price: **£17.99** (includes £0.86 VAT)
114
- - If shipped outside VAT zone: price reduces to **£17.13**
115
106
 
116
- > **INFO:** The `tax_inclusive` setting on [Markets](markets.md) controls whether prices are displayed with or without tax for each geographic region.
107
+ > **WARNING:** When building an order summary, add `additional_tax_total` only. `included_tax_total` is **already inside** the item prices adding it counts the tax twice. This is the most common bug in a European storefront.
117
108
 
118
- ## Default Tax Zone
109
+ Because prices are shown before anyone knows where the shopper lives, each [Market](markets.md) declares whether its prices are quoted with tax included. That's what lets a catalogue page show honest prices to a German visitor and a US visitor at the same time.
119
110
 
120
- Spree uses a default tax zone to estimate taxes before the customer enters a shipping address. This is important for stores with tax-inclusive pricing (VAT) — it determines which tax rate is assumed in the displayed price.
111
+ ## Tax lines
121
112
 
122
- If the customer's shipping address is outside the default tax zone, the assumed tax is removed and the correct rate for their zone is applied.
113
+ Whatever works out the tax, the record is the same: a tax line per charge, saying what was taxed and how.
123
114
 
124
- ## Tax Calculation at Checkout
115
+ | Attribute | Description |
116
+ |---|---|
117
+ | `label` | What the customer sees — `VAT 20%` |
118
+ | `rate` | The rate applied — `"0.2"` |
119
+ | `included` | Whether the tax was already inside the displayed price |
120
+ | `amount` / `display_amount` | The tax charged |
121
+ | `line_item_id` / `fulfillment_id` / `fee_id` | What was taxed — exactly one is set |
125
122
 
126
- During checkout, taxes are calculated based on the shipping address:
123
+ ```typescript Admin SDK
124
+ const { data: taxLines } = await adminClient.orders.taxLines.list('or_xxx')
125
+ ```
127
126
 
128
- 1. The customer's shipping address determines their [Zone](addresses.md#zones)
129
- 2. Spree finds matching tax rates for that zone and the product's tax category
130
- 3. Tax [Adjustments](taxes-discounts-fees.md) are created on line items
131
- 4. The order total is updated
127
+ Tax lines attach to a line item, a [fulfillment](fulfillments.md), or a [fee](fees.md) — so tax on goods and tax on delivery stay separately visible, which is what a tax return needs.
132
128
 
133
- > **NOTE:** For complex tax requirements (interstate US sales, international VAT), consider using automated tax calculation. The [Stripe Tax integration](../../integrations/payments/stripe.md) handles this automatically.
129
+ Each line also keeps its own copy of the rate and label. If someone edits that tax rate next year, the order still says what the customer was actually charged.
134
130
 
135
- ## Managing Taxes
131
+ You never create tax lines yourself; they're written whenever the total is worked out.
132
+
133
+ ## When tax is worked out
134
+
135
+ Tax is recalculated whenever the answer could have changed — an item added, an address entered, a delivery option chosen. Every one of those requests returns the updated cart, so a storefront never needs a separate "refresh the tax" call.
136
+
137
+ **Step 1: Before an address is known**
138
+
139
+ Prices are shown using the market's tax treatment. For an inclusive market the displayed price is the real one; for an added-on market tax simply hasn't been worked out yet.
140
+
141
+ **Step 2: Once the shipping address is entered**
142
+
143
+ The address decides the real rate. Tax lines are written against each item, the delivery charge, and any taxable fee.
144
+
145
+ **Step 3: At completion**
146
+
147
+ Whatever the tax is at that moment is what's charged, and the order keeps it. A rate change next month does not rewrite an order from last month.
148
+
149
+
150
+ ## Connecting a tax service
151
+
152
+ Spree's built-in rate tables are fine when the rules are simple — one country, or a handful of clearly-defined rates. They become a liability where the rules are not: US sales tax varies by city and changes constantly, and cross-border EU VAT has thresholds that shift with your turnover.
153
+
154
+ For those, connect a real tax service. **The provider is chosen per [Market](markets.md)**, which means one store can use its own rates in a simple market and an external service in a complicated one.
155
+
156
+ ```typescript Admin SDK
157
+ const { data: providers } = await adminClient.taxProviders.list()
158
+ ```
136
159
 
137
- Tax categories and rates are managed in the Admin Panel under **Settings Taxes**, or via the Admin API.
160
+ A provider declares up front what it cannot do no US local tax, no reverse charge — so the dashboard can warn a merchant who pairs it with a market that needs it, rather than quietly under-collecting.
138
161
 
139
- ### Common Configurations
162
+ Whichever provider is in use, the result is the same: tax lines on the order, in the same shape. Your storefront code doesn't change.
140
163
 
141
- **US store with state-level sales tax:**
142
- - Create zones for each taxable state
143
- - Create tax rates per zone (e.g., 8.25% for California, 6.25% for Texas)
144
- - Set `included_in_price: false`
164
+ ## Tax-exempt customers
145
165
 
146
- **EU store with VAT:**
147
- - Create a zone for EU countries
148
- - Create tax rates per country (e.g., 19% Germany, 20% France)
149
- - Set `included_in_price: true`
150
- - Set the EU zone as the default tax zone
166
+ Business customers are often exempt, and the paperwork is real. Spree records a customer's or company's **tax identifier** a VAT number, an ABN — and can validate it. Companies can also hold exemption certificates, scoped to the country or state that issued them.
151
167
 
152
- **Digital products in the EU:**
153
- - Create a "Digital" tax category
154
- - Add tax rates for each EU country using the customer's country VAT rate
155
- - Physical products use the seller's country VAT rate
168
+ Exemption is decided when tax is worked out, not stored as a flag on the customer, so an expired certificate stops applying by itself. See [Companies & Catalogs](companies-and-catalogs.md).
156
169
 
157
- ## Related Documentation
170
+ ## Related
158
171
 
159
- - [Markets](markets.md) — Tax-inclusive pricing per market
160
- - [Addresses](addresses.md) — Zones and address-based taxation
161
- - [Adjustments](taxes-discounts-fees.md) — Tax adjustments
162
- - [Orders](orders.md) — How taxes affect order totals
163
- - [Avalara integration](../../integrations/tax/avalara.md) — Automated tax calculation for complex jurisdictions
172
+ - [Order totals](order-totals.md) — how tax rolls into what a customer pays
173
+ - [Markets](markets.md) — where tax treatment and provider are chosen
174
+ - [Addresses](addresses.md) — what determines the rate
175
+ - [Companies & Catalogs](companies-and-catalogs.md) — B2B tax identifiers and exemptions
@@ -1,134 +1,127 @@
1
1
  ---
2
2
  title: Translations
3
- description: Translate Spree products, taxons, and other store content into multiple languages using Mobility-backed translations and locale-aware APIs.
3
+ description: Selling in more than one language translating product content, and translating the interface around it.
4
4
  ---
5
5
 
6
6
  ## Overview
7
7
 
8
- Spree supports two types of translations:
8
+ Two different things need translating, and they're handled separately because they belong to different people.
9
9
 
10
- 1. **Resource Translations** — translatable content fields on models like Products, Taxons, and Stores (e.g., product name, description, slug)
11
- 2. **UI Translations** — interface strings used in the admin panel (e.g., button labels, flash messages)
10
+ **Your content** — product names, descriptions, category names is written by whoever runs the store. Spree stores a version of each field per language.
12
11
 
13
- For configuring which locales and currencies are available in your store, see [Markets](markets.md). Markets control locale and currency assignment per geographic region.
12
+ **The interface** button labels, error messages, email wording is written by Spree, and is already translated into dozens of languages by the community.
14
13
 
15
- ## Resource Translations
14
+ > **INFO:** Which languages a region gets is decided by its [Market](markets.md). A market sets the default locale alongside the currency, so a visitor from France lands on French prices and French copy without choosing anything.
16
15
 
17
- Resources with user-facing content fields have built-in support for translations. Each translatable resource has a corresponding translations table in the database — for example, product translations are stored in `spree_product_translations`.
16
+ ## Translating content
17
+
18
+ Every translatable record keeps one row per language, so a product genuinely has a French name rather than a French name pasted over the English one.
18
19
 
19
20
  ```mermaid
20
21
  erDiagram
21
- Product ||--o{ ProductTranslation : "has many"
22
- Taxon ||--o{ TaxonTranslation : "has many"
23
- Store ||--o{ StoreTranslation : "has many"
22
+ Product ||--o{ ProductTranslation : "one per locale"
24
23
 
25
24
  Product {
26
- string id PK
27
25
  string status
26
+ string sku
28
27
  }
29
-
30
28
  ProductTranslation {
31
- string id PK
32
- string product_id FK
33
29
  string locale
34
30
  string name
35
- text description
31
+ string description
36
32
  string slug
37
33
  string meta_title
38
- string meta_description
39
- }
40
-
41
- TaxonTranslation {
42
- string id PK
43
- string taxon_id FK
44
- string locale
45
- string name
46
- text description
47
- string permalink
48
34
  }
49
35
  ```
50
36
 
51
- ### Translatable Fields
37
+ ### What can be translated
52
38
 
53
- | Resource | Translatable Fields |
54
- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
- | Product | `name`, `description`, `slug`, `meta_description`, `meta_title` |
56
- | Taxon | `name`, `description`, `permalink` |
57
- | Taxonomy | `name` |
58
- | Option Type | `presentation` |
59
- | Option Value | `presentation` |
60
- | Property | `presentation` |
61
- | Product Property | `value` |
62
- | Store | `name`, `meta_description`, `meta_keywords`, `seo_title`, `customer_support_email`, `address`, `contact_phone` |
39
+ | Resource | Fields |
40
+ |---|---|
41
+ | Product | `name`, `description`, `slug`, `meta_title`, `meta_description` |
42
+ | Category | `name`, `description`, `permalink` |
43
+ | Collection | `name`, `description`, `permalink` |
44
+ | Product type | `name` |
45
+ | Option type | `presentation` |
46
+ | Option value | `presentation` |
47
+ | Store | `name`, `seo_title`, `meta_description`, `meta_keywords`, and support contact details |
48
+ | Policy | `name`, `body` |
49
+ | Seller | `name`, `about` |
63
50
 
64
- ### Store API
51
+ Note that **slugs are translated too**. A French page can live at `/produits/sac-spree` rather than at the English slug with French words on it — which is what search engines actually reward. See [Slugs](slugs.md#internationalization).
65
52
 
66
- To retrieve translated content, pass the locale via the [`X-Spree-Locale` header](../../api-reference/store-api/localization.md) or the SDK `locale` option:
53
+ ## Reading translated content
54
+
55
+ Ask for a locale and you get that language back. Nothing else about the request changes:
67
56
 
68
57
 
69
58
  ```typescript Store SDK
70
- // Fetch product in French
71
- const product = await client.products.get('spree-tote', {}, {
72
- locale: 'fr',
73
- })
59
+ const product = await client.products.get('spree-tote', {}, { locale: 'fr' })
74
60
 
75
61
  product.name // "Sac Spree"
76
- product.description // "Un sac fourre-tout élégant..."
62
+ product.description // "Un sac fourre-tout élégant"
77
63
  product.slug // "sac-spree"
78
64
 
79
- // List categories in German
80
- const { data: categories } = await client.categories.list({}, {
81
- locale: 'de',
82
- })
65
+ const { data: categories } = await client.categories.list({}, { locale: 'de' })
83
66
  ```
84
67
 
85
68
  ```typescript Admin SDK
86
- // Pass the locale as a request option to get translated content.
87
- // The Admin API resolves by prefixed ID only — slugs are not accepted.
69
+ // The Admin API resolves by ID only slugs are not accepted here
88
70
  const product = await adminClient.products.get('prod_86Rf07xd4z', {}, { locale: 'fr' })
89
-
90
- const { data: categories } = await adminClient.categories.list({}, { locale: 'de' })
91
71
  ```
92
72
 
93
73
  ```bash cURL
94
- # Fetch product in French
95
74
  curl 'https://api.mystore.com/api/v3/store/products/spree-tote' \
96
75
  -H 'X-Spree-API-Key: pk_xxx' \
97
76
  -H 'X-Spree-Locale: fr'
98
77
  ```
99
78
 
100
79
 
101
- Slugs are also localized a product can have different slugs per locale. See [Slugs](slugs.md#internationalization) for details.
80
+ If a field has no translation for the requested locale, you get the default-language value rather than an empty string — a half-translated catalogue still renders as a usable page.
81
+
82
+ > **NOTE:** Responses vary by locale, so if you cache them, include the locale in the cache key. Spree sets `Vary` headers for you, which is enough for a CDN but not for a hand-rolled in-memory cache.
83
+
84
+ ## Writing translations
102
85
 
103
- ### Managing Translations
86
+ Merchants translate in the dashboard, switching locale on the record they're editing. For bulk work — handing a catalogue to a translation agency — export to CSV, translate, and import it back.
104
87
 
105
- Translations are managed in the Admin Panel. When editing a product, taxon, or other translatable resource, switch the locale selector to enter content in each language.
88
+ Translations can also be written through the Admin API, which is what you'd use to sync from an external translation service:
106
89
 
107
- Translations can also be [managed via the Admin API](../sdk/admin/quickstart.md).
90
+ ```typescript Admin SDK
91
+ await adminClient.products.update('prod_xxx', {
92
+ translations: {
93
+ fr: { name: 'Sac Spree', description: 'Un sac fourre-tout élégant…' },
94
+ de: { name: 'Spree Tasche' },
95
+ },
96
+ })
97
+
98
+ // Which resources and fields accept translations
99
+ const { data: resources } = await adminClient.translatableResources.list()
100
+ ```
108
101
 
109
- ## UI Translations
102
+ Asking the API which resources are translatable means a translation tool doesn't need a hardcoded list that goes stale.
110
103
 
111
- Spree stores UI translation strings in a separate project: [Spree I18n](https://github.com/spree-contrib/spree_i18n). This is a community-maintained project with locale files for 43+ languages.
104
+ ## Translating the interface
112
105
 
113
- To install UI translations:
106
+ Spree's own strings — everything in the dashboard and in transactional emails — come from a community-maintained project covering 40+ languages.
114
107
 
115
108
 
116
- ```bash Spree CLI (Docker)
109
+ ```bash Spree CLI
117
110
  spree bundle add spree_i18n
118
111
  ```
119
112
 
120
- ```bash Without Spree CLI
113
+ ```bash Bundler
121
114
  bundle add spree_i18n
122
115
  ```
123
116
 
124
117
 
125
- Once installed, all translation files are available automatically no need to copy any files.
118
+ That's the whole installation. Locales are picked up automatically; nothing needs copying into your app.
126
119
 
127
- > **INFO:** The full list of supported locales is available in the [Spree I18n GitHub repository](https://github.com/spree-contrib/spree_i18n/tree/main/config/locales).
120
+ > **INFO:** See the [supported locales](https://github.com/spree-contrib/spree_i18n/tree/main/config/locales) in the Spree I18n repository. Contributions are welcome if yours is incomplete.
128
121
 
129
- ## Related Documentation
122
+ ## Related
130
123
 
131
- - [Markets](markets.md) — Locale and currency configuration per geographic region
132
- - [Localization](../../api-reference/store-api/localization.md) — Locale, currency, and country headers in API requests
133
- - [Slugs](slugs.md) — Localized slugs and URL identifiers
134
- - [Products](products.md) — Product translations
124
+ - [Markets](markets.md) — which locale and currency a region gets
125
+ - [Slugs](slugs.md) — localized URLs
126
+ - [Custom Fields](metafields.md) — translating your own fields
127
+ - [Localization](../../api-reference/store-api/localization.md) — the locale, currency and country headers