Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use files tracker API on nightly to track files #165

Merged
merged 39 commits into from
Jun 25, 2024
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1b81cdb
Prepare next release
mondeja Jun 25, 2024
0c29f11
Update changelog
mondeja Jun 25, 2024
40aaf5a
Fix error
mondeja Jun 25, 2024
8bfdc1d
Formatting change
mondeja Jun 25, 2024
346b2dc
Fix wording
mondeja Jun 25, 2024
ce2d06d
Improvements
mondeja Jun 25, 2024
7b9a752
relax param parsing
mondeja Jun 25, 2024
9d9682d
fix error
mondeja Jun 25, 2024
5a416c4
Some fixes
mondeja Jun 25, 2024
1fea6d5
Add reference of macro parameters to book
mondeja Jun 25, 2024
12dd668
Better styles for reference
mondeja Jun 25, 2024
5e4d4be
Changes
mondeja Jun 25, 2024
11e3215
Improve reference
mondeja Jun 25, 2024
f305808
Improve book
mondeja Jun 25, 2024
6e6842f
Remove parameters reference from docs
mondeja Jun 25, 2024
909fc0e
Resolve some TODOs in languages
mondeja Jun 25, 2024
d23a8dd
Add section to installation
mondeja Jun 25, 2024
2344504
Add more links to book
mondeja Jun 25, 2024
bf5cc3f
Correctly document new component
mondeja Jun 25, 2024
25c775d
Improvements to book
mondeja Jun 25, 2024
1443854
Add file to .gitattributes
mondeja Jun 25, 2024
0c2e7b6
Implement files tracker API on nightly
mondeja Jun 25, 2024
b470b61
Check test works
mondeja Jun 25, 2024
eafa3d4
Change to nightly
mondeja Jun 25, 2024
f903bfb
Revert to stable
mondeja Jun 25, 2024
0204678
Minor change
mondeja Jun 25, 2024
6846b3e
Minor changes
mondeja Jun 25, 2024
6f080dc
Change order in changelog
mondeja Jun 25, 2024
36fd293
Minor change
mondeja Jun 25, 2024
5323df3
Improve book
mondeja Jun 25, 2024
3093b33
fix typo
mondeja Jun 25, 2024
0eea9ba
Improvements for book
mondeja Jun 25, 2024
5a99345
Fix error
mondeja Jun 25, 2024
bab7ee5
Minor change
mondeja Jun 25, 2024
3d4fbbf
Add nightly section to installation chapter
mondeja Jun 25, 2024
b489ba4
Change
mondeja Jun 25, 2024
b4fc5eb
Fix CI
mondeja Jun 25, 2024
57408f5
Fix wording
mondeja Jun 25, 2024
c1ecfec
Add a lot of dialects
mondeja Jun 25, 2024
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ Cargo.lock -diff
/README.md -diff
/leptos-fluent-macros/README.md -diff
/leptos-fluent/README.md -diff
book/mdbook-admonish.css -diff

# Don't export/archive these files
.github export-ignore
22 changes: 20 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -10,6 +10,22 @@ on:
workflow_dispatch:

jobs:
check-toolchain-is-stable:
name: Check toolchain is stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get toolchain
id: toolchain
uses: ./.github/actions/get-toolchain
- name: Check toolchain
run: |
if [ "${{ steps.toolchain.outputs.channel }}" != "stable" ]; then
echo 'Toolchain is not "stable", is "${{ steps.toolchain.outputs.channel }}". Check the file rust-toolchain.toml'
exit 1
fi
echo "channel=$channel" >> $GITHUB_OUTPUT

qa:
name: QA
runs-on: ubuntu-latest
@@ -82,12 +98,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install mdbook
run: cargo binstall -y mdbook
run: cargo binstall -y mdbook mdbook-admonish mdbook-toc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build book
run: |
cd book
mdbook-admonish install .
mdbook build

unit-tests:
@@ -346,12 +363,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install mdbook
run: cargo binstall -y mdbook
run: cargo binstall -y mdbook mdbook-admonish mdbook-toc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build book
run: |
cd book
mdbook-admonish install .
mdbook build
- name: Deploy book
uses: peaceiris/actions-gh-pages@v3
13 changes: 9 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# CHANGELOG

## Unreleased - [0.1.5]
## 2024-06-26 - [0.1.5]

### New features

- Add `leptos_fluent::SsrHtmlTag` component to render it on SSR to sync
global attributes of `<html>` tag with the current language.
- Add new feature `system` to enable functionalities that require system
information. Useful on non wasm targets like desktop applications.
See [GTK example].
- Add `initial_language_from_system` parameter to `leptos_fluent!` macro to set
the initial language from the system language. Useful for desktop
applications. Must be enabled the new feature `system` to use it.
- Add `leptos_fluent::SsrHtmlTag` component to render it on SSR to sync
global attribute of `<html>` tag with the current language.
- Add `initial_language_from_data_file` parameter to `leptos_fluent!` macro to
set the initial language from a data file when using `system` feature.
- Add `set_language_to_data_file` parameter to `leptos_fluent!` macro to set
@@ -24,6 +24,11 @@

[GTK example]: https://github.com/mondeja/leptos-fluent/tree/master/examples/system-gtk

### Enhancements

- Use files tracker API instead of `include_bytes!` quirk to track files
when `nightly` feature is enabled.

## 2024-06-25 - [0.1.4]

### New features
@@ -305,7 +310,7 @@ version to `0.1` during installation.

- Added all ISO-639-1 and ISO-639-2 languages.

[0.1.5]: https://github.com/mondeja/leptos-fluent/compare/v0.1.4...master
[0.1.5]: https://github.com/mondeja/leptos-fluent/compare/v0.1.4...v0.1.5
[0.1.4]: https://github.com/mondeja/leptos-fluent/compare/v0.1.3...v0.1.4
[0.1.3]: https://github.com/mondeja/leptos-fluent/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/mondeja/leptos-fluent/compare/v0.1.1...v0.1.2
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -161,6 +161,21 @@ fn App() -> impl IntoView {
url_param: "lang",
// Discover the initial language of the user from an URL parameter.
initial_language_from_url_param: true,

// Desktop applications (feature `system`)
// ---------------------------------------
// Set the initial language from the system locale.
initial_language_from_system: true,
// Set the discovered initial language of the user from
// the system locale to a data file.
initial_language_from_system_to_data_file: true,
// Get the initial language from a data file.
initial_language_from_data_file: true,
// Key to use to name the data file. Should be unique per
// application. By default is `"leptos-fluent"`.
data_file_key: "my-app",
// Set the language selected to a data file.
set_language_to_data_file: true,
}};

view! {
@@ -188,9 +203,7 @@ fn TranslatableComponent() -> impl IntoView {

#[component]
fn LanguageSelector() -> impl IntoView {
// Use `expect_i18n()` to get the current i18n context:
let i18n = expect_i18n();

// `expect_i18n()` to get the i18n context
// `i18n.languages` is a static array with the available languages
// `i18n.language.get()` to get the current language
// `lang.activate()` to set the current language
@@ -199,7 +212,7 @@ fn LanguageSelector() -> impl IntoView {
view! {
<fieldset>
{
move || i18n.languages.iter().map(|lang| {
move || expect_i18n().languages.iter().map(|lang| {
view! {
<div>
<input
@@ -227,6 +240,7 @@ fn LanguageSelector() -> impl IntoView {
- **Actix Web integration**: `actix`
- **Axum integration**: `axum`
- **Nightly toolchain**: `nightly`
- **Desktop applications**: `system`
- **JSON languages file**: `json` (enabled by default)
- **YAML languages file**: `yaml`
- **JSON5 languages file**: `json5`
@@ -240,7 +254,7 @@ fn LanguageSelector() -> impl IntoView {

[leptos]: https://leptos.dev/
[fluent-templates]: https://github.com/XAMPPRocky/fluent-templates
[quickstart]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
[quickstart]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
[examples]: https://github.com/mondeja/leptos-fluent/tree/master/examples
[book]: https://mondeja.github.io/leptos-fluent/
[documentation]: https://docs.rs/leptos-fluent
16 changes: 16 additions & 0 deletions book/book.toml
Original file line number Diff line number Diff line change
@@ -17,7 +17,23 @@ build-dir = "dist"
site-url = "/leptos-fluent/"
git-repository-url = "https://github.com/mondeja/leptos-fluent/tree/master/book"
edit-url-template = "https://github.com/mondeja/leptos-fluent/edit/master/book/{path}"
additional-css = ["./mdbook-admonish.css"]

[output.html.playground]
editable = true
line-numbers = true

[preprocessor.admonish]
on_failure = "bail"
command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install`

[preprocessor.admonish.default]
collapsible = false

[preprocessor.admonish.renderer.test]
render_mode = "strip"

[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]
373 changes: 373 additions & 0 deletions book/mdbook-admonish.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,373 @@
@charset "UTF-8";
:is(.admonition) {
display: flow-root;
margin: 1.5625em 0;
padding: 0 1.2rem;
color: var(--fg);
page-break-inside: avoid;
background-color: var(--bg);
border: 0 solid black;
border-inline-start-width: 0.4rem;
border-radius: 0.2rem;
box-shadow:
0 0.2rem 1rem rgba(0, 0, 0, 0.05),
0 0 0.1rem rgba(0, 0, 0, 0.1);
}
@media print {
:is(.admonition) {
box-shadow: none;
}
}
:is(.admonition) > * {
box-sizing: border-box;
}
:is(.admonition) :is(.admonition) {
margin-top: 1em;
margin-bottom: 1em;
}
:is(.admonition) > .tabbed-set:only-child {
margin-top: 0;
}
html :is(.admonition) > :last-child {
margin-bottom: 1.2rem;
}

a.admonition-anchor-link {
display: none;
position: absolute;
left: -1.2rem;
padding-right: 1rem;
}
a.admonition-anchor-link:link,
a.admonition-anchor-link:visited {
color: var(--fg);
}
a.admonition-anchor-link:link:hover,
a.admonition-anchor-link:visited:hover {
text-decoration: none;
}
a.admonition-anchor-link::before {
content: "§";
}

:is(.admonition-title, summary.admonition-title) {
position: relative;
min-height: 4rem;
margin-block: 0;
margin-inline: -1.6rem -1.2rem;
padding-block: 0.8rem;
padding-inline: 4.4rem 1.2rem;
font-weight: 700;
background-color: rgba(68, 138, 255, 0.1);
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
display: flex;
}
:is(.admonition-title, summary.admonition-title) p {
margin: 0;
}
html :is(.admonition-title, summary.admonition-title):last-child {
margin-bottom: 0;
}
:is(.admonition-title, summary.admonition-title)::before {
position: absolute;
top: 0.625em;
inset-inline-start: 1.6rem;
width: 2rem;
height: 2rem;
background-color: #448aff;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-size: contain;
content: "";
}
:is(.admonition-title, summary.admonition-title):hover
a.admonition-anchor-link {
display: initial;
}

details.admonition > summary.admonition-title::after {
position: absolute;
top: 0.625em;
inset-inline-end: 1.6rem;
height: 2rem;
width: 2rem;
background-color: currentcolor;
mask-image: var(--md-details-icon);
-webkit-mask-image: var(--md-details-icon);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-size: contain;
content: "";
transform: rotate(0deg);
transition: transform 0.25s;
}
details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg);
}

:root {
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}

:root {
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
}

:is(.admonition):is(.admonish-note) {
border-color: #448aff;
}

:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(68, 138, 255, 0.1);
}
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #448aff;
mask-image: var(--md-admonition-icon--admonish-note);
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
border-color: #00b0ff;
}

:is(.admonish-abstract, .admonish-summary, .admonish-tldr)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 176, 255, 0.1);
}
:is(.admonish-abstract, .admonish-summary, .admonish-tldr)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #00b0ff;
mask-image: var(--md-admonition-icon--admonish-abstract);
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-info, .admonish-todo) {
border-color: #00b8d4;
}

:is(.admonish-info, .admonish-todo)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 184, 212, 0.1);
}
:is(.admonish-info, .admonish-todo)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #00b8d4;
mask-image: var(--md-admonition-icon--admonish-info);
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
border-color: #00bfa5;
}

:is(.admonish-tip, .admonish-hint, .admonish-important)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 191, 165, 0.1);
}
:is(.admonish-tip, .admonish-hint, .admonish-important)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #00bfa5;
mask-image: var(--md-admonition-icon--admonish-tip);
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
border-color: #00c853;
}

:is(.admonish-success, .admonish-check, .admonish-done)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 200, 83, 0.1);
}
:is(.admonish-success, .admonish-check, .admonish-done)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #00c853;
mask-image: var(--md-admonition-icon--admonish-success);
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
border-color: #64dd17;
}

:is(.admonish-question, .admonish-help, .admonish-faq)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(100, 221, 23, 0.1);
}
:is(.admonish-question, .admonish-help, .admonish-faq)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #64dd17;
mask-image: var(--md-admonition-icon--admonish-question);
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
border-color: #ff9100;
}

:is(.admonish-warning, .admonish-caution, .admonish-attention)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 145, 0, 0.1);
}
:is(.admonish-warning, .admonish-caution, .admonish-attention)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff9100;
mask-image: var(--md-admonition-icon--admonish-warning);
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
border-color: #ff5252;
}

:is(.admonish-failure, .admonish-fail, .admonish-missing)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 82, 82, 0.1);
}
:is(.admonish-failure, .admonish-fail, .admonish-missing)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff5252;
mask-image: var(--md-admonition-icon--admonish-failure);
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-danger, .admonish-error) {
border-color: #ff1744;
}

:is(.admonish-danger, .admonish-error)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 23, 68, 0.1);
}
:is(.admonish-danger, .admonish-error)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff1744;
mask-image: var(--md-admonition-icon--admonish-danger);
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-bug) {
border-color: #f50057;
}

:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(245, 0, 87, 0.1);
}
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #f50057;
mask-image: var(--md-admonition-icon--admonish-bug);
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-example) {
border-color: #7c4dff;
}

:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(124, 77, 255, 0.1);
}
:is(.admonish-example)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #7c4dff;
mask-image: var(--md-admonition-icon--admonish-example);
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

:is(.admonition):is(.admonish-quote, .admonish-cite) {
border-color: #9e9e9e;
}

:is(.admonish-quote, .admonish-cite)
> :is(.admonition-title, summary.admonition-title) {
background-color: rgba(158, 158, 158, 0.1);
}
:is(.admonish-quote, .admonish-cite)
> :is(.admonition-title, summary.admonition-title)::before {
background-color: #9e9e9e;
mask-image: var(--md-admonition-icon--admonish-quote);
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}

.navy :is(.admonition) {
background-color: var(--sidebar-bg);
}

.ayu :is(.admonition),
.coal :is(.admonition) {
background-color: var(--theme-hover);
}

.rust :is(.admonition) {
background-color: var(--sidebar-bg);
color: var(--sidebar-fg);
}
.rust .admonition-anchor-link:link,
.rust .admonition-anchor-link:visited {
color: var(--sidebar-fg);
}
4 changes: 4 additions & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -12,3 +12,7 @@
- [Languages](./languages.md)
- [Checking translations](./checking-translations.md)
- [FAQs](./faqs.md)

# Reference

- [`leptos_fluent!`](./leptos_fluent.md)
60 changes: 35 additions & 25 deletions book/src/basic-usage.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Basic usage

<!-- toc -->

## Minimal skeleton

The most basic CSR app is reproduced here:

```plaintext
@@ -134,16 +138,16 @@ move_tr!("language-selected-is", { "lang" => i18n.language.get().name })
Additionally, you can use the [`tr!`] macro to translate a string inside
a reactive context. Note that if you're not inside a reactive context,
the translation won't be updated on the fly when the language changes.
This can lead to errors in console output like:
This can lead to warnings in console output like:

```text
At <file>.rs:ln, you access a signal or memo (defined at <file>.rs:ln)
outside of a reactive context. This might mean your app is not responding
to changes in signal values in the way you expect.
```admonish warning
At `./path/to/file.rs:ln`, you access a signal or memo (defined at
`./path/to/file.rs:ln`) outside of a reactive context. This might mean your
app is not responding to changes in signal values in the way you expect.
```

Can be fixed by replacing calls to `tr!` with `move_tr!` or wrapping the
`tr!` calls in a reactive context.
Can be fixed by replacing calls to [`tr!`] with [`move_tr!`] or wrapping the
[`tr!`] calls in a reactive context.

The previous code _could_ be rewritten as:

@@ -153,8 +157,8 @@ move || tr!("select-a-language")
move || tr!("language-selected-is", { "lang" => i18n.language.get().name })
```

The main difference is that `move_tr!` encapsulates the movement in a
`leptos::Signal` (wich is copyable), strictly would be rewritten as:
The main difference is that [`move_tr!`] encapsulates the movement in a
[`leptos::Signal`], strictly would be rewritten as:

```rust
leptos::Signal::derive(move || tr!("select-a-language"))
@@ -168,14 +172,13 @@ Use the [`expect_i18n`] function to get the current i18n context:
let i18n = leptos_fluent::expect_i18n();
```

It is exported as `i18n()` too:
It is exported as [`i18n`] too:

```rust
let i18n = leptos_fluent::i18n();
```

With the function `use_i18n()` context you'll get an `Option` with the current
i18n context:
The function [`use_i18n`] returns an `Option` with the current i18n context:

```rust
let i18n = leptos_fluent::use_i18n().expect("No `leptos_fluent::I18n` context found");
@@ -192,40 +195,43 @@ The i18n context has the following fields:

### Update language

To update the language, use `set` method of `language` field or just
[`lang.activate()`] (new in v0.1.1):
To update the language, use [`lang.activate`] or the `set` method of [`language`]:

```rust
expect_i18n().language.set(lang);
lang.activate();

lang.activate(); // New in v0.1.1
expect_i18n().language.set(lang);
```

// features = ["nightly"]
let i18n = leptos_fluent::expect_i18n();
i18n(lang);
```admonish tip title='Nightly'
When `nightly` feature is enabled, you update it passing a new language to the
context as a function with `(leptos_fluent::i18n())(lang)`.
```

### Get active language

To get the current active language, use `get` method of `language` field:
To get the current active language, use `get` method of [`language`] field:

```rust
i18n.language.get()
```

i18n() // features = ["nightly"]
```admonish tip title='Nightly'
When `nightly` enabled, you can get the active language with
`(leptos_fluent::i18n())()`.
```

### Get available languages

To get the available languages, iterate over the `languages` field:
To get the available languages, iterate over the [`languages`] field:

```rust
i18n.languages.iter()
```

### Check if a language is active

To check if a language is the active one, use `is_active` method of a
To check if a language is the active one, use [`is_active`] method of a
[`leptos_fluent::Language`] struct:

```rust
@@ -236,9 +242,13 @@ lang.is_active()
[`move_tr!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.move_tr.html
[`leptos_fluent::I18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html
[`leptos_fluent::Language`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.Language.html
[`lang.activate()`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.Language.html#method.activate
[`expect_i18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/fn.expect_i18n.html
[`lang.activate`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.Language.html#method.activate
[`language`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html#structfield.language
[`languages`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html#structfield.languages
[`translations`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html#structfield.translations
[fluent-templates]: https://docs.rs/fluent-templates/latest/fluent_templates
[`leptos::Signal`]: https://docs.rs/leptos/latest/leptos/struct.Signal.html
[`expect_i18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/fn.expect_i18n.html
[`i18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/fn.i18n.html
[`use_i18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/fn.use_i18n.html
[`is_active`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.Language.html#method.is_active
11 changes: 6 additions & 5 deletions book/src/checking-translations.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ For single crate projects, it would be something like:
```rust
leptos_fluent! {{
check_translations: "./src/**/*.rs",
// ...
}}
```

@@ -65,9 +64,11 @@ The mechanism of translations checking needs to know where reside the calls to
This is performed by parsing the source code looking for these macros
invocations.

This is the main reason why **leptos-fluent** doesn't provide
ways to translate directly using methods of the [`leptos_fluent::I18n`]
context, as it would be impossible to extract the translations at compile time.
```admonish note title='Why macros'
**leptos-fluent** doesn't provide ways to translate directly using
[`leptos_fluent::I18n`] context methods, as it would be impossible to extract
the translations at compile time.
```

The only limitation for checking translations with glob patterns is that the
[`tr!`] and [`move_tr!`] macros that consume each context must be in
@@ -77,4 +78,4 @@ separation of contexts in the codebase.
[`tr!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.tr.html
[`move_tr!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.move_tr.html
[`leptos_fluent::I18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html
[`leptos_fluent!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
[`leptos_fluent!`]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
59 changes: 46 additions & 13 deletions book/src/faqs.md
Original file line number Diff line number Diff line change
@@ -2,24 +2,27 @@

# FAQs

<!-- toc -->

### How to get `unic_langid::LanguageIdentifier` if not installed?

```rust
use fluent_templates::LanguageIdentifier;
```

`fluent-templates` also depends externally on [`fluent-bundle`] whichs
provides utilities for parsing the Fluent syntax.

[`fluent-bundle`]: https://docs.rs/fluent-bundle/latest/fluent_bundle/
```admonish tip
`fluent-templates` also depends externally on
[`fluent-bundle`](https://docs.rs/fluent-bundle/latest/fluent_bundle/)
whichs provides utilities for parsing the Fluent syntax.
```

### How to get the i18n context at initialization?

```rust
use leptos_fluent::leptos_fluent;

let i18n = leptos_fluent!{{
// ... options
let i18n = leptos_fluent! {{
// ...
}};

leptos::logging::log!("i18n context: {i18n:?}");
@@ -33,8 +36,7 @@ Use an expression to set the cookie attributes and will not be validated.
let attrs = "SameSite=Strict; MyCustomAttr=MyCustomValue;";
leptos_fluent! {{
cookie_attrs: attrs,

// ... other options
// ...
}}
```

@@ -46,14 +48,16 @@ From fluent-templates `v0.9.5` onwards you can get it from your translations.
let fallback_language = expect_i18n().translations.get()[0].fallback();
```

### Why examples don't use `<For/>` component?
### Why examples don't use [`<For/>`] component?

There are some cases in which the `For` component is not reproducible between
```admonish bug
There are some cases in which the [`<For/>`] component is not reproducible between
SSR and hydrate modes leading to different renders, so decided to use a
simple vector to not bring confusion to main examples.
```

In any case, the `<For/>` component is secure on CSR contexts and
`leptos_fluent::Language`s implement `Hash` and `Eq` traits to be
In any case, the [`<For/>`] component is secure on CSR contexts and
[`leptos_fluent::Language`]s implement `Hash` and `Eq` traits to be
able to be passed directly to `key`s properties trigerring reactivity
depending on the current active language.

@@ -75,7 +79,7 @@ fn render_language(lang: &'static Language) -> impl IntoView { ... }

### How to manage translations on server actions

The translations reside on the client side, so the `leptos_fluent::I18n`
The translations reside on the client side, so the [`leptos_fluent::I18n`]
can not be accessed as context on server actions. Pass the translations
as values if the bandwidth is not a problem or use your own statics on
server side.
@@ -122,3 +126,32 @@ fn render_language(lang: &'static Language) -> impl IntoView {
}
}
```

### How to change `<html>` attributes on SSR

Use the component [`leptos_fluent::SsrHtmlTag`]:

```rust
use leptos::*;
use leptos_fluent::{leptos_fluent, SsrHtmlTag};

#[component]
pub fn App() -> impl IntoView {
leptos_fluent! {{
// ...
}};

view! {
<SsrHtmlTag/>
<LanguageSelector/>
}
}

#[component]
fn LanguageSelector() -> impl IntoView { ... }
```

[`<For/>`]: https://docs.rs/leptos/latest/leptos/fn.For.html
[`leptos_fluent::SsrHtmlTag`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/fn.SsrHtmlTag.html
[`leptos_fluent::Language`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.Language.html
[`leptos_fluent::I18n`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html
50 changes: 49 additions & 1 deletion book/src/install.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Installation

<!-- toc -->

## CSR

For client side rendering apps you only need to install **leptos-fluent** and
@@ -55,16 +57,62 @@ ssr = [
watch-additional-files = ["locales"]
```

## Desktop applications

**leptos-fluent** can be installed on non-wasm targets, like desktop
applications. You need to install **leptos-fluent**, [`fluent-templates`]
and enable the `system` feature:

```toml
[dependencies]
leptos-fluent = { version = "0.1", features = ["system"] }
fluent-templates = "0.9"
```

```admonish example
See the [GTK example](https://github.com/mondeja/leptos-fluent/tree/master/examples/system-gtk).
```

## Nightly toolchain

**leptos-fluent** builds nightly functionalities by enabling the `nightly`
feature:

```toml
[dependencies]
leptos-fluent = { version = "0.1", features = ["nightly"] }
fluent-templates = "0.9"
```

## Language files

By default, **leptos-fluent** supports JSON languages files. If you want to
use other formats to load your custom languages, you can enable the
`json5` or `yaml` features:

```toml
[dependencies]
fluent-templates = "0.9"
leptos-fluent = {
version = "0.1", features = ["json5"], default-features = false
}
```

```admonish tip
See [**4. Languages**](https://mondeja.github.io/leptos-fluent/languages.html).
```

## Features

- **Server Side Rendering**: `ssr`
- **Hydration**: `hydrate`
- **Actix Web integration**: `actix`
- **Axum integration**: `axum`
- **Nightly toolchain**: `nightly`
- **Desktop applications**: `system`
- **JSON languages file**: `json` (enabled by default)
- **YAML languages file**: `yaml`
- **JSON5 languages file**: `json5`

[`fluent-templates`]: https://github.com/XAMPPRocky/fluent-templates
[`leptos_fluent!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
[`leptos_fluent!`]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
23 changes: 14 additions & 9 deletions book/src/languages.md
Original file line number Diff line number Diff line change
@@ -41,8 +41,10 @@ This standard enforces that an user will always be able to select their
language in their own language, and not in the current language of the
application.

Note that the order of the languages will be defined based on the alphabetical
```admonish abstract title='Order'
The order of the languages will be defined based on the alphabetical
order of their names, not their codes.
```

## The languages file

@@ -68,13 +70,17 @@ The languages file must expose an array of arrays with the structure:

```json5
[
// Code, Name, "ltr"/"rtl"/"auto" (optional)
// Code, Name, "ltr"/"rtl"/"auto" (optional)
["code", "Language name", "Writing direction"],
]
```

The order of the languages in [`leptos_fluent::I18n::languages`] will be
the same as in the file regardless of the alphabetical order of the names.
```admonish abstract title='Order'
The order of the languages in
[`leptos_fluent::I18n::languages`](https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html#structfield.languages)
will be the same as in the file regardless of the alphabetical order
of the names.
```

The first in the languages file will used as the initial of the user when any
other initialization value is discovered. Use the same as the one configured
@@ -122,9 +128,9 @@ Available features for languages file formats are:
- `yaml`: YAML
- `json5`: JSON5

## Tracking locales files with `cargo leptos`
## Tracking locales files with [`cargo leptos`]

Using [`cargo-leptos`] the _locales/_ folder must be manually
Using [`cargo leptos`] the _locales/_ folder must be manually
configured to be watched:

```toml
@@ -143,6 +149,5 @@ watch-additional-files = ["examples/csr/locales"]
```

[ISO 639-1 code]: https://en.wikipedia.org/wiki/ISO_639-1
[`leptos_fluent::I18n::languages`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/struct.I18n.html#structfield.languages
[`cargo-leptos`]: https://github.com/leptos-rs/cargo-leptos
[`leptos_fluent!`]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
[`cargo leptos`]: https://github.com/leptos-rs/cargo-leptos
[`leptos_fluent!`]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
555 changes: 555 additions & 0 deletions book/src/leptos_fluent.md

Large diffs are not rendered by default.

105 changes: 48 additions & 57 deletions book/src/strategies.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
<!-- markdownlint-disable MD013 -->

# Features
# Strategies

All the features of the framework are optional, following a declarative
"opt-in" configuration method.

<!-- toc -->

## Loading the initial language of the user

The initial language of the user can be set in different ways:

| Strategy | CSR | SSR | [`leptos_fluent!`] parameter |
| :----------------------------------------- | :-: | :-: | :--------------------------------------------- |
| [URL parameter] ||| `initial_language_from_url_param` |
| [Cookie] ||| `initial_language_from_cookie` |
| Browser [local storage] ||| `initial_language_from_localstorage` |
| Browser language ([`navigator.languages`]) ||| `initial_language_from_navigator` |
| [`Accept-Language`] header ||| `initial_language_from_accept_language_header` |
| Strategy | CSR | SSR | Desktop | [`leptos_fluent!`] |
| :----------------------------------------- | :-: | :-: | :-----: | :--------------------------------------------- |
| [URL parameter] |||| `initial_language_from_url_param` |
| [Cookie] |||| `initial_language_from_cookie` |
| Browser [local storage] |||| `initial_language_from_localstorage` |
| Browser language ([`navigator.languages`]) |||| `initial_language_from_navigator` |
| [`Accept-Language`] header |||| `initial_language_from_accept_language_header` |
| System language |||| `initial_language_from_system` |
| Data file |||| `initial_language_from_data_file` |

All of them can be used at the same time or just one of them. The first setting
found will be used. The order of precedence is:
@@ -29,26 +33,35 @@ found will be used. The order of precedence is:
2. [Cookie].
3. Browser [local storage].
4. Browser language from [`navigator.languages`].
- **Desktop** ([`system` feature][desktop-applications])
1. Data file.
2. System language.

## Updating the language on the client

When the user changes the language and `I18n::language.set` is called, the
framework can perform a side effect to update the language in the client. The
following strategies are available:

| Strategy | [`leptos_fluent!`] parameter |
| Strategy | [`leptos_fluent!`] |
| :---------------------- | :----------------------------- |
| [URL parameter] | `set_language_to_url_param` |
| [Cookie] | `set_language_to_cookie` |
| Browser [local storage] | `set_language_to_localstorage` |

### Updating the language from initialization on the client
### Desktop applications ([`system`][desktop-applications])

| Strategy | [`leptos_fluent!`] |
| :-------- | :-------------------------- |
| Data file | `set_language_to_data_file` |

## Updating the language from initialization on the client

When a language is loaded from initialization, the framework can perform a side
effect to persistently storage the language in the client. The following strategies
are available:

| Strategy | [`leptos_fluent!`] parameter |
| Strategy | [`leptos_fluent!`] |
| :----------------------------------------- | :------------------------------------------------ |
| [URL parameter] to [local storage] | `initial_language_from_url_param_to_localstorage` |
| [URL parameter] to [cookie] | `initial_language_from_url_param_to_cookie` |
@@ -57,15 +70,21 @@ are available:
| [`navigator.languages`] to [local storage] | `initial_language_from_navigator_to_localstorage` |
| [`navigator.languages`] to [cookie] | `initial_language_from_navigator_to_cookie` |

### Desktop applications ([`system`][desktop-applications])

| Strategy | [`leptos_fluent!`] |
| :--------------------------- | :------------------------------------------ |
| System language to data file | `initial_language_from_system_to_data_file` |

## Client side effects

When the user updates the language, the framework can perform side effects to
update the language in the client. The following side effects are available:

| Side effect | [`leptos_fluent!`] parameter |
| :------------------------------ | :--------------------------- |
| [`<html lang="...">`] attribute | `sync_html_tag_lang` |
| [`<html dir="...">`] attribute | `sync_html_tag_dir` |
| Side effect | [`leptos_fluent!`] |
| :------------------------------ | :------------------- |
| [`<html lang="...">`] attribute | `sync_html_tag_lang` |
| [`<html dir="...">`] attribute | `sync_html_tag_dir` |

[`<html lang="...">`]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang
[`<html dir="...">`]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir
@@ -74,52 +93,24 @@ update the language in the client. The following side effects are available:

The names of the settings can be configured using the following parameters:

| Strategy | [`leptos_fluent!`] parameter | Default value |
| :---------------------- | :--------------------------- | :------------ |
| [Cookie] | `cookie_name` | `"lf-lang"` |
| [Cookie attributes] | `cookie_attrs` | `""` |
| Browser [local storage] | `localstorage_key` | `"lang"` |
| [URL parameter] | `url_param` | `"lang"` |
| Strategy | [`leptos_fluent!`] | Default value |
| :---------------------- | :----------------- | :------------ |
| [Cookie] | `cookie_name` | `"lf-lang"` |
| [Cookie attributes] | `cookie_attrs` | `""` |
| Browser [local storage] | `localstorage_key` | `"lang"` |
| [URL parameter] | `url_param` | `"lang"` |

### Desktop applications ([`system`][desktop-applications])

[`leptos_fluent!`]: https://docs.rs/leptos-fluent-macros/latest/leptos_fluent_macros/macro.leptos_fluent.html
| Strategy | [`leptos_fluent!`] | Default value |
| :-------- | :----------------- | :---------------- |
| Data file | `data_file_key` | `"leptos-fluent"` |

[`leptos_fluent!`]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
[local storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
[`navigator.languages`]: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages
[`Accept-Language`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language
[Cookie]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
[Cookie attributes]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie
[URL parameter]: https://developer.mozilla.org/es/docs/Web/API/URLSearchParams

# Common configurations

## Local storage from navigator (CSR)

```rust
leptos_fluent! {{
locales: "./locales",
translations: [TRANSLATIONS],

set_language_to_localstorage: true,
initial_language_from_localstorage: true,
initial_language_from_navigator: true,
initial_language_from_navigator_to_localstorage: true,
initial_language_from_url_param: true,
initial_language_from_url_param_to_localstorage: true,
}}
```

## Cookie from navigator and header (SSR + CSR)

```rust
leptos_fluent! {{
locales: "./locales",
translations: [TRANSLATIONS],

set_language_to_cookie: true,
initial_language_from_cookie: true,
initial_language_from_navigator: true,
initial_language_from_navigator_to_cookie: true,
initial_language_from_url_param: true,
initial_language_from_url_param_to_cookie: true,
initial_language_from_accept_language_header: true,
}}
```
[desktop-applications]: https://mondeja.github.io/leptos-fluent/install.html#desktop-applications
17 changes: 5 additions & 12 deletions examples/ssr-hydrate-axum/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,16 +19,14 @@ leptos = "0.6"
leptos_meta = "0.6"
leptos_axum = { version = "0.6", optional = true }
leptos_router = "0.6"
leptos-fluent = { path = "../../leptos-fluent", features = ["yaml"], default-features = false}
leptos-fluent = { path = "../../leptos-fluent", features = [
"yaml"
], default-features = false }
fluent-templates = "0.9"
wasm-bindgen = "=0.2.92"

[features]
csr = [
"leptos/csr",
"leptos_meta/csr",
"leptos_router/csr",
]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
hydrate = [
"leptos/hydrate",
"leptos_meta/hydrate",
@@ -77,9 +75,4 @@ lib-features = ["hydrate"]
lib-profile-release = "wasm-release"

[package.metadata.cargo-all-features]
skip_feature_sets = [
[],
["csr", "ssr"],
["csr", "hydrate"],
["ssr", "hydrate"],
]
skip_feature_sets = [[], ["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
2 changes: 1 addition & 1 deletion examples/system-gtk/README.md
Original file line number Diff line number Diff line change
@@ -9,6 +9,6 @@ sudo apt install libgtk-4-dev
## Build and run

```sh
cd examples/gtk
cd examples/system-gtk
cargo run
```
6 changes: 3 additions & 3 deletions examples/system-gtk/src/main.rs
Original file line number Diff line number Diff line change
@@ -25,11 +25,11 @@ fn main() {
translations: [TRANSLATIONS],
locales: "./locales",
check_translations: "./src/**/*.rs",
initial_language_from_system: true,
set_language_to_data_file: true,
initial_language_from_data_file: true,
data_file_key: "gtk-example",
initial_language_from_system: true,
initial_language_from_system_to_data_file: true,
set_language_to_data_file: true,
data_file_key: "leptos-fluent-gtk-example",
}};

// Run the application
69 changes: 46 additions & 23 deletions leptos-fluent-macros/src/files_tracker.rs
Original file line number Diff line number Diff line change
@@ -5,35 +5,58 @@ pub(crate) fn build_files_tracker_quote(
languages_path: &Option<String>,
core_locales_path: &Option<String>,
) -> proc_macro2::TokenStream {
let mut files_tracker_str = "{".to_string();
for (lang, paths) in fluent_resources {
for (i, path) in paths.iter().enumerate() {
#[cfg(feature = "nightly")]
{
for (_, paths) in fluent_resources {
for path in paths {
proc_macro::tracked_path::path(path);
}
}

if let Some(languages_file_path) = &languages_path {
proc_macro::tracked_path::path(languages_file_path);
}

if let Some(core_locales_file_path) = &core_locales_path {
proc_macro::tracked_path::path(core_locales_file_path);
}

"".parse::<proc_macro2::TokenStream>().unwrap()
}

#[cfg(not(feature = "nightly"))]
{
let mut files_tracker_str = "{".to_string();
for (lang, paths) in fluent_resources {
for (i, path) in paths.iter().enumerate() {
files_tracker_str.push_str(&format!(
"let s{}{} = include_bytes!(\"{}\");",
lang.replace('-', "_"),
i,
&escape_string(path)
));
}
}
if let Some(languages_file_path) = &languages_path {
files_tracker_str.push_str(&format!(
"let s{}{} = include_bytes!(\"{}\");",
lang.replace('-', "_"),
i,
&escape_string(path)
"let languages = include_bytes!(\"{}\");",
&escape_string(languages_file_path)
));
}
if let Some(core_locales_file_path) = &core_locales_path {
files_tracker_str.push_str(&format!(
"let core_locales = include_bytes!(\"{}\");",
&escape_string(core_locales_file_path)
));
}
files_tracker_str.push_str("};");
files_tracker_str
.parse::<proc_macro2::TokenStream>()
.unwrap()
}
if let Some(languages_file_path) = &languages_path {
files_tracker_str.push_str(&format!(
"let languages = include_bytes!(\"{}\");",
&escape_string(languages_file_path)
));
}
if let Some(core_locales_file_path) = &core_locales_path {
files_tracker_str.push_str(&format!(
"let core_locales = include_bytes!(\"{}\");",
&escape_string(core_locales_file_path)
));
}
files_tracker_str.push_str("};");
files_tracker_str
.parse::<proc_macro2::TokenStream>()
.unwrap()
}

#[cfg(not(feature = "nightly"))]
fn escape_string(s: &str) -> String {
s.replace('"', "\\\"")
// Windows path separator
335 changes: 218 additions & 117 deletions leptos-fluent-macros/src/languages.rs

Large diffs are not rendered by default.

147 changes: 5 additions & 142 deletions leptos-fluent-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(feature = "nightly", feature(track_path))]

//! Macros for the leptos-fluent crate.
//!
@@ -29,6 +30,8 @@ use quote::quote;

/// Create the i18n context for internationalization.
///
/// [Reference](https://mondeja.github.io/leptos-fluent/leptos_fluent.html)
///
/// # Example
///
/// ```rust,ignore
@@ -73,148 +76,8 @@ use quote::quote;
/// }
/// ```
///
/// ## Arguments
///
/// - **`translations` \***: Translations to be used by your application. This
/// must be the same identifier used in the [`fluent_templates::static_loader!`]
/// macro, which returns [`once_cell:sync::Lazy`]`<[`StaticLoader`]>`.
/// - **`locales`**: Path to the locales folder, which must contain the translations
/// for each language in your application. Is expected to be a path relative from
/// `Cargo.toml` file, the same used in the [`fluent_templates::static_loader!`]
/// macro.
/// - **`core_locales`**: Path to the core locales file, which must contain a shared
/// translation for all languages. Is expected to be a path relative from `Cargo.toml`,
/// the same used in the [`fluent_templates::static_loader!`] macro.
/// - **`check_translations`**: Path to the files to check if all translations are
/// being used and their placeholders are correct. Is expected to be a glob pattern
/// relative from `Cargo.toml` file. Tipically, you should use `"./src/**/*.rs"` for
/// a single crate or something like `"../{app,components}/src/**/*.rs"` to match
/// multiple crates in a workspace.
/// - **`languages`**: Path to a languages file, which should an array of arrays
/// where each inner array contains a language identifier and a language name,
/// respectively. The language identifier should be a valid language tag, such as
/// `en-US`, `en`, `es-ES`, etc. Is expected to be a path relative from `Cargo.toml`
/// file.
/// By default, the languages file should be a JSON with a *.json* extension because
/// the `json` feature is enabled. For example:
/// ```json
/// [
/// ["en-US", "English (United States)"],
/// ["es-ES", "Español (España)"]
/// ]
/// ```
/// You can set `default-features = false` and enable the `yaml` or the `json5` feature
/// to be able to use a YAML or JSON5 file. For example:
/// ```yaml
/// # locales/languages.yaml
/// - - en-US
/// - English (United States)
/// - - es-ES
/// - Español (España)
/// ```
/// ```json5
/// // locales/languages.json5
/// [
/// ["en-US", "English (United States)"],
/// ["es-ES", "Español (España)"]
/// ]
/// ```
/// You can define a third element in the inner array with the direction of the language,
/// to use it in the [`<html dir="...">` attribute] (see `sync_html_tag_dir`). For example:
/// ```json
/// [
/// ["en-US", "English (United States)", "ltr"],
/// ["es-ES", "Español (España)", "auto"],
/// ["ar", "العربية", "rtl"],
/// ["it", "Italiano"]
/// ]
/// ```
/// - **`sync_html_tag_lang`** (_`false`_): Synchronize the global [`<html lang="...">` attribute]
/// with current language using [`leptos::create_effect`]. Can be a literal boolean or an
/// expression that will be evaluated at runtime.
/// - **`sync_html_tag_dir`** (_`false`_): Synchronize the global [`<html dir="...">` attribute]
/// with current language using [`leptos::create_effect`]. Can be a literal boolean or an
/// expression that will be evaluated at runtime. For custom languages from a languages file,
/// you can specify a third element in the inner array with the direction of the language,
/// which can be `"auto"`, `"ltr"`, or `"rtl"`. For automatic languages will be defined depending
/// on the language. For example, Arabic will be `"rtl"`, English will be `"ltr"` and Japanese
/// will be `"auto"`.
/// - **`url_param`** (_`"lang"`_): The parameter name to manage the language in a URL parameter.
/// Can be a literal string or an expression that will be evaluated at runtime. It will take effect
/// on client-side and server side.
/// - **`initial_language_from_url_param`** (_`false`_): Load the initial language of the user
/// from a URL parameter. Can be a literal boolean or an expression that will be evaluated at
/// runtime. It will take effect on client-side and server side.
/// - **`set_language_to_url_param`** (_`false`_): Save the language of the user to an URL parameter
/// when setting the language. Can be a literal boolean or an expression that will be evaluated at
/// runtime. It will only take effect on client-side.
/// - **`initial_language_from_url_param_to_localstorage`** (_`false`_): Save the initial language
/// of the user from the URL to [local storage]. Can be a literal boolean or an expression that will
/// be evaluated at runtime. It will only take effect on client-side.
/// - **`initial_language_from_url_param_to_cookie`** (_`false`_): Save the initial language of the user
/// from the URL to a cookie. Can be a literal boolean or an expression that will be evaluated at runtime.
/// - **`localstorage_key`** (_`"lang"`_): The [local storage] field to get and save the current language
/// of the user. Can be a literal string or an expression that will be evaluated at runtime.
/// It will only take effect on client-side.
/// - **`initial_language_from_localstorage`** (_`false`_): Load the initial language of the
/// user from [local storage] if not found in the URL param. Can be a literal boolean or an expression
/// that will be evaluated at runtime. It will only take effect on client-side.
/// - **`set_language_to_localstorage`** (_`false`_): Save the language of the user to [local storage] if
/// when setting the language. Can be a literal boolean or an expression that will be evaluated at
/// runtime. It will only take effect on client-side.
/// - **`initial_language_from_localstorage_to_cookie`** (_`false`_): Save the initial language of the user
/// from [local storage] to a cookie. Can be a literal boolean or an expression that will be evaluated at
/// runtime. It will only take effect on client-side.
/// - **`initial_language_from_navigator`** (_`false`_): Load the initial language of the user
/// from [`navigator.languages`] if not found in [local storage]. Can be a literal boolean or an
/// expression that will be evaluated at runtime. It will only take effect on client-side.
/// - **`initial_language_from_navigator_to_localstorage`** (_`false`_): Save the initial language of the user
/// from [`navigator.languages`] to [local storage]. Can be a literal boolean or an expression that will be
/// evaluated at runtime. It will only take effect on client-side.
/// - **`initial_language_from_navigator_to_cookie`** (_`false`_): Save the initial language of the user
/// from [`navigator.languages`] to a cookie. Can be a literal boolean or an expression that will be evaluated
/// at runtime. It will only take effect on client-side.
/// - **`initial_language_from_accept_language_header`** (_`false`_): Load the initial language of the user
/// from the `Accept-Language` header. Can be a literal boolean or an expression that will be evaluated at
/// runtime. It will only take effect on server-side.
/// - **`cookie_name`** (_`"lf-lang"`_): The cookie name to manage language in a cookie. Can be a literal string or an
/// expression that will be evaluated at runtime. It will take effect on client-side and server side.
/// - **`cookie_attrs`** (_`""`_): The [attributes][cookie-attributes] to set in the cookie. Can be a literal string or an expression
/// that will be evaluated at runtime. For example, `"SameSite=Strict; Secure; path=/; max-age=600"`.
/// It will take effect on client-side.
/// - **`initial_language_from_cookie`** (_`false`_): Load the initial language of the user from a cookie.
/// Can be a literal boolean or an expression that will be evaluated at runtime. It will take effect on client-side
/// and server side.
/// - **`initial_language_from_cookie_to_localstorage`** (_`false`_): Save the initial language of the user
/// from the cookie to [local storage]. Can be a literal boolean or an expression that will be evaluated at runtime.
/// - **`set_language_to_cookie`** (_`false`_): Save the language of the user to a cookie when setting the language.
/// Can be a literal boolean or an expression that will be evaluated at runtime. It will only take effect on client-side.
/// - **`initial_language_from_system`** (_`false`_): Load the initial language of the user from the system language
/// on non wasm targets. Can be a literal boolean or an expression that will be evaluated at runtime. It will only
/// take effect on client side of desktop applications. The feature `system` must be enabled to use this option.
/// - **`initial_language_from_system_to_data_file`** (_`false`_): Save the initial language of the user from the system
/// language to a data file. Can be a literal boolean or an expression that will be evaluated at runtime.
/// It will only take effect on client side of desktop applications. The feature `system` must be enabled to use
/// this option.
/// - **`initial_language_from_data_file`** (_`false`_): Load the initial language of the user from a data file.
/// Can be a literal boolean or an expression that will be evaluated at runtime. It will only take effect on client
/// side of desktop applications. The feature `system` must be enabled to use this option.
/// - **`data_file_key`** (_`"leptos-fluent"`_): The key to store the language in the data file. Can be a literal string
/// or an expression that will be evaluated at runtime. It will only take effect on client side of desktop applications.
/// The feature `system` must be enabled to use this option.
/// - **`set_language_to_data_file`** (_`false`_): Save the language of the user to a data file when setting the language.
/// Can be a literal boolean or an expression that will be evaluated at runtime. It will only take effect on client side
/// of desktop applications. The feature `system` must be enabled to use this option.
///
/// [`fluent_templates::static_loader!`]: https://docs.rs/fluent-templates/latest/fluent_templates/macro.static_loader.html
/// [`once_cell:sync::Lazy`]: https://docs.rs/once_cell/latest/once_cell/sync/struct.Lazy.html
/// [`StaticLoader`]: https://docs.rs/fluent-templates/latest/fluent_templates/struct.StaticLoader.html
/// [`<html lang="...">` attribute]: https://developer.mozilla.org/es/docs/Web/HTML/Global_attributes/lang
/// [`<html dir="...">` attribute]: https://developer.mozilla.org/es/docs/Web/HTML/Global_attributes/dir
/// [local storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
/// [`navigator.languages`]: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages
/// [`leptos::create_effect`]: https://docs.rs/leptos/latest/leptos/fn.create_effect.html
/// [cookie-attributes]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie
/// See the reference with all the parameters explained in detail at
/// https://mondeja.github.io/leptos-fluent/leptos_fluent.html
#[proc_macro]
pub fn leptos_fluent(
input: proc_macro::TokenStream,
45 changes: 15 additions & 30 deletions leptos-fluent-macros/src/loader.rs
Original file line number Diff line number Diff line change
@@ -355,9 +355,7 @@ impl Parse for I18nLoader {
let mut initial_language_from_data_file_expr: Option<
syn::Expr,
> = None;
#[cfg(feature = "system")]
let mut data_file_key_str: Option<syn::LitStr> = None;
#[cfg(feature = "system")]
let mut data_file_key_expr: Option<syn::Expr> = None;

while !fields.is_empty() {
@@ -586,14 +584,14 @@ impl Parse for I18nLoader {
),
));
}
} else if k == "initial_language_from_system_to_data_file" {
} else if k == "initial_language_from_data_file" {
#[cfg(feature = "system")]
{
if let Some(err) = parse_litbool_or_expr_param(
&fields,
&mut initial_language_from_system_to_data_file_bool,
&mut initial_language_from_system_to_data_file_expr,
"initial_language_from_system_to_data_file",
&mut initial_language_from_data_file_bool,
&mut initial_language_from_data_file_expr,
"initial_language_from_data_file",
) {
return Err(err);
}
@@ -604,20 +602,20 @@ impl Parse for I18nLoader {
return Err(syn::Error::new(
k.span(),
concat!(
"The parameter 'initial_language_from_system_to_data_file' of",
"The parameter 'initial_language_from_data_file' of",
" leptos_fluent! macro requires the feature",
" 'system' enabled.",
),
));
}
} else if k == "set_language_to_data_file" {
} else if k == "initial_language_from_system_to_data_file" {
#[cfg(feature = "system")]
{
if let Some(err) = parse_litbool_or_expr_param(
&fields,
&mut set_language_to_data_file_bool,
&mut set_language_to_data_file_expr,
"set_language_to_data_file",
&mut initial_language_from_system_to_data_file_bool,
&mut initial_language_from_system_to_data_file_expr,
"initial_language_from_system_to_data_file",
) {
return Err(err);
}
@@ -628,20 +626,20 @@ impl Parse for I18nLoader {
return Err(syn::Error::new(
k.span(),
concat!(
"The parameter 'set_language_to_data_file' of",
"The parameter 'initial_language_from_system_to_data_file' of",
" leptos_fluent! macro requires the feature",
" 'system' enabled.",
),
));
}
} else if k == "initial_language_from_data_file" {
} else if k == "set_language_to_data_file" {
#[cfg(feature = "system")]
{
if let Some(err) = parse_litbool_or_expr_param(
&fields,
&mut initial_language_from_data_file_bool,
&mut initial_language_from_data_file_expr,
"initial_language_from_data_file",
&mut set_language_to_data_file_bool,
&mut set_language_to_data_file_expr,
"set_language_to_data_file",
) {
return Err(err);
}
@@ -652,14 +650,13 @@ impl Parse for I18nLoader {
return Err(syn::Error::new(
k.span(),
concat!(
"The parameter 'initial_language_from_data_file' of",
"The parameter 'set_language_to_data_file' of",
" leptos_fluent! macro requires the feature",
" 'system' enabled.",
),
));
}
} else if k == "data_file_key" {
#[cfg(feature = "system")]
{
if let Some(err) = parse_litstr_or_expr_param(
&fields,
@@ -670,18 +667,6 @@ impl Parse for I18nLoader {
return Err(err);
}
}

#[cfg(not(feature = "system"))]
{
return Err(syn::Error::new(
k.span(),
concat!(
"The parameter 'data_file_key' of",
" leptos_fluent! macro requires the feature",
" 'system' enabled.",
),
));
}
} else {
return Err(syn::Error::new(
k.span(),
24 changes: 19 additions & 5 deletions leptos-fluent/README.md
Original file line number Diff line number Diff line change
@@ -161,6 +161,21 @@ fn App() -> impl IntoView {
url_param: "lang",
// Discover the initial language of the user from an URL parameter.
initial_language_from_url_param: true,

// Desktop applications (feature `system`)
// ---------------------------------------
// Set the initial language from the system locale.
initial_language_from_system: true,
// Set the discovered initial language of the user from
// the system locale to a data file.
initial_language_from_system_to_data_file: true,
// Get the initial language from a data file.
initial_language_from_data_file: true,
// Key to use to name the data file. Should be unique per
// application. By default is `"leptos-fluent"`.
data_file_key: "my-app",
// Set the language selected to a data file.
set_language_to_data_file: true,
}};

view! {
@@ -188,9 +203,7 @@ fn TranslatableComponent() -> impl IntoView {

#[component]
fn LanguageSelector() -> impl IntoView {
// Use `expect_i18n()` to get the current i18n context:
let i18n = expect_i18n();

// `expect_i18n()` to get the i18n context
// `i18n.languages` is a static array with the available languages
// `i18n.language.get()` to get the current language
// `lang.activate()` to set the current language
@@ -199,7 +212,7 @@ fn LanguageSelector() -> impl IntoView {
view! {
<fieldset>
{
move || i18n.languages.iter().map(|lang| {
move || expect_i18n().languages.iter().map(|lang| {
view! {
<div>
<input
@@ -227,6 +240,7 @@ fn LanguageSelector() -> impl IntoView {
- **Actix Web integration**: `actix`
- **Axum integration**: `axum`
- **Nightly toolchain**: `nightly`
- **Desktop applications**: `system`
- **JSON languages file**: `json` (enabled by default)
- **YAML languages file**: `yaml`
- **JSON5 languages file**: `json5`
@@ -240,7 +254,7 @@ fn LanguageSelector() -> impl IntoView {

[leptos]: https://leptos.dev/
[fluent-templates]: https://github.com/XAMPPRocky/fluent-templates
[quickstart]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
[quickstart]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
[examples]: https://github.com/mondeja/leptos-fluent/tree/master/examples
[book]: https://mondeja.github.io/leptos-fluent/
[documentation]: https://docs.rs/leptos-fluent
37 changes: 32 additions & 5 deletions leptos-fluent/src/lib.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
#![forbid(unsafe_code)]
#![cfg_attr(feature = "nightly", feature(fn_traits))]
#![cfg_attr(feature = "nightly", feature(unboxed_closures))]

//! [![Crates.io](https://img.shields.io/crates/v/leptos-fluent?logo=rust)](https://crates.io/crates/leptos-fluent)
//! [![License](https://img.shields.io/crates/l/leptos-fluent?logo=mit)](https://github.com/mondeja/leptos-fluent/blob/master/LICENSE.md)
//! [![Tests](https://img.shields.io/github/actions/workflow/status/mondeja/leptos-fluent/ci.yml?label=tests&logo=github)](https://github.com/mondeja/leptos-fluent/actions)
@@ -158,6 +159,21 @@
//! url_param: "lang",
//! // Discover the initial language of the user from an URL parameter.
//! initial_language_from_url_param: true,
//!
//! // Desktop applications (feature `system`)
//! // ---------------------------------------
//! // Set the initial language from the system locale.
//! initial_language_from_system: true,
//! // Set the discovered initial language of the user from
//! // the system locale to a data file.
//! initial_language_from_system_to_data_file: true,
//! // Get the initial language from a data file.
//! initial_language_from_data_file: true,
//! // Key to use to name the data file. Should be unique per
//! // application. By default is `"leptos-fluent"`.
//! data_file_key: "my-app",
//! // Set the language selected to a data file.
//! set_language_to_data_file: true,
//! }};
//!
//! view! {
@@ -185,9 +201,7 @@
//!
//! #[component]
//! fn LanguageSelector() -> impl IntoView {
//! // Use `expect_i18n()` to get the current i18n context:
//! let i18n = expect_i18n();
//!
//! // `expect_i18n()` to get the i18n context
//! // `i18n.languages` is a static array with the available languages
//! // `i18n.language.get()` to get the current language
//! // `lang.activate()` to set the current language
@@ -196,7 +210,7 @@
//! view! {
//! <fieldset>
//! {
//! move || i18n.languages.iter().map(|lang| {
//! move || expect_i18n().languages.iter().map(|lang| {
//! view! {
//! <div>
//! <input
@@ -224,6 +238,7 @@
//! - **Actix Web integration**: `actix`
//! - **Axum integration**: `axum`
//! - **Nightly toolchain**: `nightly`
//! - **Desktop applications**: `system`
//! - **JSON languages file**: `json` (enabled by default)
//! - **YAML languages file**: `yaml`
//! - **JSON5 languages file**: `json5`
@@ -237,7 +252,7 @@
//!
//! [leptos]: https://leptos.dev/
//! [fluent-templates]: https://github.com/XAMPPRocky/fluent-templates
//! [quickstart]: https://docs.rs/leptos-fluent/latest/leptos_fluent/macro.leptos_fluent.html
//! [quickstart]: https://mondeja.github.io/leptos-fluent/leptos_fluent.html
//! [examples]: https://github.com/mondeja/leptos-fluent/tree/master/examples
//! [book]: https://mondeja.github.io/leptos-fluent/
//! [documentation]: https://docs.rs/leptos-fluent
@@ -666,6 +681,18 @@ pub fn SsrHtmlTag() -> impl IntoView {
}

/// Reactive HTML tag to set attributes on SSR
///
/// Currently there is not a way to set the `dir` and `lang` attributes
/// of `<html>` tags on SSR. This components updates it on SSR. Must be
/// rendered in a view.
///
/// ```rust,ignore
/// use leptos_fluent::SsrHtmlTag;
///
/// view! {
/// <SsrHtmlTag/>
/// }
/// ```
#[component(transparent)]
#[cfg(not(feature = "ssr"))]
pub fn SsrHtmlTag() -> impl IntoView {}