{"id":36399785,"url":"https://github.com/nscathic/safflower","last_synced_at":"2026-01-11T16:02:48.227Z","repository":{"id":329987379,"uuid":"1119623643","full_name":"nscathic/safflower","owner":"nscathic","description":"A localising tool with macros for static allocation of formatted text","archived":false,"fork":false,"pushed_at":"2026-01-01T16:37:31.000Z","size":96,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-02T22:50:16.246Z","etag":null,"topics":["language","localisation","localization"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nscathic.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-19T15:15:43.000Z","updated_at":"2026-01-01T16:37:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nscathic/safflower","commit_stats":null,"previous_names":["nscathic/safflower"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nscathic/safflower","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nscathic%2Fsafflower","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nscathic%2Fsafflower/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nscathic%2Fsafflower/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nscathic%2Fsafflower/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nscathic","download_url":"https://codeload.github.com/nscathic/safflower/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nscathic%2Fsafflower/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28312183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T14:58:17.114Z","status":"ssl_error","status_checked_at":"2026-01-11T14:55:53.580Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["language","localisation","localization"],"created_at":"2026-01-11T16:02:47.640Z","updated_at":"2026-01-11T16:02:48.218Z","avatar_url":"https://github.com/nscathic.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `safflower` \n*Statically-Allocated Fromat-Friendly Localising (Written Entirely in Rust)*\n\n[![Webiste](https://img.shields.io/badge/site-nscathic-blue)](https://nscathic.eu/projects/safflower.html)\n[![Crates.io Version](https://img.shields.io/crates/v/safflower)](https://crates.io/crates/safflower)\n[![docs](https://img.shields.io/docsrs/safflower?logo=rust)](https://docs.rs/safflower/latest/)\n[![Repo](https://img.shields.io/badge/github-repo-blue?logo=github)](https://github.com/nscathic/safflower)\n[![License](https://img.shields.io/badge/license-MIT-blue)](license.md)\n\n`safflower` aims to provide a no-fuss text localiser with minimum runtime overhead.\nIt does so in two main ways: \n1) all the text is read, checked, and parsed at compile time; and\n2) all localisations of the text are available at the same time.\n\nBelow is a description of how it works. If you just want to see it in use, skip to [Accessing text](#accessing-text).\n\n## Loading\nThe `load!` macro reads a text file from the path provided. If there are any errors in the file (mainly from formatting), they are caught at compile time. This removes the need for runtime error handling (path exists, file can be read, contents can be parsed, key exists, etc.).\n\nThe macro generates a module `localisation` with a few things:\n- an enum for your locales;\n- a const array of all available locales (for e.g. iteration);\n- a function for every key in your file, to the text;\n- a static `Mutex` to keep the currently set locale.\n\n### Locale choice\nThe user does not need to hold onto any state, since the locale setting is kept in a static `Mutex`, accessible raw or through `localisation::set_locale()` and `localisation::get_locale()`. During the `load!` macro, it is set to the first declared locale.\n\nI'm not a fan of global variables, but I think this makes sense here: we don't expect it to change a lot, maybe not at all during the lifetime of the program, but every single piece of text depends on it. \n\nThis also means that the text getting functions are *thread blocking*, but only for the short while it takes to access it -- `Locale` implements `Copy` to minimise fuzz. If someone has a better solution, feel free to say so.\n\n\u003e***Note***\n\u003e\n\u003eIn a small benchmark on an old laptop, one million calls to `text!` for a 256-byte string took about ~13 ms (in release mode), whereas one million calls to `format!` for the same text took about ~2 ms. \n\n### File structure\nThe file structure is designed to attempt to find a balance between ease-of-use and ease-of-parsing (which affects compile time). A minimal example:\n```toml\n# Declare english and italian as locales\n!locales en it\n\n# Define an entry with key 'greet'\ngreet:\n    # Define english text\n    en \"Hi!\"\n    # Define italian text\n    it \"Ciao!\"\n\n```\n#### Comments\nComments are parsed as anything starting with a `#` and ending with a newline. \nThere are two exceptions: \n1) comments may not be inserted between a locale and its value; and\n2) comments are not parsed inside values.\n\n#### Config\nA config line is a `!` followed y a key and one or more values, all on the same line. \n\nThese are the currently available config keys:\n- `!locales` is used to declare locales, separated by whitespace. This must occur before any text entries using them.\n\n- `!include` appends one or more files' contents to be parsed, in the order read.\n\n- `!scope` declares everything following it to be in a scope. This translates directly to a structure of `pub mod`s, in a pretty straightforward way. A file included after `!scope a` is genereated completely inside `pub mod a { .. }`. Note that declaring several scopes in one file will *not* nest them.\n\n#### Entries\nThe rest of the file must contain entries, each is a key followed by a colon `:` and at least one pair of a locale and a quote-enclosed value. \n\nKeys and locales must both start with an ASCII alphabetical character and only contain ASCII alphanumerics, hyphens `-`, and underscores `_`, but are case-insensitve (`_` is considered to be the lowercase version of `-`).\n\n#### Values and formatting\nA value may contain any valid UTF-8. Quotes and curly braces may be escaped with a backslash `\\`. The strings are passed wholesale to `format!`, and so any regular formatting will work, e.g. `\"Hello {name}, I'm {dist:.2} light-years away.\"`.\n\n\u003e ***Note***\n\u003e\n\u003e You may use unnamed parameters like `{0}` or `{}`, but as they need proper names to be passed into functions, they will be renamed to `arg0` etc. This means that using both `{0}` and `arg0` will create overlap. I don't foresee this being a problem for anyone, though.\n\n## Accessing text\nThe `text!` macro is designed to fit in as a replacement for `format!`, where the string literal is replaced by a key from the loaded file. It matches on the locale to choose which localised text to format, inserting arguments as `format!` would.\n\nWhen writing the texts in the file, arguments may be entered just like for `format!`. \n\n### Example:\n`strings.txt`\n```toml\n!locales en\ntext:\n    en \"Hello!\"\n\ntext-with-args: \n    en \"Hi {name}, I'm {me}.\"\n```\nrust-side:\n```rust\nuse safflower::{text, load};\n\nload!(\"strings.txt\");\n\nlet foo = \"foo\";\nlet bar = 42;\n\nassert_eq!(\n    text!(text),\n    format!(\"Hello!\"),\n);\n\nassert_eq!(\n    text!(text_with_args, foo, bar),\n    format!(\"Hi {foo}, I'm {bar}.\"),\n);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnscathic%2Fsafflower","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnscathic%2Fsafflower","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnscathic%2Fsafflower/lists"}