{"id":51203911,"url":"https://github.com/stackoverprof/say-dictionary","last_synced_at":"2026-06-28T02:06:53.802Z","repository":{"id":336153092,"uuid":"1148461964","full_name":"stackoverprof/say-dictionary","owner":"stackoverprof","description":"[NPM] Simple and LLM-friendly i18n solution.","archived":false,"fork":false,"pushed_at":"2026-02-13T22:21:02.000Z","size":60,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T04:44:42.334Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://stackoverprof.github.io/say-dictionary-site/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackoverprof.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2026-02-03T01:49:41.000Z","updated_at":"2026-02-13T22:21:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stackoverprof/say-dictionary","commit_stats":null,"previous_names":["stackoverprof/say-dictionary"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/stackoverprof/say-dictionary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fsay-dictionary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fsay-dictionary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fsay-dictionary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fsay-dictionary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackoverprof","download_url":"https://codeload.github.com/stackoverprof/say-dictionary/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackoverprof%2Fsay-dictionary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34874571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-06-28T02:06:52.695Z","updated_at":"2026-06-28T02:06:53.795Z","avatar_url":"https://github.com/stackoverprof.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# say-dictionary\n\nURL-based i18n with configurable languages and a CLI for extracting translation keys.\n\n## Why?\n\nThe dictionary format is designed to be **LLM-friendly**. Just hand your `dictionary.json` to an AI and ask it to translate — done. No complex tooling, no translation service integrations, no manual key mapping.\n\n## Installation\n\n```bash\nnpm install say-dictionary\n# or\npnpm add say-dictionary\n```\n\n## Usage\n\n### 1. Initialize (once in your entry point)\n\n```ts\n// root.tsx or app entry\nimport { init } from 'say-dictionary';\nimport dictionary from './dictionary.json';\n\ninit(dictionary);\n```\n\n### 2. Use anywhere\n\n```ts\nimport { say, getLanguage, setLanguage } from 'say-dictionary';\n\n// Get translated text\nsay(\"Order Now\"); // Returns \"Order Now\" or \"Panta núna\" based on URL\n\n// ICU formatting (only when vars are provided)\nsay(\"You have {count, plural, one {# pizza} other {# pizzas}}\", {\n  count: 2,\n});\n\n// Get current language from URL (null if no language prefix)\ngetLanguage(); // \"is\" or null\n\n// Navigate to different language\nsetLanguage('is'); // Redirects to /is/current-path\n```\n\n## Dictionary Format\n\n```json\n{\n  \"Order Now\": { \"en\": \"Order Now\", \"is\": \"Panta núna\" },\n  \"Welcome\": { \"en\": \"Welcome\", \"is\": \"Velkomin\" },\n  \"You have {count, plural, one {# pizza} other {# pizzas}}\": {\n    \"is\": \"Þú átt {count, plural, one {# pizzur} other {# pizzur}}\"\n  }\n}\n```\n\nLanguages are automatically detected from the dictionary keys.\nICU message formatting is **opt-in** and only runs when you pass variables to\n`say(key, vars)`. If a translation is missing for the current language, the key\nitself is treated as the ICU message.\n\nExample:\n\n```ts\nsay(\n  \"Today is the {day, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} of March\",\n  { day: 3 }\n);\n// \"Today is the 3rd of March\"\n\nsay(\"Event on {date, date, ::MMMM d}\", { date: new Date(\"2026-03-02\") });\n// \"Event on March 2\"\n```\n\n## URL Structure\n\nThe language is detected from the first path segment:\n\n- `/is/about` → Icelandic (`say()` returns translation)\n- `/about` → No language prefix (`say()` returns the key itself)\n\n## SSR (Next.js, Remix, etc.)\n\nFor SSR frameworks, use `ssrLang()` to prevent hydration mismatch:\n\n```tsx\n// app/[lang]/page.tsx\nexport default async function Page({ params }) {\n  const { lang } = await params;\n  return \u003cHome lang={lang} /\u003e;\n}\n\n// app/page.tsx\n\"use client\";\nimport { ssrLang, say } from 'say-dictionary';\n\nexport default function Home({ lang }: { lang?: string }) {\n  ssrLang(lang ?? null);\n  return \u003ch1\u003e{say(\"Hello\")}\u003c/h1\u003e;\n}\n```\n\nThis is optional — only needed for SSR. Client-side apps (Vite, CRA) work without it.\n\n## CLI\n\nExtract translation keys from your source files:\n\n```bash\nnpx say-dictionary extract -l en,is -i ./app -o ./dictionary.json\n```\n\nOptions:\n\n- `-l, --lang` - Comma-separated list of languages\n- `-i, --in` - Source directory to scan\n- `-o, --out` - Output dictionary file\n\nOutput `dictionary.json`:\n\n```json\n{\n  \"Order Now\": { \"en\": \"Order Now\", \"is\": \"\" },\n  \"Welcome\": { \"en\": \"Welcome\", \"is\": \"\" }\n}\n```\n\n**The first language (`en`) gets the key as its value.** Hand this to an LLM to fill in the translations.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackoverprof%2Fsay-dictionary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackoverprof%2Fsay-dictionary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackoverprof%2Fsay-dictionary/lists"}