{"id":27344775,"url":"https://github.com/2moe/glossa-dsl","last_synced_at":"2025-04-12T17:34:20.634Z","repository":{"id":281411489,"uuid":"945064977","full_name":"2moe/glossa-dsl","owner":"2moe","description":"Glossa-DSL: A domain-specific language designed exclusively for localization (L10n)","archived":false,"fork":false,"pushed_at":"2025-04-02T23:48:47.000Z","size":148,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-03T00:06:10.442Z","etag":null,"topics":["bincode","encoding","parser","rust","serde"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/2moe.png","metadata":{"files":{"readme":"docs/Readme.md","changelog":null,"contributing":null,"funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-08T15:22:59.000Z","updated_at":"2025-04-02T23:48:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"c02c49e9-94c2-4ef0-a11f-84015c9a6d57","html_url":"https://github.com/2moe/glossa-dsl","commit_stats":null,"previous_names":["2moe/tmpl-resolver","2moe/glossa-dsl"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fglossa-dsl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fglossa-dsl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fglossa-dsl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fglossa-dsl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/2moe","download_url":"https://codeload.github.com/2moe/glossa-dsl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248605499,"owners_count":21132182,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bincode","encoding","parser","rust","serde"],"created_at":"2025-04-12T17:33:34.146Z","updated_at":"2025-04-12T17:34:20.618Z","avatar_url":"https://github.com/2moe.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# glossa-DSL\n\n[![glossa-dsl.crate](https://img.shields.io/crates/v/glossa-dsl.svg?logo=rust\u0026logoColor=lightsalmon\u0026label=glossa-dsl)](https://crates.io/crates/glossa-dsl)\n[![Documentation](https://docs.rs/glossa-dsl/badge.svg)](https://docs.rs/glossa-dsl)\n\n[![Apache-2 licensed](https://img.shields.io/crates/l/glossa-dsl.svg?logo=apache)](../License)\n\n\u003e old_name: tmpl_resolver\n\nA **domain-specific language** designed exclusively for localization (L10n).\n\nIts syntax is similar to Mozilla Fluent, but it doesn't have as many features as Fluent.\n\nThe core logic is implemented using `nom` and can be used in `no_std`.\n\n\u003c!--\n## Core Types\n\n- [`Resolver`]: Main resolution engine\n\n### Private Types\n\n- [`Template`]: Enum representing template variants\n- [`Selector`]: Conditional branching structure\n- [`ResolverError`]: Comprehensive error reporting\n--\u003e\n\n## Key Concepts\n\n- **Templates**: Contain either direct text parts or conditional selectors\n- **Selectors**: Enable branch logic based on parameter values\n- **Variable Resolution**: Recursive resolution with context-aware lookup\n\n## Features\n\n- `[]`\n  - Minimal configuration for `no_std` use\n- [\"std\"]\n  - Enables standard library\n  - Uses ahash::HashMap for faster lookups\n- [\"serde\"]\n  - Adds serialization capabilities\n  - Enables template storage/transmission\n- [\"bincode\"]\n  - Efficient binary serialization\n- [\"toml\"]\n  - Enables `ResolverError::{DecodeTomlError, EncodeTomlError}`\n\n## Basic\n\n```rust\nuse glossa_dsl::{Resolver, error::ResolverResult};\n\nfn main() -\u003e ResolverResult\u003c()\u003e {\n  let resolver: Resolver = [\n      (\"h\", \"Hello\"),\n      (\"greeting\", \"{h} { $name }! Today is {$day}.\")\n    ]\n    .try_into()?;\n\n  let ctx = [(\"name\", \"Alice\"), (\"day\", \"Sunday\")];\n\n  let result = resolver.get_with_context(\"greeting\", \u0026ctx)?;\n  assert_eq!(result, \"Hello Alice! Today is Sunday.\");\n  Ok(())\n}\n```\n\n## Conditional Logic\n\n```rust\nuse glossa_dsl::{Resolver, error::ResolverResult};\n\nfn main() -\u003e ResolverResult\u003c()\u003e {\n  let selector_msg = [(\n    \"message\",\n    r#\"\n    $status -\u003e\n      [success] Operation succeeded!\n      [error] Error occurred!\n      *[default] Unknown status: {$status}\n    \"#\n  )];\n\n  let resolver: Resolver = selector_msg.try_into()?;\n\n  let success_msg = resolver.get_with_context(\"message\", \u0026[(\"status\", \"success\")])?;\n\n  assert_eq!(success_msg, \"Operation succeeded!\");\n  Ok(())\n}\n```\n\n## Escape\n\n- `\"{{ a   }}\"` =\u003e `\"a\"`\n- `\"{{{a}}}\"` =\u003e `\"a\"`\n- `\"{{{{  a  }}}}\"` =\u003e `\"a\"`\n- `\"{{    {a}    }}\"` =\u003e `\"{a}\"`\n- `\"{{a}\"` =\u003e ❌ nom Error, code: take_until\n- `\"{{{    {{a}}    }}}\"` =\u003e `\"{{a}}\"`\n- `\"{{{    {{ a }}    }}}\"` =\u003e `\"{{ a }}\"`\n- `\"{{{ {{a} }}}\"` =\u003e `\"{{a}\"`\n\n```rust\nuse glossa_dsl::{error::ResolverResult, Resolver};\n\nfn main() -\u003e ResolverResult\u003c()\u003e {\n  let resolver: Resolver = [\n    (\"h\", \"Hello { $name }\"),\n    (\"how_are_you\", \"How Are You\"),\n    (\"greeting\", \"{h}!{{ how_are_you }}? {{    {$name} }}\"),\n  ]\n  .try_into()?;\n\n  let ctx = [(\"name\", \"Alice\")];\n\n  let result = resolver.get_with_context(\"greeting\", \u0026ctx)?;\n  assert_eq!(result, \"Hello Alice!how_are_you? {$name}\");\n  Ok(())\n}\n```\n\n## Real World Examples\n\nAdd dependencies\n\n```sh\ncargo add toml tap anyhow\ncargo add glossa-dsl --features=std,serde,toml\n```\n\n### Emoji\n\nWe can use emoji as ~~variable~~ identifier name.\n\n---\n\ntoml:\n\n```toml\n\"🐱\" = \"ฅ(°ω°ฅ)\"\nhello = \"Hello {🐱}\"\n```\n\n1. `hello` references `{🐱}`\n2. expanding `hello`\n3. we would get `\"Hello ฅ(°ω°ฅ)\"`.\n\nrust:\n\n```rust\nlet text = res.try_get(\"hello\")?;\nassert_eq!(text, \"Hello ฅ(°ω°ฅ)\");\n```\n\n---\n\ntoml:\n\n```toml\nhello = \"Hello {$🐱}\"\n```\n\n\u003e `$🐱` means that its value is passed in externally.\n\nrust:\n\n```rust\nlet text = res.get_with_context(\"hello\", \u0026[(\"🐱\", \"QwQ\")])?;\nassert_eq!(text, \"Hello QwQ\");\n```\n\n---\n\n```rust\nuse tap::Pipe;\nuse glossa_dsl::{error::ResolverResult, Resolver, resolver::AHashRawMap};\n\nfn main() -\u003e ResolverResult\u003c()\u003e {\n  let res: Resolver = r##\"\n      meow = \"喵\"\n      \"🐱\" = \"{ meow } ฅ(°ω°ฅ)\"\n\n      \"问候\" = \"\"\"\n        $period -\u003e\n          [morning] 早安{🐱}\n          [night] 晚安{🐱}\n          *[other] {$period}好\n      \"\"\"\n\n      \"称谓\" = \"\"\"\n      $gender -\u003e\n        *[male] 先生\n        [female] 女士\n      \"\"\"\n\n      greeting = \"{ 问候 }！{ $name }{ 称谓 }。\"\n    \"##\n    .pipe(toml::from_str::\u003cAHashRawMap\u003e)?\n    .try_into()?;\n\n  let get_text = |ctx| res.get_with_context(\"greeting\", ctx);\n\n  let text = [\n    (\"period\", \"morning\"),\n    (\"name\", \"Young\"),\n    (\"gender\", \"unknown\"),\n  ]\n  .as_ref()\n  .pipe(get_text)?;\n\n  assert_eq!(text, \"早安喵 ฅ(°ω°ฅ)！Young先生。\");\n  assert_eq!(res.try_get(\"🐱\")?, \"喵 ฅ(°ω°ฅ)\");\n\n  Ok(())\n}\n```\n\n### Simple L10n Message\n\n```rust\nuse anyhow::Result as AnyResult;\nuse tap::{Pipe, TryConv};\nuse glossa_dsl::{Resolver, resolver::AHashRawMap};\n\nconst EN_TOML: \u0026str = r#\"\n  num-to-en = \"\"\"\n    $num -\u003e\n      [0] zero\n      [1] one\n      [2] two\n      [3] three\n      *[other] {$num}\n  \"\"\"\n\n  unread_msg = \"unread message\"\n\n  unread-count = \"\"\"\n    $num -\u003e\n      [0] No {unread_msg}s.\n      [1] You have { num-to-en } {unread_msg}.\n      *[other] You have { num-to-en } {unread_msg}s.\n  \"\"\"\n\n  show-unread-messages-count = \"{unread-count}\"\n\"#;\n\nconst ZH_TOML: \u0026str = r#\"\n  \"阿拉伯数字转汉字\" = \"\"\"\n    $num -\u003e\n      [0] 〇\n      [1] 一\n      [2] 二\n      [3] 三\n      *[其他] {$num}\n  \"\"\"\n\n  \"未读msg\" = \"未读消息\"\n\n  \"显示未读消息数量\" = \"\"\"\n    $num -\u003e\n        [0] 没有{ 未读msg }。\n        [2] 您有两条{ 未读msg }。\n       *[其他] 您有{ 阿拉伯数字转汉字 }条{ 未读msg }。\n  \"\"\"\n\n  show-unread-messages-count = \"{显示未读消息数量}\"\n\"#;\n\nfn main() -\u003e AnyResult\u003c()\u003e {\n  let get_text = |lang| -\u003e AnyResult\u003c_\u003e {\n    match lang {\n      \"zh\" =\u003e ZH_TOML,\n      _ =\u003e EN_TOML,\n    }\n    .pipe(toml::from_str::\u003cAHashRawMap\u003e)?\n    .try_conv::\u003cResolver\u003e()?\n    .pipe(|r| {\n      move |num_str| {\n        r.get_with_context(\"show-unread-messages-count\", \u0026[(\"num\", num_str)])\n      }\n    })\n    .pipe(Ok)\n  };\n\n  let get_en_text = get_text(\"en\")?;\n  assert_eq!(get_en_text(\"0\")?, \"No unread messages.\");\n  assert_eq!(get_en_text(\"1\")?, \"You have one unread message.\");\n  assert_eq!(get_en_text(\"2\")?, \"You have two unread messages.\");\n  assert_eq!(get_en_text(\"100\")?, \"You have 100 unread messages.\");\n\n  let get_zh_text = get_text(\"zh\")?;\n  assert_eq!(get_zh_text(\"0\")?, \"没有未读消息。\");\n  assert_eq!(get_zh_text(\"1\")?, \"您有一条未读消息。\");\n  assert_eq!(get_zh_text(\"2\")?, \"您有两条未读消息。\");\n  assert_eq!(get_zh_text(\"100\")?, \"您有100条未读消息。\");\n\n  Ok(())\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2moe%2Fglossa-dsl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F2moe%2Fglossa-dsl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2moe%2Fglossa-dsl/lists"}