diff --git a/.gitattributes b/.gitattributes index be4199e9..b691f0f3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88cfba58..23a68c21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a8d73be..e02faac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 `` 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 `` 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 diff --git a/README.md b/README.md index 2122b9b5..7b1a7aba 100644 --- a/README.md +++ b/README.md @@ -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! {
{ - move || i18n.languages.iter().map(|lang| { + move || expect_i18n().languages.iter().map(|lang| { view! {
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 diff --git a/book/book.toml b/book/book.toml index ceb0d64e..27c33f0c 100644 --- a/book/book.toml +++ b/book/book.toml @@ -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"] diff --git a/book/mdbook-admonish.css b/book/mdbook-admonish.css new file mode 100644 index 00000000..960b806c --- /dev/null +++ b/book/mdbook-admonish.css @@ -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,'); + -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,'); + 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,"); +} + +:root { + --md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,"); +} + +: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); +} diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index f45b768f..d60c465c 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -12,3 +12,7 @@ - [Languages](./languages.md) - [Checking translations](./checking-translations.md) - [FAQs](./faqs.md) + +# Reference + +- [`leptos_fluent!`](./leptos_fluent.md) diff --git a/book/src/basic-usage.md b/book/src/basic-usage.md index 80c1e9a3..0adca733 100644 --- a/book/src/basic-usage.md +++ b/book/src/basic-usage.md @@ -1,5 +1,9 @@ # Basic usage + + +## 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 .rs:ln, you access a signal or memo (defined at .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,32 +195,35 @@ 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() @@ -225,7 +231,7 @@ 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 diff --git a/book/src/checking-translations.md b/book/src/checking-translations.md index b81ee02d..f6f824b8 100644 --- a/book/src/checking-translations.md +++ b/book/src/checking-translations.md @@ -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 diff --git a/book/src/faqs.md b/book/src/faqs.md index 61708a52..da6a9801 100644 --- a/book/src/faqs.md +++ b/book/src/faqs.md @@ -2,24 +2,27 @@ # FAQs + + ### 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 `` component? +### Why examples don't use [``] component? -There are some cases in which the `For` component is not reproducible between +```admonish bug +There are some cases in which the [``] 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 `` component is secure on CSR contexts and -`leptos_fluent::Language`s implement `Hash` and `Eq` traits to be +In any case, the [``] 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 `` 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! { + + + } +} + +#[component] +fn LanguageSelector() -> impl IntoView { ... } +``` + +[``]: 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 diff --git a/book/src/install.md b/book/src/install.md index 50ccafe5..043d187d 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -1,5 +1,7 @@ # Installation + + ## CSR For client side rendering apps you only need to install **leptos-fluent** and @@ -55,6 +57,51 @@ 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` @@ -62,9 +109,10 @@ watch-additional-files = ["locales"] - **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 diff --git a/book/src/languages.md b/book/src/languages.md index 83197d62..68a645f4 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -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 diff --git a/book/src/leptos_fluent.md b/book/src/leptos_fluent.md new file mode 100644 index 00000000..ad226ecd --- /dev/null +++ b/book/src/leptos_fluent.md @@ -0,0 +1,555 @@ + + +# `leptos_fluent!` + +The `leptos_fluent!` macro is used to load the translations and set the current +locale. It is used in the root component of the application. + +```rust +leptos_fluent! {{ + locales: "./locales", + translations: [TRANSLATIONS], +}} +``` + + + +## Common configurations + +### CSR | Local storage from navigator + +```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, +}} +``` + +### CSR + SSR | Cookie from navigator and header + +```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, +}} +``` + + + +### featsystem | Data files on Desktop applications + + + +```rust +leptos_fluent! {{ + locales: "./locales", + translations: [TRANSLATIONS], + + initial_language_from_system: true, + initial_language_from_system_to_data_file: true, + initial_language_from_data_file: true, + set_language_to_data_file: true, + data_file_key: "system-language-example", +}} +``` + +## Parameters + +### `translations` + +Set the translations to be used in the application. It must be a reference +to a static array of [`fluent_templates::static_loader!`] instances. + +```rust +use fluent_templates::static_loader; +use leptos_fluent::leptos_fluent; + +static_loader! { + pub static TRANSLATIONS = { + locales: "./locales", + fallback_language: "en", + }; +} + +leptos_fluent! {{ + locales: "./locales", + translations: [TRANSLATIONS], + // ^^^^^^^^^^^^^^^^^^^^^^^^^ +}} +``` + +Must be the same identifier used in the [`fluent_templates::static_loader!`] +macro, which returns an [`once_cell:sync::Lazy`] variable. + +### `locales` + +Set the path to the locales directory which contain the Fluent +files for the translations. Must be relative to the _Cargo.toml_ file, the same +used in the [`fluent_templates::static_loader!`] macro. + +```rust +leptos_fluent! {{ + locales: "./locales", + // ^^^^^^^^^^^^^^^^^ + translations: [TRANSLATIONS], +}} +``` + +### `core_locales` + +Common locale resources that are shared across all locales. +Must be relative to the _Cargo.toml_ file, the same +used in the [`fluent_templates::static_loader!`] macro: + +```rust +static_loader! { + pub static TRANSLATIONS = { + locales: "./locales", + core_locales: "./locales/core", + }; +} + +leptos_fluent! {{ + locales: "./locales", + core_locales: "./locales/core", + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + translations: [TRANSLATIONS], +}} +``` + +### `languages` + +Path to a file containing the list of languages supported by the application. +Must be relative to the _Cargo.toml_ file. + +```rust +leptos_fluent! {{ + locales: "./locales", + languages: "./locales/languages.json", + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + translations: [TRANSLATIONS], +}} +``` + +```admonish tip +See [**4. Languages**](https://mondeja.github.io/leptos-fluent/languages.html) +``` + +The languages file should contain an array of arrays where each inner array +contains a language identifier and a language name, respectively, at least. + +The language identifier should be a valid language tag, such as `en-US`, `en`, +`es-ES`, etc. 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 [`` 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"] +] +``` + +### `check_translations` + +Check the translations at compile time. It is useful to ensure that all +translations are correct and that there are no missing translations. + +Must be a glob relative to the _Cargo.toml_ file. For single crate projects: + +```rust +leptos_fluent! {{ + locales: "./locales", + translations: [TRANSLATIONS], + check_translations: "./src/**/*.rs", + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +}} +``` + +For workspace projects: + +```rust +leptos_fluent! {{ + locales: "./locales", + translations: [TRANSLATIONS], + check_translations: "../{app,components}/src/**/*.rs", + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +}} +``` + +### CSR | `sync_html_tag_lang` + +Synchronize the global [`` attribute] with current language +using [`leptos::create_effect`]. Can be a literal boolean or an expression +that will be evaluated at runtime. + +```rust +leptos_fluent! {{ + // ... + sync_html_tag_lang: true, +}} +``` + +### CSR | `sync_html_tag_dir` + +Synchronize the global [`` attribute] with current language +using [`leptos::create_effect`]. + +Can be a literal boolean or an expression that will be evaluated at runtime. + +```rust +leptos_fluent! {{ + // ... + sync_html_tag_dir: true, +}} +``` + +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"`. + +### CSR + SSR | `url_param` + +Set the name of the URL parameter that will be used to manage the current +language. By default it is `"lang"`. + +```rust +leptos_fluent! {{ + // ... + url_param: "lang", +}} +``` + +### CSR + SSR | `initial_language_from_url_param` + +Set the initial language from the URL parameter. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_url_param: true, +}} +``` + +### CSR | `initial_language_from_url_param_to_localstorage` + +Set the initial language from the URL parameter and save it in the local storage. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_url_param_to_localstorage: true, +}} +``` + +### CSR | `initial_language_from_url_param_to_cookie` + +Set the initial language from the URL parameter and save it in a cookie. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_url_param_to_cookie: true, +}} +``` + +### CSR | `set_language_to_url_param` + +Set the current language to the URL parameter. + +```rust +leptos_fluent! {{ + // ... + set_language_to_url_param: true, +}} +``` + +### CSR | `localstorage_key` + +Key to manage the current language in the local storage. By default it is +`"lang"`. + +```rust +leptos_fluent! {{ + // ... + localstorage_key: "lang", +}} +``` + +### CSR | `initial_language_from_localstorage` + +Get the initial language from the local storage. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_localstorage: true, +}} +``` + +### CSR | `initial_language_from_localstorage_to_cookie` + +Get the initial language from the local storage and save it in a cookie. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_localstorage_to_cookie: true, +}} +``` + +### CSR | `set_language_to_localstorage` + +Set the current language to the local storage. + +```rust +leptos_fluent! {{ + // ... + set_language_to_localstorage: true, +}} +``` + +### CSR | `initial_language_from_navigator` + +Get the initial language from [`navigator.languages`]. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_navigator: true, +}} +``` + +### CSR | `initial_language_from_navigator_to_localstorage` + +Get the initial language from [`navigator.languages`] and save it in the [local storage]. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_navigator_to_localstorage: true, +}} +``` + +### CSR | `initial_language_from_navigator_to_cookie` + +Get the initial language from [`navigator.languages`] and save it in a cookie. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_navigator_to_cookie: true, +}} +``` + +### SSR | `initial_language_from_accept_language_header` + +Get the initial language from the [`Accept-Language`] header. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_accept_language_header: true, +}} +``` + +### CSR + SSR | `cookie_name` + +Name of the cookie that will be used to manage the current language. By default +it is `"lf-lang"`. + +```rust +leptos_fluent! {{ + // ... + cookie_name: "lang", +}} +``` + +### CSR | `cookie_attrs` + +[Cookie attributes] to set on the language cookie. By default it is `""` (empty). + +```rust +leptos_fluent! {{ + // ... + cookie_attrs: "SameSite=Strict; Secure", +}} +``` + +If the passed value is an expression the cookie will not be validated at +compile time: + +```rust +let attrs = "SameSite=Strict; Secure; MyCustomCookie=value" +leptos_fluent! {{ + // ... + cookie_attrs: attrs, +}} +``` + +### CSR + SSR | `initial_language_from_cookie` + +Get the initial language from the cookie. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_cookie: true, +}} +``` + +### CSR | `initial_language_from_cookie_to_localstorage` + +Get the initial language from the cookie and save it in the [local storage]. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_cookie_to_localstorage: true, +}} +``` + +### CSR | `set_language_to_cookie` + +Set the current language to the cookie. + +```rust +leptos_fluent! {{ + // ... + set_language_to_cookie: true, +}} +``` + + + +### featsystem | `initial_language_from_system` + + + +Get the initial language from the system. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_system: true, +}} +``` + + + +### featsystem | `initial_language_from_data_file` + + + +Get the initial language from a data file. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_data_file: true, +}} +``` + + + +### featsystem | `initial_language_from_system_to_data_file` + + + +Get the initial language from the system and save it in a data file. + +```rust +leptos_fluent! {{ + // ... + initial_language_from_system_to_data_file: true, +}} +``` + + + +### featsystem | `set_language_to_data_file` + + + +Set the current language to a data file. + +```rust +leptos_fluent! {{ + // ... + set_language_to_data_file: true, +}} +``` + + + +### featsystem | `data_file_key` + + + +Key to manage the current language in the data file. It should be unique +per application. By default it is `"leptos-fluent"`. + +```rust +leptos_fluent! {{ + // ... + data_file_key: "my-app", +}} +``` + +[`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 +[`` attribute]: https://developer.mozilla.org/es/docs/Web/HTML/Global_attributes/lang +[`` 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 +[`Accept-Language`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language diff --git a/book/src/strategies.md b/book/src/strategies.md index 7ea36974..fed3ebc2 100644 --- a/book/src/strategies.md +++ b/book/src/strategies.md @@ -1,21 +1,25 @@ -# Features +# Strategies All the features of the framework are optional, following a declarative "opt-in" configuration method. + + ## 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,6 +33,9 @@ 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 @@ -36,19 +43,25 @@ 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 | -| :------------------------------ | :--------------------------- | -| [``] attribute | `sync_html_tag_lang` | -| [``] attribute | `sync_html_tag_dir` | +| Side effect | [`leptos_fluent!`] | +| :------------------------------ | :------------------- | +| [``] attribute | `sync_html_tag_lang` | +| [``] attribute | `sync_html_tag_dir` | [``]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang [``]: 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 diff --git a/examples/ssr-hydrate-axum/Cargo.toml b/examples/ssr-hydrate-axum/Cargo.toml index fcdfccc8..d0735065 100644 --- a/examples/ssr-hydrate-axum/Cargo.toml +++ b/examples/ssr-hydrate-axum/Cargo.toml @@ -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"]] diff --git a/examples/system-gtk/README.md b/examples/system-gtk/README.md index 0ec58549..f61688e0 100644 --- a/examples/system-gtk/README.md +++ b/examples/system-gtk/README.md @@ -9,6 +9,6 @@ sudo apt install libgtk-4-dev ## Build and run ```sh -cd examples/gtk +cd examples/system-gtk cargo run ``` diff --git a/examples/system-gtk/src/main.rs b/examples/system-gtk/src/main.rs index 16ce6c51..6c80bbf5 100644 --- a/examples/system-gtk/src/main.rs +++ b/examples/system-gtk/src/main.rs @@ -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 diff --git a/leptos-fluent-macros/src/files_tracker.rs b/leptos-fluent-macros/src/files_tracker.rs index 52af011b..95abcb8b 100644 --- a/leptos-fluent-macros/src/files_tracker.rs +++ b/leptos-fluent-macros/src/files_tracker.rs @@ -5,35 +5,58 @@ pub(crate) fn build_files_tracker_quote( languages_path: &Option, core_locales_path: &Option, ) -> 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::().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::() + .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::() - .unwrap() } +#[cfg(not(feature = "nightly"))] fn escape_string(s: &str) -> String { s.replace('"', "\\\"") // Windows path separator diff --git a/leptos-fluent-macros/src/languages.rs b/leptos-fluent-macros/src/languages.rs index 80867ab5..8466d395 100644 --- a/leptos-fluent-macros/src/languages.rs +++ b/leptos-fluent-macros/src/languages.rs @@ -459,6 +459,8 @@ fn language_name_from_language_code( if use_country_code { let c = code.to_string().to_lowercase().replace('_', "-"); match c.as_str() { + // lang (3 letter) -> number + "jbo-001" => return "Lojban (World)", // lang (2 letter) -> country (2 letter) "af-na" => return "Afrikaans (Namibia)", "af-za" => return "Afrikaans (South Africa)", @@ -499,8 +501,8 @@ fn language_name_from_language_code( "bm-ml" => return "ߓߊߡߊߣߊߣߞߊߣ (ߞߊ߲ߞߊ߲)", "bn-bd" => return "বাংলা (বাংলাদেশ)", "bn-in" => return "বাংলা (ভারত)", - "bo-cn" => return "བོད་སྐད་ (ཀྲུང་ཧྭ)", - "bo-in" => return "བོད་སྐད་ (ཀྲུང་ཧྭ)", // TODO: The same as bo-cn, looks bad + "bo-cn" => return "བོད་སྐད་ (རྒྱ་ནག)", + "bo-in" => return "བོད་སྐད་ (཭ི་ཝི)", "br-fr" => return "Brezhoneg (Frañs)", "bs-ba" => return "Bosanski (Bosna i Hercegovina)", "ca-ad" => return "Català (Andorra)", @@ -821,7 +823,7 @@ fn language_name_from_language_code( "it-va" => return "Italiano (Città del Vaticano)", "iu-ca" => return "ᐃᓄᒃᑎᑐᑦ (Canada)", "ja-jp" => return "日本語 (日本)", - "jv-id" => return "Basa Jawa (Indonesia)", // TODO: check this + "jv-id" => return "ꦧꦱꦗꦮ (Indonesia)", // TODO: check this "ka-ge" => return "ქართული (საქართველო)", "ki-ke" => return "Gĩkũyũ (Kenya)", "kk-kz" => return "Қазақ тілі (Қазақстан)", @@ -831,7 +833,7 @@ fn language_name_from_language_code( "ko-kp" => return "한국어(북한)", "ks-in" => return "कश्मीरी (भारत)", "ku-tr" => return "Kurdî (Tirkiye)", - "kw-gb" => return "Cornish (United Kingdom)", // TODO: check this + "kw-gb" => return "Cornish (United Kingdom)", "ky-kg" => return "Кыргызстандык (Кыргызстан)", "lb-lu" => return "Lëtzebuergesch (Lëtzebuerg)", "lg-ug" => return "Luganda (Yuganda)", // TODO: check this @@ -868,7 +870,7 @@ fn language_name_from_language_code( "nn-no" => return "Norsk nynorsk (Noreg)", "nr-za" => return "isiNdebele (South Africa)", "ny-mw" => return "Chichewa (Malawi)", - "oc-fr" => return "Occitan (France)", // TODO: Check this + "oc-fr" => return "Occitan (France)", "os-ge" => return "Ирон æвзаг (Росси)", "om-et" => return "Afaan Oromoo (Itoophiyaa)", "om-ke" => return "Afaan Oromoo (Keeniyaa)", @@ -1013,8 +1015,8 @@ fn language_name_from_language_code( "bm-mli" => return "ߓߊߡߊߣߊߣߞߊߣ (ߞߊ߲ߞߊ߲)", "bn-bgd" => return "বাংলা (বাংলাদেশ)", "bn-ind" => return "বাংলা (ভারত)", - "bo-chn" => return "བོད་སྐད་ (ཀྲུང་ཧྭ)", - "bo-ind" => return "བོད་སྐད་ (ཀྲུང་ཧྭ)", // TODO: The same as bo-cn, looks bad + "bo-chn" => return "བོད་སྐད་ (རྒྱ་ནག)", + "bo-ind" => return "བོད་སྐད་ (཭ི་ཝི)", "br-fra" => return "Brezhoneg (Frañs)", "bs-bih" => return "Bosanski (Bosna i Hercegovina)", "ca-and" => return "Català (Andorra)", @@ -1343,7 +1345,7 @@ fn language_name_from_language_code( "io-001" => return "Ido", "iu-can" => return "ᐃᓄᒃᑎᑐᑦ (Canada)", "ja-jpn" => return "日本語 (日本)", - "jv-idn" => return "Basa Jawa (Indonesia)", // TODO: check this + "jv-idn" => return "ꦧꦱꦗꦮ (Indonesia)", // TODO: check this "ka-geo" => return "ქართული (საქართველო)", "ki-ken" => return "Gĩkũyũ (Kenya)", "kk-kaz" => return "Қазақ тілі (Қазақстан)", @@ -1353,7 +1355,7 @@ fn language_name_from_language_code( "ko-prk" => return "한국어(북한)", "ks-ind" => return "कश्मीरी (भारत)", "ku-tur" => return "Kurdî (Tirkiye)", - "kw-gbr" => return "Cornish (United Kingdom)", // TODO: check this + "kw-gbr" => return "Cornish (United Kingdom)", "ky-kgz" => return "Кыргызстандык (Кыргызстан)", "lb-lux" => return "Lëtzebuergesch (Lëtzebuerg)", "lg-uga" => return "Luganda (Yuganda)", // TODO: check this @@ -1390,7 +1392,7 @@ fn language_name_from_language_code( "nn-nor" => return "Norsk nynorsk (Noreg)", "nr-zaf" => return "isiNdebele (South Africa)", "ny-mwi" => return "Chichewa (Malawi)", - "oc-fra" => return "Occitan (France)", // TODO: Check this + "oc-fra" => return "Occitan (France)", "om-eth" => return "Afaan Oromoo (Itoophiyaa)", "om-ken" => return "Afaan Oromoo (Keeniyaa)", "or-ind" => return "ଓଡ଼ିଆ (ଭାରତ)", @@ -1501,119 +1503,218 @@ fn language_name_from_language_code( // Chakma language, an Indo-Aryan language spoken in Bangladesh // TODO: How is written "Bangladesh" in Chakma language? "cpp-bd" => return "𑄌𑄋𑄴𑄟𑄳𑄦 𑄞𑄌𑄴 (Bangladesh)", + "agq-CM" => return "Aghem (Cameroon)", + "ksf-CM" => return "Bafia (Cameroon)", + "bas-CM" => return "Ɓasaá (Cameroon)", + "dua-CM" => return "Duala (Cameroon)", + "ewo-CM" => return "Ewondo (Cameroon)", + "kkj-CM" => return "Kako (Cameroon)", + "nmg-CM" => return "Kwasio (Cameroon)", + "mgo-CM" => return "Metaʼ (Cameroon)", + "mua-CM" => return "Mundang (Cameroon)", + "nnh-CM" => return "Ngiemboon (Cameroon)", + "jgo-CM" => return "Ngomba (Cameroon)", + "yav-CM" => return "Yɔɔŋmbɔ (Cameroon)", + "moh-CA" => return "Mohawk (Canada)", + "kea-CV" => return "Kabuverdianu (Cabo Verde)", + "arn-CL" => return "Mapudungun (Chile)", + "yue-CN" => return "粤语 (中国)", + "byn-ER" => return "Blin (Eritrea)", + "gez-ER" => return "ግዕዝ (ኤርትራ)", + "tig-ER" => return "Tigre (Eritrea)", + "gez-ET" => return "ግዕዝ (ኤርትራ)", + "wal-ET" => return "Wolaytta (Ethiopia)", + "smn-FI" => return "anarâškielâ (Suopma)", + "gsw-FR" => return "Elsässisch (Frankreich)", + "ksh-DE" => return "Kölsch (Deutschland)", + "nds-DE" => return "Plattdüütsch (Deutschland)", + "dsb-DE" => return "Niedersorbisch (Deutschland)", + "hsb-DE" => return "Hornjoserbšćina (Němska)", + "gaa-GH" => return "Ga (Ghana)", + "kpe-GN" => return "Kpelle (Guinea)", + "nqo-GN" => return "N'Ko (Guinea)", + "yue-HK" => return "粤语 (中国)", + "brx-IN" => return "बोड़ो (भारत)", + "ccp-IN" => return "बोड़ो (भारत)", // TODO: same as brx-IN + "kok-IN" => return "कोंकणी (भारत)", + "mni-IN" => return "Manipuri (India)", + "sat-IN" => return "Santali (India)", + "ckb-IR" => return "کوردی (ایران)", + "mzn-IR" => return "مازِرونی (ایران)", + "lrc-IR" => return "لۊری شومالی (ایران)", + "ckb-IQ" => return "کوردی (ایران)", + "lrc-IQ" => return "لۊری شومالی (ایران)", + "syr-IQ" => return "ܣܘܪܝܝܐ (ܩܕܝܫܐ)", + "fur-IT" => return "Friulian (Italy)", // TODO: check + "scn-IT" => return "Sicilian (Italy)", // TODO: check + "ebu-KE" => return "Kĩembu (Kenya)", + "guz-KE" => return "Ekegusii (Kenya)", + "kln-KE" => return "Kalenjin (Kenya)", + "kam-KE" => return "Kikamba (Kenya)", + "luo-KE" => return "Dholuo (Kenya)", + "luy-KE" => return "Luyia (Kenya)", + "mas-KE" => return "Maa (Kenya)", + "mer-KE" => return "Kĩmĩrũ (Kenya)", + "saq-KE" => return "Kisampur (Kenya)", + "dav-KE" => return "Kitaita (Kenya)", + "teo-KE" => return "Kiteso (Kenya)", + "kpe-LR" => return "Kpelle (Liberia)", + "vai-LR" => return "ꕙꔤ (ꕞꔤꔫꕩ)", + "gsw-LI" => return "Elsässisch (Liechtenstein)", + "khq-ML" => return "Koyra ciini (Maali)", + "ses-ML" => return "Koyraboro senni (Maali)", + "mfe-MU" => return "kreol morisien (Moris)", + "tzm-MA" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "zgh-MA" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "shi-MA" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "mgh-MZ" => return "Makua (Moçambique)", + "seh-MZ" => return "sena (Moçambique)", + "naq-NA" => return "Khoekhoegowab (Namibiab)", + "nds-NL" => return "Plattdüütsch (Nederland)", + "twq-NE" => return "Tasawaq senni (Nižer)", + "dje-NE" => return "Zarmaciine (Nižer)", + "kaj-NG" => return "Kaje (Najeriya)", + "kcg-NG" => return "Tyap (Najeriya)", + "ceb-PH" => return "Cebuano (Pilipinas)", + "fil-PH" => return "Filipino (Pilipinas)", + "myv-RU" => return "эрзянь (Россия)", + "sah-RU" => return "саха тыла (Россия)", + "dyo-SN" => return "joola (Senegal)", + "nso-ZA" => return "Sesotho sa Leboa (Afrika Borwa)", + "nus-SS" => return "Thok Nath (SS)", + "ast-ES" => return "asturianu (España)", + "gsw-CH" => return "Elsässisch (Schweiz)", + "wae-CH" => return "Walser (Schweiz)", + "syr-SY" => return "ܣܘܪܝܝܐ (ܣܘܪܝܐ)", + "trv-TW" => return "Sakizaya (Taiwan)", + "asa-TZ" => return "Kipare (Tanzania)", + "bez-TZ" => return "Hibena (Tanzania)", + "lag-TZ" => return "Kɨlaangi (Tanzania)", + "jmc-TZ" => return "Kimachame (Tanzania)", + "kde-TZ" => return "Chimakonde (Tanzania)", + "mas-TZ" => return "Maa (Tanzania)", + "rof-TZ" => return "Kihorombo (Tanzania)", + "rwk-TZ" => return "Kiruwa (Tanzania)", + "sbp-TZ" => return "Ishisangu (Tanzania)", + "ksb-TZ" => return "Kishambaa (Tanzania)", + "vun-TZ" => return "Kyuk (Tanzania)", + "cgg-UG" => return "Rukiga (Uganda)", + "nyn-UG" => return "Runyankore (Uganda)", + "xog-UG" => return "Olusoga (Uganda)", + "teo-UG" => return "Kiteso (Uganda)", + "chr-US" => return "ᏣᎳᎩ (ᎠᎹᏂᏟ)", + "haw-US" => return "ʻŌlelo Hawaiʻi (ʻAmelika Hui Pū ʻIa)", + "lkt-US" => return "Lakȟólʼiyapi (Mílahaŋska Tȟamákȟočhe)", + "bem-ZM" => return "Ichibemba (Zambia)", // lang (3 letter) -> country (3 letter) "kab-dza" => return "θɐqβæjlɪθ (Asenǧaq n Dzayer)", "cpp-bgd" => return "𑄌𑄋𑄴𑄟𑄳𑄦 𑄞𑄌𑄴 (Bangladesh)", + "agq-CMR" => return "Aghem (Cameroon)", + "ksf-CMR" => return "Bafia (Cameroon)", + "bas-CMR" => return "Ɓasaá (Cameroon)", + "dua-CMR" => return "Duala (Cameroon)", + "ewo-CMR" => return "Ewondo (Cameroon)", + "kkj-CMR" => return "Kako (Cameroon)", + "nmg-CMR" => return "Kwasio (Cameroon)", + "mgo-CMR" => return "Metaʼ (Cameroon)", + "mua-CMR" => return "Mundang (Cameroon)", + "nnh-CMR" => return "Ngiemboon (Cameroon)", + "jgo-CMR" => return "Ngomba (Cameroon)", + "yav-CMR" => return "Yɔɔŋmbɔ (Cameroon)", + "moh-CAN" => return "Mohawk (Canada)", + "kea-CPV" => return "Kabuverdianu (Cabo Verde)", + "arn-CHL" => return "Mapudungun (Chile)", + "yue-CHN" => return "粤语 (中国)", + "byn-ERI" => return "Blin (Eritrea)", + "gez-ERI" => return "ግዕዝ (ኤርትራ)", + "tig-ERI" => return "Tigre (Eritrea)", + "gez-ETH" => return "ግዕዝ (ኤርትራ)", + "wal-ETH" => return "Wolaytta (Ethiopia)", + "smn-FIN" => return "anarâškielâ (Suopma)", + "gsw-FRA" => return "Elsässisch (Frankreich)", + "ksh-DEU" => return "Kölsch (Deutschland)", + "nds-DEU" => return "Plattdüütsch (Deutschland)", + "dsb-DEU" => return "Niedersorbisch (Deutschland)", + "hsb-DEU" => return "Hornjoserbšćina (Němska)", + "gaa-GHA" => return "Ga (Ghana)", + "kpe-GIN" => return "Kpelle (Guinea)", + "nqo-GIN" => return "N'Ko (Guinea)", + "yue-HKG" => return "粤语 (中国)", + "brx-IND" => return "बोड़ो (भारत)", + "ccp-IND" => return "बोड़ो (भारत)", // TODO: same as brx-IND + "kok-IND" => return "कोंकणी (भारत)", + "mni-IND" => return "Manipuri (India)", + "sat-IND" => return "Santali (India)", + "ckb-IRN" => return "کوردی (ایران)", + "mzn-IRN" => return "مازِرونی (ایران)", + "lrc-IRN" => return "لۊری شومالی (ایران)", + "ckb-IRQ" => return "کوردی (ایران)", + "lrc-IRQ" => return "لۊری شومالی (ایران)", + "syr-IRQ" => return "ܣܘܪܝܝܐ (ܩܕܝܫܐ)", + "fur-ITA" => return "Friulian (Italy)", // TODO: check + "scn-ITA" => return "Sicilian (Italy)", // TODO: check + "ebu-KEN" => return "Kĩembu (Kenya)", + "guz-KEN" => return "Ekegusii (Kenya)", + "kln-KEN" => return "Kalenjin (Kenya)", + "kam-KEN" => return "Kikamba (Kenya)", + "luo-KEN" => return "Dholuo (Kenya)", + "luy-KEN" => return "Luyia (Kenya)", + "mas-KEN" => return "Maa (Kenya)", + "mer-KEN" => return "Kĩmĩrũ (Kenya)", + "saq-KEN" => return "Kisampur (Kenya)", + "dav-KEN" => return "Kitaita (Kenya)", + "teo-KEN" => return "Kiteso (Kenya)", + "kpe-LBR" => return "Kpelle (Liberia)", + "vai-LBR" => return "ꕙꔤ (ꕞꔤꔫꕩ)", + "gsw-LIE" => return "Elsässisch (Liechtenstein)", + "khq-MLI" => return "Koyra ciini (Maali)", + "ses-MLI" => return "Koyraboro senni (Maali)", + "mfe-MUS" => return "kreol morisien (Moris)", + "tzm-MAR" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "zgh-MAR" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "shi-MAR" => return "ⵜⴰⵎⴰⵣⵉⵖⵜ (ⵍⵎⴰⵣⵉⵖ)", + "mgh-MOZ" => return "Makua (Moçambique)", + "seh-MOZ" => return "sena (Moçambique)", + "naq-NAM" => return "Khoekhoegowab (Namibiab)", + "nds-NLD" => return "Plattdüütsch (Nederland)", + "twq-NER" => return "Tasawaq senni (Nižer)", + "dje-NER" => return "Zarmaciine (Nižer)", + "kaj-NGA" => return "Kaje (Najeriya)", + "kcg-NGA" => return "Tyap (Najeriya)", + "ceb-PHL" => return "Cebuano (Pilipinas)", + "fil-PHL" => return "Filipino (Pilipinas)", + "myv-RUS" => return "эрзянь (Россия)", + "sah-RUS" => return "саха тыла (Россия)", + "dyo-SEN" => return "joola (Senegal)", + "nso-ZAF" => return "Sesotho sa Leboa (Afrika Borwa)", + "nus-SSD" => return "Thok Nath (SS)", + "ast-ESP" => return "asturianu (España)", + "gsw-CHE" => return "Elsässisch (Schweiz)", + "wae-CHE" => return "Walser (Schweiz)", + "syr-SYR" => return "ܣܘܪܝܝܐ (ܣܘܪܝܐ)", + "trv-TWN" => return "Sakizaya (Taiwan)", + "asa-TZA" => return "Kipare (Tanzania)", + "bez-TZA" => return "Hibena (Tanzania)", + "lag-TZA" => return "Kɨlaangi (Tanzania)", + "jmc-TZA" => return "Kimachame (Tanzania)", + "kde-TZA" => return "Chimakonde (Tanzania)", + "mas-TZA" => return "Maa (Tanzania)", + "rof-TZA" => return "Kihorombo (Tanzania)", + "rwk-TZA" => return "Kiruwa (Tanzania)", + "sbp-TZA" => return "Ishisangu (Tanzania)", + "ksb-TZA" => return "Kishambaa (Tanzania)", + "vun-TZA" => return "Kyuk (Tanzania)", + "cgg-UGA" => return "Rukiga (Uganda)", + "nyn-UGA" => return "Runyankore (Uganda)", + "xog-UGA" => return "Olusoga (Uganda)", + "teo-UGA" => return "Kiteso (Uganda)", + "chr-USA" => return "ᏣᎳᎩ (ᎠᎹᏂᏟ)", + "haw-USA" => return "ʻŌlelo Hawaiʻi (ʻAmelika Hui Pū ʻIa)", + "lkt-USA" => return "Lakȟólʼiyapi (Mílahaŋska Tȟamákȟočhe)", + "bem-ZMB" => return "Ichibemba (Zambia)", // Followed this table: https://www.fincher.org/Utilities/CountryLanguageList.shtml - // - // TODO: - // - Next dialects are iso639-3 codes. Investigate them: - // + 'agq-CM' and 'agq-CMR' - // + 'ksf-CM' and 'ksf-CMR' - // + 'bas-CM' and 'bas-CMR' - // + 'dua-CM' and 'dua-CMR' - // + 'ewo-CM' and 'ewo-CMR' - // + 'kkj-CM' and 'kkj-CMR' - // + 'nmg-CM' and 'nmg-CMR' - // + 'mgo-CM' and 'mgo-CMR' - // + 'mua-CM' and 'mua-CMR' - // + 'nnh-CM' and 'nnh-CMR' - // + 'jgo-CM' and 'jgo-CMR' - // + 'yav-CM' and 'yav-CMR' - // + 'moh-CA' and 'moh-CAN' - // + 'kea-CV' and 'kea-CPV' - // + 'arn-CL' and 'arn-CHL' - // + 'yue-CN' and 'yue-CHN' - // + 'byn-ER' and 'byn-ERI' - // + 'gez-ER' and 'gez-ERI' - // + 'tig-ER' and 'tig-ERI' - // + 'gez-ET' and 'gez-ETH' - // + 'wal-ET' and 'wal-ETH' - // + 'smn-FI' and 'smn-FIN' - // + 'gsw-FR' and 'gsw-FRA' - // + 'ksh-DE' and 'ksh-DEU' - // + 'nds-DE' and 'nds-DEU' - // + 'dsb-DE' and 'dsb-DEU' - // + 'hsb-DE' and 'hsb-DEU' - // + 'gaa-GH' and 'gaa-GHA' - // + 'kpe-GN' and 'kpe-GIN' - // + 'nqo-GN' and 'nqo-GIN' - // + 'yue-HK' and 'yue-HKG' - // + 'brx-IN' and 'brx-IND' - // + 'ccp-IN' and 'ccp-IND' - // + 'kok-IN' and 'kok-IND' - // + 'mni-IN' and 'mni-IND' - // + 'sat-IN' and 'sat-IND' - // + 'ckb-IR' and 'ckb-IRN' - // + 'mzn-IR' and 'mzn-IRN' - // + 'lrc-IR' and 'lrc-IRN' - // + 'ckb-IQ' and 'ckb-IRQ' - // + 'lrc-IQ' and 'lrc-IRQ' - // + 'syr-IQ' and 'syr-IRQ' - // + 'fur-IT' and 'fur-ITA' - // + 'scn-IT' and 'scn-ITA' - // + 'ebu-KE' and 'ebu-KEN' - // + 'guz-KE' and 'guz-KEN' - // + 'kln-KE' and 'kln-KEN' - // + 'kam-KE' and 'kam-KEN' - // + 'luo-KE' and 'luo-KEN' - // + 'luy-KE' and 'luy-KEN' - // + 'mas-KE' and 'mas-KEN' - // + 'mer-KE' and 'mer-KEN' - // + 'saq-KE' and 'saq-KEN' - // + 'dav-KE' and 'dav-KEN' - // + 'teo-KE' and 'teo-KEN' - // + 'kpe-LR' and 'kpe-LBR' - // + 'vai-LR' and 'vai-LBR' - // + 'gsw-LI' and 'gsw-LIE' - // + 'khq-ML' and 'khq-MLI' - // + 'ses-ML' and 'ses-MLI' - // + 'mfe-MU' and 'mfe-MUS' - // + 'tzm-MA' and 'tzm-MAR' - // + 'zgh-MA' and 'zgh-MAR' - // + 'shi-MA' and 'shi-MAR' - // + 'mgh-MZ' and 'mgh-MOZ' - // + 'seh-MZ' and 'seh-MOZ' - // + 'naq-NA' and 'naq-NAM' - // + 'nds-NL' and 'nds-NLD' - // + 'twq-NE' and 'twq-NER' - // + 'dje-NE' and 'dje-NER' - // + 'kaj-NG' and 'kaj-NGA' - // + 'kcg-NG' and 'kcg-NGA' - // + 'ceb-PH' and 'ceb-PHL' - // + 'fil-PH' and 'fil-PHL' - // + 'myv-RU' and 'myv-RUS' - // + 'sah-RU' and 'sah-RUS' - // + 'dyo-SN' and 'dyo-SEN' - // + 'nso-ZA' and 'nso-ZAF' - // + 'nus-SS' and 'nus-SSD' - // + 'ast-ES' and 'ast-ESP' - // + 'gsw-CH' and 'gsw-CHE' - // + 'wae-CH' and 'wae-CHE' - // + 'syr-SY' and 'syr-SYR' - // + 'trv-TW' and 'trv-TWN' - // + 'asa-TZ' and 'asa-TZA' - // + 'bez-TZ' and 'bez-TZA' - // + 'lag-TZ' and 'lag-TZA' - // + 'jmc-TZ' and 'jmc-TZA' - // + 'kde-TZ' and 'kde-TZA' - // + 'mas-TZ' and 'mas-TZA' - // + 'rof-TZ' and 'rof-TZA' - // + 'rwk-TZ' and 'rwk-TZA' - // + 'sbp-TZ' and 'sbp-TZA' - // + 'ksb-TZ' and 'ksb-TZA' - // + 'vun-TZ' and 'vun-TZA' - // + 'cgg-UG' and 'cgg-UGA' - // + 'nyn-UG' and 'nyn-UGA' - // + 'xog-UG' and 'xog-UGA' - // + 'teo-UG' and 'teo-UGA' - // + 'chr-US' and 'chr-USA' - // + 'haw-US' and 'haw-USA' - // + 'lkt-US' and 'lkt-USA' - // + 'jbo-001' - // + 'bem-ZM' and 'bem-ZMB' _ => {} } } diff --git a/leptos-fluent-macros/src/lib.rs b/leptos-fluent-macros/src/lib.rs index b51e71ec..a3e21b7c 100644 --- a/leptos-fluent-macros/src/lib.rs +++ b/leptos-fluent-macros/src/lib.rs @@ -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 [`` 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 [`` 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 [`` 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 -/// [`` attribute]: https://developer.mozilla.org/es/docs/Web/HTML/Global_attributes/lang -/// [`` 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, diff --git a/leptos-fluent-macros/src/loader.rs b/leptos-fluent-macros/src/loader.rs index 9463049f..8922698c 100644 --- a/leptos-fluent-macros/src/loader.rs +++ b/leptos-fluent-macros/src/loader.rs @@ -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 = None; - #[cfg(feature = "system")] let mut data_file_key_expr: Option = 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(), diff --git a/leptos-fluent/README.md b/leptos-fluent/README.md index 2122b9b5..7b1a7aba 100644 --- a/leptos-fluent/README.md +++ b/leptos-fluent/README.md @@ -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! {
{ - move || i18n.languages.iter().map(|lang| { + move || expect_i18n().languages.iter().map(|lang| { view! {
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 diff --git a/leptos-fluent/src/lib.rs b/leptos-fluent/src/lib.rs index 5c26c4de..f8db62df 100644 --- a/leptos-fluent/src/lib.rs +++ b/leptos-fluent/src/lib.rs @@ -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! { //!
//! { -//! move || i18n.languages.iter().map(|lang| { +//! move || expect_i18n().languages.iter().map(|lang| { //! view! { //!
//! impl IntoView { } /// Reactive HTML tag to set attributes on SSR +/// +/// Currently there is not a way to set the `dir` and `lang` attributes +/// of `` tags on SSR. This components updates it on SSR. Must be +/// rendered in a view. +/// +/// ```rust,ignore +/// use leptos_fluent::SsrHtmlTag; +/// +/// view! { +/// +/// } +/// ``` #[component(transparent)] #[cfg(not(feature = "ssr"))] pub fn SsrHtmlTag() -> impl IntoView {}