Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/guide/layout-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,7 @@ The layout template and any page, index page, or show page template could use th
The `beforeMain`, `main`, and `afterMain` blocks are inside the section that Apostrophe refreshes regularly during content editing. Any `script` tags inside those blocks will run an indeterminate number of times during editing. Be especially careful when using event handlers. As a reminder, any widget-related JavaScript belongs in a [widget player](/guide/custom-widgets.md#client-side-javascript-for-widgets).

`layout.html` is a naming convention in Apostrophe, but is not a required file name. You can name it anything you like. Just remember to extend `data.outerLayout` and update page templates to extend it by its new name.

**RTL language support:** The `outerLayout` template automatically applies the correct text direction (`dir` attribute) to the `<html>` element based on your locale configuration. See the [localization guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for more information.

:::
138 changes: 128 additions & 10 deletions docs/guide/localization/dynamic.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,39 @@ The Apostrophe user interface has a locale chooser for editors, but visitors to

`data.locale` is simple. It will render as the locale code for the active locale. So if you're in the English (`'en'`) locale, it will return `'en'`.

### `data.i18n.direction`

The `data.i18n` object includes a `direction` property that indicates the text direction of the current locale. This will be either `'ltr'` (left-to-right) or `'rtl'` (right-to-left), based on the locale's configuration.

Apostrophe's default layouts automatically apply this to the `<html>` element. If you're using a custom layout, you can access it like this:

<AposCodeBlock>

``` nunjucks
{# In a page template or widget #}
<div class="my-component" dir="{{ data.i18n.direction }}">
{# Component content will flow in the correct direction #}
</div>
```
<template v-slot:caption>
views/widget.html
</template>
</AposCodeBlock>

You can also use it for conditional logic:

<AposCodeBlock>

``` nunjucks
<div class="hero {% if data.i18n.direction == 'rtl' %}hero--rtl{% endif %}">
{# Hero content #}
</div>
```
<template v-slot:caption>
views/layout.html
</template>
</AposCodeBlock>

### `data.localizations`

This property provides much more information. It will be an array of objects that include information about the current page context (or piece, when on a show page) in all the active locales. It will include special properties that can help build a locale switcher for visitors:
Expand All @@ -118,6 +151,7 @@ This property provides much more information. It will be an array of objects tha
| ------- | ------- |
| `locale` | The locale code (e.g., `'en'`, `'es'`) |
| `label` | The configured label for the locale (e.g., "English", "Spanish") |
| `direction` | The text direction for the locale: `'ltr'` or `'rtl'` |
| `available` | A boolean value indicating whether the page has been added to this locale |
| `current` | A boolean value indicating whether this is the currently displayed locale |
| `_url` | An API route that will return the page's URL in the locale (if it exists there) |
Expand All @@ -140,6 +174,7 @@ Here is an example of `data.localizations` for the page from the screenshots abo
_url: '/api/v1/@apostrophecms/page/cksqi1ye1000mof3rdgtmvn0y:es:draft/locale/en',
locale: 'en',
label: 'English',
direction: 'ltr',
homePageUrl: 'http://localhost:3000/'
},
{
Expand All @@ -157,20 +192,20 @@ Here is an example of `data.localizations` for the page from the screenshots abo
homePageUrl: 'http://localhost:3000/fr/'
},
{
_id: 'cksqi1ye1000mof3rdgtmvn0y:es:draft',
title: 'Página localizada 🇪🇸',
_id: 'cksqi1ye1000mof3rdgtmvn0y:he:draft',
title: 'עמוד מקומי 🇮🇱',
slug: '/localized-page',
type: 'default-page',
visibility: 'public',
aposLocale: 'es:draft',
aposLocale: 'he:draft',
aposMode: 'draft',
available: true,
_url: '/api/v1/@apostrophecms/page/cksqi1ye1000mof3rdgtmvn0y:es:draft/locale/es',
current: true,
locale: 'es',
label: 'Spanish',
homePageUrl: 'http://localhost:3000/es/'
}
_url: '/api/v1/@apostrophecms/page/cksqi1ye1000mof3rdgtmvn0y:es:draft/locale/he',
locale: 'he',
label: 'Hebrew',
direction: 'rtl',
homePageUrl: 'http://localhost:3000/he/'
},
]
```

Expand All @@ -193,7 +228,7 @@ Here is an example of using the `data.localizations` array to generate a locale
not the current locale
#}
{% if localization._url and not localization.current %}
<a href="{{ localization._url or localization.homePageUrl }}">
<a href="{{ localization._url or localization.homePageUrl }}" dir="{{ localization.direction }}">
{% endif %}
{# Using both the label and the locale code #}
{{ localization.label }} ({{ localization.locale }})
Expand All @@ -205,3 +240,86 @@ Here is an example of using the `data.localizations` array to generate a locale
</ul>
</div>
```

### RTL support in external frontends

When using external frontends like Astro with the `@apostrophecms/apostrophe-astro` integration, RTL support is automatically handled by the `AposLayout` component.

The locale direction is also available through the `aposData` prop if you need it for custom layouts or components:

The most common approach is to set the direction on your page layout, typically on the `<html>` element:

<AposCodeBlock>

```astro
---
// src/layouts/Layout.astro
const { aposData } = Astro.props;
---

<html lang={aposData.locale} dir={aposData.i18n.direction}>
<head>
<!-- head content -->
</head>
<body>
<slot />
</body>
</html>
```
<template v-slot:caption>
src/layouts/Layout.astro
</template>
</AposCodeBlock>

For individual widgets or components that need specific direction handling:

<AposCodeBlock>

```astro
---
// src/components/MyWidget.astro
const { aposData } = Astro.props;
const isRtl = aposData.i18n.direction === 'rtl';
---

<section class:list={[ 'my-widget', { 'is-rtl': isRtl } ]}>
<!-- Widget content -->
</section>

<style>
.my-widget.is-rtl {
direction: rtl;
}
</style>
```
<template v-slot:caption>
src/components/MyWidget.astro
</template>
</AposCodeBlock>

You can also access direction information when building locale switchers:

<AposCodeBlock>

```astro
---
const { aposData } = Astro.props;
const localizations = aposData.localizations || [];
---

<nav class="locale-switcher">
{localizations.map((loc) => (
<a
href={loc._url || loc.homePageUrl}
dir={loc.direction}
class:list={[ 'locale-link', { 'current': loc.current } ]}
>
{loc.label}
</a>
))}
</nav>
```
<template v-slot:caption>
src/components/LocaleSwitcher.astro
</template>
</AposCodeBlock>
99 changes: 99 additions & 0 deletions docs/guide/localization/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,105 @@ So how does Apostrophe choose the best locale to use? In many cases it is clear.
3. The URL matches the locale's configured `prefix` and the locale has no `hostname`.
4. The locale is the default locale (when no other locale matches).

### Right-to-Left (RTL) language support

Apostrophe supports Right-to-Left (RTL) languages, allowing content editors to work comfortably with languages like Hebrew, Arabic, Farsi, and others. RTL support currently affects:

- **Frontend presentation**: Content on your website displays with proper RTL directionality
- **Content editing fields**: Text input fields, rich text areas, and other content fields adapt to RTL when editing content in an RTL locale
- **Admin UI framework**: The admin interface itself (menus, buttons, modals, etc.) remains in LTR direction, regardless of the content locale being edited

::: info
If you provide i18n translation strings for the admin UI in an RTL language, they will currently display in LTR direction. A fully RTL-aware admin interface is planned for future development but is not included in the current implementation.
:::

### Configuring RTL locales

To designate a locale as RTL, add the `direction: 'rtl'` property to the locale definition in the `@apostrophecms/i18n` module configuration.

<AposCodeBlock>

```javascript
module.exports = {
options: {
locales: {
en: {
label: 'English'
},
he: {
label: 'Hebrew',
direction: 'rtl'
},
ar: {
label: 'Arabic',
prefix: '/ar',
direction: 'rtl'
}
}
}
}
```
<template v-slot:caption>
modules/@apostrophecms/i18n/index.js
</template>
</AposCodeBlock>

When an editor switches to an RTL locale, supported input fields (String, Password, URL, Slug, Email) will automatically adjust their text direction to RTL, making content entry more natural for RTL languages.

::: info
Slug fields use left-to-right (LTR) direction by default, regardless of the locale. This is often desirable since URLs are typically more compatible in LTR format.
:::

### Schema field direction overrides

In some cases, you may need to override the text direction for specific fields regardless of the active locale. This is useful for fields that should always maintain a particular direction, such as:
- Code snippets or technical identifiers (always LTR)
- Phone numbers or postal codes (often LTR for consistency)
- Fields in a specific language regardless of the page locale

You can set the `direction` property directly on individual field definitions:

<AposCodeBlock>

```javascript
module.exports = {
extend: '@apostrophecms/piece-type',
fields: {
add: {
productCode: {
type: 'string',
label: 'Product Code',
direction: 'ltr' // Always LTR, even in RTL locales
},
arabicDescription: {
type: 'string',
label: 'Arabic Description',
direction: 'rtl' // Always RTL, even in LTR locales
}
}
}
};
```
<template v-slot:caption>
modules/product/index.js
</template>
</AposCodeBlock>

The `direction` property is supported on the following field types:
- `string`
- `slug`
- `password`
- `date`
- `time`
- `dateAndTime`
- `float`
- `integer`
- `url`
- `email`
- `box`

For information on using RTL locale data in templates, see the [static localization guide](static.md). For a complete reference of RTL-related configuration options, see the [`@apostrophecms/i18n` module reference](/reference/modules/i18n.md).

## The default locale

The default locale is the locale used when no others match the URL better. It is typically the locale used by your website's primary audience. **If no locales are configured, Apostrophe will use `en` as the default locale name.**
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/field-types/array.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contactInfo: {

### Required

| Property | Type | Default | Description |
| Property | Type | Default | Description |
|-----------|-----------|-----------|-----------|
|`label` | String | n/a | Sets the visible label for the field in the UI |
|`type` | String | n/a | Specifies the field type (`array` for this type) |
Expand Down Expand Up @@ -156,7 +156,7 @@ In this example, the third field will "switch" between the ice cream flavors and

### `whenEmpty`

If no array items have been added and the array has `inline: true`, the `whenEmpty` setting supplies an object consisting of a `label` and `icon` that are displayed to the editor until items are added. The `label` property takes a localizable string, while the `icon` property takes an icon that has already been [registered](https://github.com/apostrophecms/apostrophe/blob/main/modules/@apostrophecms/asset/lib/globalIcons.js) or is registered through a module [`icons` property](/reference/module-api/module-overview.md#icons).
If no array items have been added and the array has `inline: true`, the `whenEmpty` setting supplies an object consisting of a `label` and `icon` that are displayed to the editor until items are added. The `label` property takes a localizable string, while the `icon` property takes an icon that has already been [registered](https://github.com/apostrophecms/apostrophe/blob/main/packages/apostrophe/modules/%40apostrophecms/asset/lib/globalIcons.js) or is registered through a module [`icons` property](/reference/module-api/module-overview.md#icons).

```javascript
{
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/box.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ There is also a helper function that will return a string of CSS rules.
|-----------|-----------|-----------|-----------|
|`help` | String | n/a | Help text for the content editor |
|`def` | Object | <pre style='width: 170px; border-radius: 4px; padding: 3px 6px; background-color: var(--vp-code-bg);font-size: var(--vp-code-font-size); color: var(--vp-code-color);'><code>{<br/>&nbsp;top: null,<br />&nbsp;right: null,<br />&nbsp;bottom: null,<br />&nbsp;left: null<br />}</code></pre> | The default value. Must be an object with keys `top`, `right`, `bottom`, `left`. Each value must be a number |
|`direction` | String | ltr | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
|`requiredIf` | Object | `{}` | Conditions to meet before the field is required. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/date.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ birthday: {
| Property | Type | Default | Description |
|-----------|-----------|-----------|-----------|
|`def` | String | n/a | The default value for the field. Must be in `YYYY-MM-DD` format. |
|`direction` | String | 'ltr' | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/dateAndTime.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ eventDateAndTime: {

| Property | Type | Default | Description |
|-----------|-----------|-----------|-----------|
|`direction` | String | 'ltr' | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/email.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ workEmail: {

| Property | Type | Default | Description |
|-----------|-----------|-----------|------------|
|`direction` | String | n/a | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/float.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ gpa: {
| Property | Type | Default | Description |
|-----------|-----------|-----------|-----------|
|`def` | Number | n/a | The default value for the field |
|`direction` | String | 'ltr' | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/integer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ rating: {
| Property | Type | Default | Description |
|-----------|-----------|-----------|-----------|
|`def` | Number | n/a | The default value for the field |
|`direction` | String | 'ltr' | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`if` | Object | `{}` | Conditions to meet before the field is active. [See the guide for details.](/guide/conditional-fields) |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/password.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ secret: {
|-----------|-----------|-----------|------------|
|[`autocomplete`](#autocomplete) | String | n/a | Sets the value of the `autocomplete` attribute on the field. |
|`def` | String | n/a | The default value for the field |
|`direction` | String | n/a | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|`help` | String | n/a | Help text for the content editor |
|`htmlHelp` | String | n/a | Help text with support for HTML markup |
|`min` | Integer | n/a | Sets the minimum number of characters allowed |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/slug.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ projectSlug: {
|-----------|-----------|-----------|-----------|
|[`autocomplete`](#autocomplete) | String | n/a | Sets the value of the `autocomplete` attribute on the field. |
|`def` | String | n/a | The default value for the field |
|`direction` | String | 'rtl' | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|[`following`](#following) | String/Array | n/a | The name of a field or an array of field names that will be used to automatically generate this field's value. If this field is edited to no longer match the fields it is following, it will stop responding to edits in those fields.|
|[`followingIgnore`](#followingignore) | Boolean/Array | n/a | Controls which `following` values should be ignored when auto-generating field content. |
|`help` | String | n/a | Help text for the content editor |
Expand Down
1 change: 1 addition & 0 deletions docs/reference/field-types/string.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ biography: {
|-----------|-----------|-----------|-----------|
|[`autocomplete`](#autocomplete) | String | n/a | Sets the value of the `autocomplete` attribute on the field. |
|`def` | String | n/a | The default value for the field |
|`direction` | String | n/a | Sets the text direction for the field. Use `'ltr'` for left-to-right or `'rtl'` for right-to-left. Overrides the locale's default direction. See the [RTL language support guide](/guide/localization/overview.md#right-to-left-rtl-language-support) for details. |
|[`following`](#following) | String/Array | n/a | The name of a field or an array of field names that will be used to automatically generate this field's value. If this field is edited to no longer match the fields it is following, it will stop responding to edits in those fields.|
|[`followingIgnore`](#followingignore) | Boolean/Array | n/a | Controls which `following` values should be ignored when auto-generating field content. |
|`help` | String | n/a | Help text for the content editor |
Expand Down
Loading