From a065a23799eea5e37ddc246a09e7c0cf17ac4f40 Mon Sep 17 00:00:00 2001 From: BenGlut Date: Fri, 6 Nov 2026 06:32:18 +0100 Subject: [PATCH 1/2] site: multilingual showcase page for the JavaScript SDK One static page per language (en, fr, de, es, it, zh, pt, pt-pt, pl, ru, nl), built by site/build.py from site/template.html and site/i18n/.json, and published on GitHub Pages by .github/workflows/pages.yml. The version, release date and code samples come from package.json, CHANGELOG.md and README.md; site/seo_check.py checks every page (.agents/rules/seo-geo.md). README: a 'Get started with the JavaScript SDK' button and the site's language links at the top. The site is not part of the npm package. --- .agents/rules/seo-geo.md | 35 +++ .github/workflows/pages.yml | 94 +++++++ .gitignore | 3 + .npmignore | 3 + CHANGELOG.md | 14 ++ README.md | 30 +++ assets/site-button.svg | 33 +++ llms.txt | 1 + site/README.md | 56 +++++ site/assets/logo-tigertag-black.svg | 15 ++ site/assets/logo-tigertag-white.svg | 20 ++ site/assets/og.png | Bin 0 -> 127576 bytes site/build.py | 370 ++++++++++++++++++++++++++++ site/i18n/de.json | 107 ++++++++ site/i18n/en.json | 107 ++++++++ site/i18n/es.json | 107 ++++++++ site/i18n/fr.json | 107 ++++++++ site/i18n/it.json | 107 ++++++++ site/i18n/nl.json | 107 ++++++++ site/i18n/pl.json | 107 ++++++++ site/i18n/pt-pt.json | 107 ++++++++ site/i18n/pt.json | 107 ++++++++ site/i18n/ru.json | 107 ++++++++ site/i18n/zh.json | 107 ++++++++ site/seo_check.py | 225 +++++++++++++++++ site/site.css | 199 +++++++++++++++ site/site.js | 59 +++++ site/template.html | 212 ++++++++++++++++ 28 files changed, 2546 insertions(+) create mode 100644 .agents/rules/seo-geo.md create mode 100644 .github/workflows/pages.yml create mode 100644 assets/site-button.svg create mode 100644 site/README.md create mode 100644 site/assets/logo-tigertag-black.svg create mode 100644 site/assets/logo-tigertag-white.svg create mode 100644 site/assets/og.png create mode 100644 site/build.py create mode 100644 site/i18n/de.json create mode 100644 site/i18n/en.json create mode 100644 site/i18n/es.json create mode 100644 site/i18n/fr.json create mode 100644 site/i18n/it.json create mode 100644 site/i18n/nl.json create mode 100644 site/i18n/pl.json create mode 100644 site/i18n/pt-pt.json create mode 100644 site/i18n/pt.json create mode 100644 site/i18n/ru.json create mode 100644 site/i18n/zh.json create mode 100644 site/seo_check.py create mode 100644 site/site.css create mode 100644 site/site.js create mode 100644 site/template.html diff --git a/.agents/rules/seo-geo.md b/.agents/rules/seo-geo.md new file mode 100644 index 0000000..d324c5b --- /dev/null +++ b/.agents/rules/seo-geo.md @@ -0,0 +1,35 @@ +# Search and answer engines (SEO / GEO) + +**Rule.** The SDK site built from `site/` is written to be found, understood and quoted +correctly by search engines and AI answer engines, **with verifiable facts only**. It is +checked by `site/seo_check.py` on every pull request that touches it and before every +deploy; a failing check is fixed, never bypassed. + +**How.** + +1. **Facts, not claims.** Every statement on the site is something `README.md`, + `CHANGELOG.md` or `package.json` says, and can be checked there. No ranking, no "the + best", no comparison with other projects. Never text or instructions aimed only at + crawlers or AI: what an engine reads is what a person reads. +2. **Real code only.** Commands and code samples are the README's, line for line + (`build.py --check` enforces it). The npm link is the one the README gives. +3. **Answer first.** A section opens with one sentence that answers its question. Common + questions live in a visible FAQ, mirrored by the `FAQPage` JSON-LD. +4. **One complete page per language** (the eleven of `LANGS` in `site/build.py`): real + text in that language, code untranslated, a self canonical, `hreflang` to every + language plus `x-default`, `og:locale` with its alternates. +5. **Titles and descriptions.** Title at most 60 characters, description 50 to 160, + unique per language, natural sentences with the words people search for (NFC tag, + filament, Node.js, JavaScript SDK). `seo_check.py` warns past 60 / 160 and fails past + 70 / 200 or on a duplicate across languages. +6. **Structured data mirrors the page.** `Organization`, `WebSite`, + `SoftwareSourceCode` (with the `package.json` version and `dateModified`) and + `FAQPage`. One `

` per page. +7. **Images.** An `alt` on every image; `alt=""` only on a decorative duplicate. +8. **Machine-readable copies stay in step.** `llms.txt` is published at the site root + and linked from every page; the sitemap carries ``; `robots.txt` allows every + crawler. +9. **No emoji**, no prices. + +**Checks.** `python site/build.py --check`, then +`python site/build.py --out _site && python site/seo_check.py _site`. diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..491f9b1 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,94 @@ +name: Publish the SDK site + +# Builds site/ (one static page per language, see site/build.py) and publishes it +# on GitHub Pages: https://tigertag-project.github.io/TigerTag-SDK-JS/ +# +# Exactly one workflow deploys this site, and this is it. +# Prerequisite, once: Settings > Pages > Source = "GitHub Actions". + +on: + push: + branches: [main] + paths: + - 'site/**' + - 'README.md' + - 'CHANGELOG.md' + - 'package.json' + - 'llms.txt' + - 'assets/favicon.svg' + - 'assets/apple-touch-icon.png' + - '.github/workflows/pages.yml' + pull_request: + paths: + - 'site/**' + - 'README.md' + - 'CHANGELOG.md' + - 'package.json' + - '.github/workflows/pages.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages-${{ github.event_name }} + cancel-in-progress: false + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # the page's dateModified comes from git + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + # Every language must have every key, and every code sample must be in the README. + - name: Check the translations and code samples + run: python site/build.py --check + + - name: Build the site + run: python site/build.py --out _site + + # Search / answer-engine essentials (.agents/rules/seo-geo.md): no deploy + # with a broken hreflang set, a missing title or invalid JSON-LD. + - name: Check the site for search and answer engines + run: python site/seo_check.py _site + + - if: github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@v3 + with: + path: _site + + deploy: + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deploy.outputs.page_url }} + steps: + - uses: actions/configure-pages@v5 + - id: deploy + uses: actions/deploy-pages@v4 + + # The published result, not this workflow's green tick: every language answers. + - name: The published site answers in every language + run: | + set -euo pipefail + base="${{ steps.deploy.outputs.page_url }}" + for lang in "" fr/ de/ es/ it/ zh/ pt/ pt-pt/ pl/ ru/ nl/; do + for i in 1 2 3 4 5 6; do + code=$(curl -s -o /dev/null -w '%{http_code}' "${base}${lang}") && [ "$code" = 200 ] && break + sleep 10 + done + echo "${base}${lang} -> ${code}" + [ "$code" = 200 ] + done diff --git a/.gitignore b/.gitignore index 6dd283d..620c31d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ coverage/ # Internal tooling (not for public repo) .claude/ CLAUDE.md + +# Built site (site/build.py) +_site/ diff --git a/.npmignore b/.npmignore index a82a5ea..60105e4 100644 --- a/.npmignore +++ b/.npmignore @@ -12,3 +12,6 @@ CLAUDE.md *.swp *.swo .DS_Store +site/ +_site/ +.agents/ diff --git a/CHANGELOG.md b/CHANGELOG.md index df7b47f..aef4f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] + +### Added +- **Showcase site** (`site/`), published on GitHub Pages at + https://tigertag-project.github.io/TigerTag-SDK-JS/ by `.github/workflows/pages.yml`: + one static page per language (en, fr, de, es, it, zh, pt, pt-pt, pl, ru, nl) built by + `site/build.py` from `site/template.html` and `site/i18n/.json`, with the install + command, the README quick start, the features, the CLI and Electron samples, the + playground, the ecosystem and an FAQ. The version, release date and code samples are + read from `package.json`, this changelog and the README. `site/seo_check.py` checks + every page (rule: `.agents/rules/seo-geo.md`). Not part of the npm package. +- README: a "Get started with the JavaScript SDK" button (`assets/site-button.svg`) and + the site's language links at the top. + ## [1.2.0] — 2026-09-30 ### Added diff --git a/README.md b/README.md index 57da098..c6489d7 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,36 @@ [![Protocol](https://img.shields.io/badge/protocol-TigerTag%20v2.2-orange)](https://github.com/TigerTag-Project/TigerTag-RFID-Guide) [![Offline first](https://img.shields.io/badge/offline-first-teal)](database/) +

+ + Get started with the JavaScript SDK + +

+ +

+ English +  ·  + Français +  ·  + Deutsch +  ·  + Español +  ·  + Italiano +  ·  + 中文 +  ·  + Português (BR) +  ·  + Português (PT) +  ·  + Polski +  ·  + Русский +  ·  + Nederlands +

+ **Offline JavaScript / Node.js SDK for TigerTag RFID material identification.** > TigerTag is the **world's most widely deployed open-source RFID protocol** for diff --git a/assets/site-button.svg b/assets/site-button.svg new file mode 100644 index 0000000..4c38f32 --- /dev/null +++ b/assets/site-button.svg @@ -0,0 +1,33 @@ + + Get started with the JavaScript SDK + + + + + + + + + + + + + + + + + + + + + + + Get started with the JavaScript SDK + Install, read, verify and write TigerTag chips + + + diff --git a/llms.txt b/llms.txt index fc64fcc..2fdada7 100644 --- a/llms.txt +++ b/llms.txt @@ -1,6 +1,7 @@ # TigerTag JavaScript SDK — Reference for AI Agents # Version: 1.2.0 | Protocol: TigerTag Open Source v2.2 # npm: https://www.npmjs.com/package/tigertag +# Site: https://tigertag-project.github.io/TigerTag-SDK-JS/ (11 languages) # SDK: https://github.com/TigerTag-Project/TigerTag-SDK-JS # Spec: https://github.com/TigerTag-Project/TigerTag-RFID-Guide diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..163cdb5 --- /dev/null +++ b/site/README.md @@ -0,0 +1,56 @@ +# The SDK site + +The public showcase of the TigerTag JavaScript SDK, published on GitHub Pages: + + +It presents the SDK, shows how to install and use it, and links to the npm package, +the repository and the rest of the TigerTag ecosystem. It is static HTML, one page per +language, built by [`build.py`](build.py) and published by +[`pages.yml`](../.github/workflows/pages.yml) on every push to `main` that touches +`site/`, `README.md`, `CHANGELOG.md`, `package.json` or `llms.txt`. The same workflow +builds and checks the site on pull requests, without deploying. + +Nothing here is part of the npm package (`files` in `package.json`, and `.npmignore`). + +## Files + +| File | Contents | +|---|---| +| [`template.html`](template.html) | The page. Every `{{key}}` is replaced by the text of that key in the page's language; `{{@…}}` keys are filled by `build.py` (language menu, `hreflang` links, JSON-LD, version, code samples). | +| [`i18n/en.json`](i18n/en.json) | The English text, which is the reference: every other language has exactly its keys. | +| `i18n/.json` | One file per language: `fr`, `de`, `es`, `it`, `zh` (Simplified Chinese), `pt` (Brazil), `pt-pt` (Portugal), `pl`, `ru`, `nl` — the languages of the other TigerTag sites. | +| [`site.css`](site.css), [`site.js`](site.js) | The look (dark by default, light on request) and the theme / language / copy buttons, the same as the TigerTag protocol site. Each page loads them with `?v=` and a hash of the file's content, so a new version is never hidden by a browser cache. | +| [`assets/`](assets/) | The TigerTag logos (white on the dark theme, contoured black on the light one) and `og.png`, the picture shown when the site is shared (1200 × 630). The favicon comes from the repository's `assets/`. | + +## Where the facts come from + +- The package name and version: `package.json`. The release date: the newest version in `CHANGELOG.md`. +- The quick start in the hero: read from the README's `## Quick start` section at build time. +- The other code samples: `CODE` in `build.py`, copied from the README. `build.py --check` + fails when a line of them is no longer in the README, and when the README stops + documenting `npm install ` or linking the npm page. +- Every sentence in `i18n/` states something the README, `CHANGELOG.md` or `package.json` + says. A new fact goes to the README first. + +## Editing + +- **Change a text**: edit `i18n/en.json`, then the same key in every other language. +- **Add a text**: add the key to every `i18n/*.json` file and use `{{key}}` in `template.html`. + The build refuses a language that misses a key, has an unknown one or an empty value. +- **Check and preview**: + + ```bash + python3 site/build.py --check + python3 site/build.py && python3 -m http.server 8000 --directory _site + python3 site/build.py --only en # English only, while translations are pending + ``` + +- **Search and answer engines**: `python3 site/build.py --out _site && python3 site/seo_check.py _site` + checks every page (title and description length, one `h1`, canonical and `hreflang`, + sharing cards, `alt` text, JSON-LD types, the version and the FAQ they mirror, in-page + links, no emoji, the install command, sitemap, robots, `llms.txt`). The rule behind it + is [`.agents/rules/seo-geo.md`](../.agents/rules/seo-geo.md). + +The English page is the root (`/`), every other language has its own folder (`/fr/`, +`/zh/`…), and the pages point to each other with `hreflang`. A language chosen in the +menu is remembered, and the English root forwards to it on the next visit. diff --git a/site/assets/logo-tigertag-black.svg b/site/assets/logo-tigertag-black.svg new file mode 100644 index 0000000..42121e8 --- /dev/null +++ b/site/assets/logo-tigertag-black.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/site/assets/logo-tigertag-white.svg b/site/assets/logo-tigertag-white.svg new file mode 100644 index 0000000..6d40c2c --- /dev/null +++ b/site/assets/logo-tigertag-white.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/site/assets/og.png b/site/assets/og.png new file mode 100644 index 0000000000000000000000000000000000000000..5dd4720df4b0d2f95e30d4a7aedf5de1fbeb6eab GIT binary patch literal 127576 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 aB-;_1P)q.json`: +English at the root, the others under `//`. Every page is complete HTML in +its own language, with `hreflang` links between them, JSON-LD in its language and +a sitemap listing them all. + +The facts that change with a release are read from the repository itself: the +package name and version from `package.json`, the release date from +`CHANGELOG.md`, and the code samples from `README.md` (or copied from it), so the +page never shows a command or an API the README does not document. + +Usage: + python site/build.py # build into _site/ + python site/build.py --check # only check the translations + python site/build.py --out DIR --url https://example.org/path/ +""" + +import argparse +import hashlib +import html +import json +import re +import shutil +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +REPO = HERE.parent +SITE_URL = "https://tigertag-project.github.io/TigerTag-SDK-JS/" +REPO_URL = "https://github.com/TigerTag-Project/TigerTag-SDK-JS" + +# The eleven languages of the TigerTag sites, in the same order. `pt` (Brazil) +# and `pt-pt` (Portugal) are separate dictionaries on purpose. +LANGS = ["en", "fr", "de", "es", "it", "zh", "pt", "pt-pt", "pl", "ru", "nl"] +HTML_LANG = {"zh": "zh-Hans", "pt": "pt-BR", "pt-pt": "pt-PT"} +OG_LOCALE = {"en": "en_US", "fr": "fr_FR", "de": "de_DE", "es": "es_ES", "it": "it_IT", + "zh": "zh_CN", "pt": "pt_BR", "pt-pt": "pt_PT", "pl": "pl_PL", "ru": "ru_RU", "nl": "nl_NL"} + +# Repository file -> published path under assets/. +ASSETS = { + "assets/favicon.svg": "favicon.svg", + "assets/apple-touch-icon.png": "apple-touch-icon.png", +} + +# Crawlers of search and answer engines, welcomed by name in robots.txt. Listing +# them is an explicit opt-in, not a ranking trick. +AI_AGENTS = ["GPTBot", "OAI-SearchBot", "ChatGPT-User", "ClaudeBot", "Claude-SearchBot", "Claude-User", + "PerplexityBot", "Perplexity-User", "Google-Extended", "Applebot-Extended", "CCBot", + "Amazonbot", "Bytespider", "Meta-ExternalAgent", "cohere-ai", "DuckAssistBot", "MistralAI-User"] + +ORG_ID = "https://tigersystem.io/#organization" +FAQ_COUNT = 7 + +# Code shown on the page, copied verbatim from README.md (the quick start is read +# from it at build time). Code is never translated. +CODE = { + "install": "npm install {name}", + "install_alt": "pnpm add {name}\nyarn add {name}\nbun add {name}", + "read": """// Node.js — nfc-pcsc (ACR122U / PN532) +reader.on('card', async (card) => { + const uid = Buffer.from(card.uid, 'hex'); // 7 bytes + const payload = await reader.read(4, 144, 4); // pages 4–39, 144 bytes + const tag = TigerTag.fromPages(uid, payload); + console.log(tag.pretty()); + console.log(String(tag.verify())); // VALID / NOT SIGNED +});""", + "write": """// Immutable surgical update — returns a new TigerTag, original unchanged +const patched = tag.patch({ nozzleTempMin: 200, dryTemp: 55 }); + +// Erase a chip — write the returned 80 bytes to the NFC chip +const blankBytes = TigerTag.erase();""", + "cli": """# Parse a dump file +tigertag dump.bin + +# JSON output +tigertag dump.bin --json + +# Update the reference tables now (into the data dir) +tigertag update""", + "electron": """// main.js — replace parseTigerTag() subprocess dependency +const { TigerTag, TigerTagDB } = require('tigertag'); + +function parseTigerTag(payload, uid) { + const tag = TigerTag.fromPages(Buffer.from(uid), Buffer.from(payload)); + const db = new TigerTagDB(); + return { + dict: tag.toDict(db), + raw: tag.toRawDict(), + sig: tag.verify(db).toDict(), + }; +}""", + "playground": """npm run playground +# then open http://localhost:7432/tools/playground.html + +# Enable live reader support (one-time install) +npm install ws nfc-pcsc""", +} + + +def package(): + return json.loads((REPO / "package.json").read_text(encoding="utf-8")) + + +def readme(): + return (REPO / "README.md").read_text(encoding="utf-8") + + +def readme_block(text, heading, lang): + """The first fenced code block of `lang` under a README heading.""" + start = text.find(f"\n{heading}\n") + if start < 0: + raise SystemExit(f"error: README.md has no {heading!r} section") + match = re.compile(rf"```{lang}\n(.*?)\n```", re.S).search(text, start) + if not match: + raise SystemExit(f"error: README.md {heading!r} has no {lang} code block") + return match.group(1) + + +def check_code(text, name): + """Every sample shown on the page is in the README, line for line.""" + problems = [] + for key, code in CODE.items(): + for line in code.replace("{name}", name).splitlines(): + line = line.strip() + if line and line not in text and not line.startswith("# then open"): + problems.append(f"{key}: {line!r}") + if problems: + raise SystemExit("error: code samples not found in README.md\n " + "\n ".join(problems)) + + +def release_date(): + """The date of the newest released version in CHANGELOG.md (YYYY-MM-DD).""" + text = (REPO / "CHANGELOG.md").read_text(encoding="utf-8") + match = re.search(r"^## \[\d+\.\d+\.\d+\][^\n]*?(\d{4}-\d{2}-\d{2})", text, re.M) + return match.group(1) if match else "" + + +def highlight(code, comment): + """Escape a code sample and colour its comments and strings.""" + out = [] + for line in code.splitlines(): + pos = line.find(comment) + # A '#' only starts a comment at the start of a shell line or after a space. + if comment == "#" and pos > 0 and line[pos - 1] != " ": + pos = -1 + body, note = (line[:pos], line[pos:]) if pos >= 0 else (line, "") + body = html.escape(body, quote=False) + body = re.sub(r"'[^'\n]*'", lambda m: f'{m.group(0)}', body) + if note: + body += f'{html.escape(note, quote=False)}' + out.append(body) + return "\n".join(out) + + +def asset_version(name): + """Short sha256 of a published site file, appended to its URL as ?v=.""" + return hashlib.sha256((HERE / name).read_bytes()).hexdigest()[:10] + + +def site_date(): + """The page's last change, from git (the site, the README, the package).""" + import subprocess + from datetime import datetime, timezone + try: + date = subprocess.run(["git", "log", "-1", "--format=%cI", "--", "site", "README.md", "package.json"], + cwd=REPO, capture_output=True, text=True, check=True).stdout.strip()[:10] + except (OSError, subprocess.CalledProcessError): + date = "" + return date or datetime.now(timezone.utc).date().isoformat() + + +PLACEHOLDER = re.compile(r"\{\{([a-z0-9_@]+)\}\}") + + +def load_dictionaries(): + """Every language, checked against English: same keys, none empty.""" + dicts = {} + for lang in LANGS: + path = HERE / "i18n" / f"{lang}.json" + if not path.exists(): + raise SystemExit(f"error: missing translation {path.relative_to(REPO)}") + dicts[lang] = json.loads(path.read_text(encoding="utf-8")) + reference = list(dicts["en"]) + problems = [] + for lang, words in dicts.items(): + missing = [k for k in reference if k not in words] + extra = [k for k in words if k not in dicts["en"]] + empty = [k for k, v in words.items() if not isinstance(v, str) or not v.strip()] + for label, keys in (("missing", missing), ("unknown", extra), ("empty", empty)): + if keys: + problems.append(f"{lang}.json: {label} key(s): {', '.join(keys)}") + template_keys = set(PLACEHOLDER.findall((HERE / "template.html").read_text(encoding="utf-8"))) + unused = [k for k in template_keys if not k.startswith("@") and k not in dicts["en"]] + if unused: + problems.append(f"template.html uses unknown key(s): {', '.join(sorted(unused))}") + if problems: + raise SystemExit("error: translations do not match en.json\n " + "\n ".join(problems)) + return dicts + + +def page_path(lang): + return "" if lang == "en" else f"{lang}/" + + +def render(template, lang, words, dicts, site_url, ctx): + base = "" if lang == "en" else "../" + url = site_url + page_path(lang) + pkg = ctx["package"] + hl = HTML_LANG.get(lang, lang) + + alternates = [f'' for l in LANGS] + alternates.append(f'') + + menu = [] + for l in LANGS: + current = ' aria-current="true"' if l == lang else "" + menu.append(f'

  • {html.escape(dicts[l]["lang_name"])}
  • ') + + faq = [{"@type": "Question", "name": words[f"faq{i}_q"], + "acceptedAnswer": {"@type": "Answer", "text": words[f"faq{i}_a"]}} for i in range(1, FAQ_COUNT + 1)] + jsonld = [ + {"@context": "https://schema.org", "@type": "Organization", "@id": ORG_ID, "name": "TigerTag Corp", + "url": "https://tigersystem.io", "logo": site_url + "assets/apple-touch-icon.png", + "sameAs": ["https://github.com/TigerTag-Project", "https://tigertag-project.github.io/TigerTag-RFID-Guide/"]}, + {"@context": "https://schema.org", "@type": "WebSite", "name": "TigerTag JavaScript SDK", "url": url, + "inLanguage": hl, "description": words["meta_description"], "publisher": {"@id": ORG_ID}}, + {"@context": "https://schema.org", "@type": "SoftwareSourceCode", "name": "TigerTag JavaScript SDK", + "alternateName": pkg["name"], "description": words["meta_description"], "inLanguage": hl, + "url": url, "codeRepository": REPO_URL, "programmingLanguage": "JavaScript", + "runtimePlatform": "Node.js " + pkg.get("engines", {}).get("node", ""), + "version": pkg["version"], "dateModified": ctx["date"], + "license": "https://www.apache.org/licenses/LICENSE-2.0", + "image": site_url + "assets/og.png", + "isBasedOn": "https://github.com/TigerTag-Project/TigerTag-RFID-Guide", + "author": {"@id": ORG_ID}, "publisher": {"@id": ORG_ID}}, + {"@context": "https://schema.org", "@type": "FAQPage", "inLanguage": hl, "mainEntity": faq}, + ] + # ` block. + jsonld_html = "\n".join('" + for j in jsonld) + + special = { + "@lang": lang, + "@htmllang": hl, + "@oglocale": OG_LOCALE[lang], + "@oglocalealt": "\n".join(f'' for l in LANGS if l != lang), + "@url": url, + "@siteurl": site_url, + "@base": base, + "@langpath": page_path(lang), + "@alternates": "\n".join(alternates), + "@langmenu": "\n".join(menu), + "@jsonld": jsonld_html, + "@name": html.escape(pkg["name"]), + "@version": html.escape(pkg["version"]), + "@npm": ctx["npm"], + "@repo": REPO_URL, + "@vcss": asset_version("site.css"), + "@vjs": asset_version("site.js"), + "@trademark": f'{html.escape(words["foot_trademark"])}', + } + for key, code in ctx["code"].items(): + special["@code_" + key] = code + + def fill(match): + key = match.group(1) + if key in special: + return special[key] + return html.escape(words[key].replace("{date}", ctx["released"]), quote=True) + + return PLACEHOLDER.sub(fill, template) + + +def context(): + pkg = package() + text = readme() + name = pkg["name"] + if f"npm install {name}" not in text: + raise SystemExit(f"error: README.md does not document `npm install {name}`") + # The npm page, only as the README links it. + npm = f"https://www.npmjs.com/package/{name}" + if npm not in text: + raise SystemExit(f"error: README.md does not link {npm}") + check_code(text, name) + code = {key: highlight(value.replace("{name}", name), "#" if key in ("install", "install_alt", "cli", "playground") else "//") + for key, value in CODE.items()} + code["quick"] = highlight(readme_block(text, "## Quick start", "js"), "//") + return {"package": pkg, "npm": npm, "code": code, "date": site_date(), "released": release_date()} + + +def build(out, site_url): + dicts = load_dictionaries() + template = (HERE / "template.html").read_text(encoding="utf-8") + ctx = context() + if out.exists(): + shutil.rmtree(out) + (out / "assets").mkdir(parents=True) + + for lang in LANGS: + target = out / page_path(lang) / "index.html" + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(render(template, lang, dicts[lang], dicts, site_url, ctx), encoding="utf-8") + + for name in ("site.css", "site.js"): + shutil.copy2(HERE / name, out / name) + for source, published in ASSETS.items(): + shutil.copy2(REPO / source, out / "assets" / published) + # Pictures made for the site itself (site/assets/), and the sharing card. + for extra in sorted((HERE / "assets").glob("*")): + shutil.copy2(extra, out / "assets" / extra.name) + # The machine-readable description of the SDK, at the site root too. + shutil.copy2(REPO / "llms.txt", out / "llms.txt") + + urls = "\n".join( + " {}{}{}".format( + site_url + page_path(lang), ctx["date"], + "".join(f'' for l in LANGS)) + for lang in LANGS) + (out / "sitemap.xml").write_text( + '\n' + '\n' + f"{urls}\n\n", encoding="utf-8") + robots = ["User-agent: *", "Allow: /", ""] + for agent in AI_AGENTS: + robots += [f"User-agent: {agent}", "Allow: /", ""] + robots += [f"Sitemap: {site_url}sitemap.xml"] + (out / "robots.txt").write_text("\n".join(robots) + "\n", encoding="utf-8") + (out / ".nojekyll").write_text("", encoding="utf-8") + print(f"built {len(LANGS)} languages into {out}") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--out", default=str(REPO / "_site")) + parser.add_argument("--url", default=SITE_URL) + parser.add_argument("--check", action="store_true") + parser.add_argument("--only", help="comma-separated languages, for a quick local preview") + args = parser.parse_args() + if args.only: + LANGS[:] = [l for l in LANGS if l in args.only.split(",")] + if args.check: + load_dictionaries() + context() + print(f"translations and code samples OK: {len(LANGS)} languages") + return + url = args.url if args.url.endswith("/") else args.url + "/" + build(Path(args.out), url) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/site/i18n/de.json b/site/i18n/de.json new file mode 100644 index 0000000..6352b6a --- /dev/null +++ b/site/i18n/de.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Deutsch", + "lang_label": "Sprache wählen", + "theme_label": "Hell oder dunkel", + "skip": "Zum Inhalt springen", + "copy": "Kopieren", + "copied": "Kopiert", + "meta_title": "TigerTag JavaScript-SDK: NFC-Tags lesen und schreiben", + "meta_description": "Node.js-SDK zum Lesen, Prüfen, Erstellen und Ändern von TigerTag-NFC-Chips auf Filament- und Harzspulen für den 3D-Druck. Offline, Apache-2.0.", + "og_title": "TigerTag JavaScript-SDK", + "og_description": "TigerTag-NFC-Chips mit Node.js lesen, prüfen und schreiben: Material, Farbe, Druckwerte, Menge und Signaturprüfung offline.", + "og_alt": "TigerTag JavaScript-SDK: npm install tigertag", + "nav_start": "Loslegen", + "nav_features": "Funktionen", + "nav_tools": "CLI und Electron", + "nav_playground": "Playground", + "nav_eco": "Ökosystem", + "nav_faq": "FAQ", + "hero_kicker": "JavaScript- / Node.js-SDK", + "hero_headline": "TigerTag-NFC-Chips mit JavaScript lesen, prüfen und schreiben.", + "hero_lead": "Das SDK des offenen TigerTag-Protokolls für Filament und Harz im 3D-Druck: Es macht aus den Bytes eines Chips Material, Farbe, Druckeinstellungen und Menge, prüft seine ECDSA-P256-Signatur offline und baut neue Tags Byte für Byte.", + "hero_caption": "Der Schnellstart aus dem README: Er läuft direkt nach npm install, ohne Einrichtung.", + "cta_start": "Loslegen", + "cta_npm": "Auf npm ansehen", + "cta_github": "Quellcode auf GitHub", + "fact1_label": "Aktuelle Version, veröffentlicht am {date}", + "fact2_label": "Nutzt das eingebaute crypto und fetch", + "fact3_label": "Laufzeitabhängigkeiten zum Lesen und Prüfen", + "fact4_label": "Open-Source-Lizenz mit Patentgewährung", + "start_title": "In drei Schritten loslegen", + "start_lead": "Paket installieren, einen Chip mit Ihrer NFC-Bibliothek lesen, einen neuen schreiben. Dank der mitgelieferten Referenztabellen läuft der erste Start ohne Netz.", + "step1_title": "Paket installieren", + "step1_text": "Aus der npm-Registry. pnpm, yarn und bun installieren dasselbe Paket.", + "step2_title": "Einen Chip lesen", + "step2_text": "Übergeben Sie die 7-Byte-UID und die 144 Bytes, die Ihre NFC-Bibliothek liest. Hier mit nfc-pcsc und einem ACR122U.", + "step3_title": "Ändern und schreiben", + "step3_text": "Jede Änderung liefert einen neuen TigerTag; toBytes() gibt die zu schreibenden Bytes. erase() liefert einen leeren Chip.", + "start_more": "Die vollständige API im README", + "feat_title": "Was das SDK kann", + "feat_lead": "Ein Paket für alles rund um einen TigerTag-Chip: lesen, prüfen, schreiben und seine Daten aktuell halten.", + "f1_title": "Jeden Dump lesen", + "f1_text": "fromPages(), fromDump() und fromFile() nehmen eine UID mit 80 oder 144 Bytes oder einen vollständigen 180-Byte-Dump. pretty(), describe(), toDict() und toRawDict() liefern Text, einen Absatz in Klartext oder JSON-fertige Objekte.", + "f2_title": "Echtheit offline prüfen", + "f2_text": "verify() prüft die ECDSA-P256-Signatur, die die UID des Chips an seine Produktdaten bindet, mit dem im Paket enthaltenen öffentlichen Schlüssel. Ergebnis: valid, invalid, unsigned, no_key oder no_uid.", + "f3_title": "Tags erstellen und ändern", + "f3_text": "create(), asInit(), patch() und erase() erzeugen Chip-Bytes. Ein TigerTag ist unveränderlich: patch() liefert einen neuen und verweigert Änderungen an idTigertag, idProduct, uid oder der Signatur.", + "f4_title": "Aus dem Katalog bauen", + "f4_text": "TigerTag.fromCatalog(productId) baut einen vollständigen TigerTag+ aus dem offiziellen Produktkatalog, der beim ersten Gebrauch geladen und in einem Ordner pro Benutzer zwischengespeichert wird.", + "f5_title": "Mit dem Hersteller abgleichen", + "f5_text": "Für TigerTag+-Chips holen rawApi(), diffApi() und patchFromApi() die aktuellen Produktdaten der Marke, zeigen die Unterschiede zum Chip und übernehmen sie.", + "f6_title": "Referenzdaten, zuerst offline", + "f6_text": "Die Tabellen für Material, Marke, Optik, Typ, Durchmesser und Einheit sind im Paket enthalten. Eine Hintergrundprüfung aktualisiert sie höchstens einmal am Tag; offline: true oder TIGERTAG_OFFLINE=1 bedeutet null Netzaufrufe.", + "types_title": "Drei Arten von Tags", + "types_lead": "Das SDK liest und schreibt die drei Tag-Typen des TigerTag-Protokolls v2.2 auf NTAG213-, NTAG215- und NTAG216-Chips.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Alle Felder auf dem Chip, vollständig offline lesbar.", + "t2_name": "TigerTag Init", + "t2_text": "Eine leere Vorlage, bereit zum Programmieren. TigerTag.asInit() erstellt eine.", + "t3_name": "TigerTag+", + "t3_text": "Alle Felder auf dem Chip, dazu eine Produkt-ID, die mit der Cloud-API für Updates des Herstellers verknüpft ist. Ein signierter Chip lässt sich mit verify() prüfen.", + "tools_title": "Auf der Kommandozeile und in Electron", + "tools_lead": "Das Paket installiert den Befehl tigertag und läuft im Hauptprozess einer Electron-App.", + "cli_title": "Der Befehl tigertag", + "cli_text": "Einen Dump als Text, JSON oder Rohfelder ausgeben und die Referenztabellen bei Bedarf aktualisieren.", + "electron_title": "In einer Electron-App", + "electron_text": "Chips im Hauptprozess auswerten, ohne Unterprozess mitzuliefern.", + "pg_title": "Ohne Lesegerät ausprobieren: der Playground", + "pg_text": "Eine lokale Webseite, vom SDK selbst bereitgestellt, die TigerTag-Chips baut und dekodiert und jedes Byte zeigt. Keine NFC-Hardware nötig.", + "pg_point1": "Ein- und Ausgabe des SDK nebeneinander: der Aufruf create(), dann pretty(), describe(), verify() und toDict().", + "pg_point2": "Mit einem oder mehreren PC/SC-Lesegeräten wie dem ACR122U werden Chips beim Auflegen gelesen, und Burn schreibt den aktuellen Inhalt.", + "pg_point3": "Der Playground schreibt nie eine Signatur: Nur ein zertifizierter Hersteller kann eine ausstellen.", + "eco_title": "Teil des TigerTag-Ökosystems", + "eco_lead": "Das SDK setzt die offene TigerTag-Spezifikation um, neben den anderen offiziellen Werkzeugen.", + "eco_spec_title": "Spezifikation des TigerTag-Protokolls", + "eco_spec_text": "Die offene Spezifikation, die das SDK umsetzt: Speicheraufbau, Tag-Typen, Signaturverfahren und Referenzdatenbank.", + "eco_py_title": "TigerTag-SDK für Python", + "eco_py_text": "Die Python-Portierung: lesen, prüfen, schreiben und vergleichen. Die Playground-Seite ist in beiden SDKs dieselbe Datei.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Quelloffene Desktop-Lagerverwaltung für Windows, macOS und Linux, mit TigerTag Pod und ACR122U-Lesegeräten.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "Die offizielle Mobil-App: Chips mit dem eingebauten NFC des Telefons lesen und schreiben.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repository", + "faq_title": "Fragen", + "faq1_q": "Wie installiere ich das TigerTag JavaScript-SDK?", + "faq1_a": "Mit npm install tigertag, oder pnpm add tigertag, yarn add tigertag bzw. bun add tigertag. Es braucht Node.js 18 oder neuer und kein weiteres Paket zum Lesen und Prüfen.", + "faq2_q": "Braucht es eine Internetverbindung?", + "faq2_a": "Nein. Die Referenztabellen und der öffentliche Signaturschlüssel sind im Paket enthalten, Lesen und Prüfen funktionieren offline. Das Netz wird nur für die tägliche Tabellenprüfung, den Produktkatalog und die TigerTag+-Cloud-Methoden genutzt; offline: true oder TIGERTAG_OFFLINE=1 schaltet es ab.", + "faq3_q": "Mit welchen NFC-Chips und Lesegeräten funktioniert es?", + "faq3_a": "TigerTag nutzt NTAG213-, NTAG215- und NTAG216-Chips (ISO 14443-3). Das SDK nimmt die UID und die Bytes, die Ihre NFC-Bibliothek liest, und ist daher an kein Lesegerät gebunden; das README zeigt nfc-pcsc mit einem ACR122U.", + "faq4_q": "Wie prüft es, ob ein Chip echt ist?", + "faq4_a": "verify() prüft die ECDSA-P256-Signatur des Chips über seine UID und Produktkennungen mit dem im Paket enthaltenen öffentlichen Schlüssel. Es liefert valid, invalid, unsigned (ein Maker-Tag), no_key oder no_uid, ohne Serveraufruf.", + "faq5_q": "Kann es Chips beschreiben?", + "faq5_a": "Es erzeugt die Bytes: create(), patch(), asInit(), erase() und fromCatalog() liefern einen TigerTag, dessen toBytes() der Inhalt ist, den Ihre NFC-Bibliothek schreibt. Die Burn-Schaltfläche des Playgrounds schreibt ihn auf die Chips angeschlossener Lesegeräte.", + "faq6_q": "Gibt es eine Python-Version?", + "faq6_a": "Ja: TigerTag-SDK-Python ist die Python-Portierung derselben API, und beide SDKs stellen dieselbe Playground-Seite bereit.", + "faq7_q": "Unter welcher Lizenz steht das SDK?", + "faq7_a": "Apache-2.0, mit ausdrücklicher Patentgewährung. Das TigerTag-Protokoll selbst umzusetzen erfordert keine Lizenz.", + "join_title": "Ein Befehl zum Start", + "join_text": "npm install tigertag, dann TigerTag.fromPages(uid, payload).", + "join_btn": "Auf npm ansehen", + "foot_licences": "SDK Apache-2.0 · Protokollspezifikation CC-BY-4.0", + "foot_trademark": "TigerTag ist eine Marke der TigerTag Corp. Markenrichtlinie", + "foot_protocol": "TigerTag-Protokoll", + "foot_source": "Quelle dieser Seite" +} diff --git a/site/i18n/en.json b/site/i18n/en.json new file mode 100644 index 0000000..a3b66ee --- /dev/null +++ b/site/i18n/en.json @@ -0,0 +1,107 @@ +{ + "lang_name": "English", + "lang_label": "Choose a language", + "theme_label": "Light or dark", + "skip": "Skip to content", + "copy": "Copy", + "copied": "Copied", + "meta_title": "TigerTag JavaScript SDK: read and write NFC filament tags", + "meta_description": "Node.js SDK to read, verify, create and patch TigerTag NFC chips on 3D-printing filament and resin. Offline, Apache-2.0: npm install tigertag.", + "og_title": "TigerTag JavaScript SDK", + "og_description": "Read, verify and write TigerTag NFC chips from Node.js: material, colour, print settings, quantity and offline signature check.", + "og_alt": "TigerTag JavaScript SDK: npm install tigertag", + "nav_start": "Get started", + "nav_features": "Features", + "nav_tools": "CLI and Electron", + "nav_playground": "Playground", + "nav_eco": "Ecosystem", + "nav_faq": "FAQ", + "hero_kicker": "JavaScript / Node.js SDK", + "hero_headline": "Read, verify and write TigerTag NFC chips from JavaScript.", + "hero_lead": "The SDK of the open TigerTag protocol for 3D-printing filament and resin: it turns a chip's bytes into material, colour, print settings and quantity, checks its ECDSA-P256 signature offline, and builds new tags byte for byte.", + "hero_caption": "The README quick start: it works right after npm install, with no setup.", + "cta_start": "Get started", + "cta_npm": "View on npm", + "cta_github": "Source on GitHub", + "fact1_label": "Current version, released {date}", + "fact2_label": "Uses the built-in crypto and fetch", + "fact3_label": "Runtime dependencies for parsing and verification", + "fact4_label": "Open-source licence, with a patent grant", + "start_title": "Get started in three steps", + "start_lead": "Install the package, read a chip with your NFC library, write a new one. The bundled reference tables make the first run work with no network.", + "step1_title": "Install the package", + "step1_text": "From the npm registry. pnpm, yarn and bun install the same package.", + "step2_title": "Read a chip", + "step2_text": "Pass the 7-byte UID and the 144 bytes your NFC library reads. Here with nfc-pcsc and an ACR122U.", + "step3_title": "Change and write", + "step3_text": "Every change returns a new TigerTag; toBytes() gives the bytes to write back. erase() gives a blank chip.", + "start_more": "The full API in the README", + "feat_title": "What the SDK does", + "feat_lead": "One package for every side of a TigerTag chip: reading, checking, writing, and keeping its data up to date.", + "f1_title": "Read any dump", + "f1_text": "fromPages(), fromDump() and fromFile() take a UID with 80 or 144 bytes, or a full 180-byte dump. pretty(), describe(), toDict() and toRawDict() return text, a plain-language paragraph or JSON-ready objects.", + "f2_title": "Verify authenticity offline", + "f2_text": "verify() checks the ECDSA-P256 signature that binds the chip UID to its product data, with the public key bundled in the package. The result is valid, invalid, unsigned, no_key or no_uid.", + "f3_title": "Create and patch tags", + "f3_text": "create(), asInit(), patch() and erase() build chip bytes. A TigerTag is immutable: patch() returns a new one, and refuses to change idTigertag, idProduct, uid or the signature.", + "f4_title": "Build from the catalogue", + "f4_text": "TigerTag.fromCatalog(productId) builds a complete TigerTag+ from the official product catalogue, downloaded on first use and cached in a per-user folder.", + "f5_title": "Sync with the manufacturer", + "f5_text": "For TigerTag+ chips, rawApi(), diffApi() and patchFromApi() fetch the brand's current product data, list what differs from the chip and apply it.", + "f6_title": "Reference data, offline first", + "f6_text": "The material, brand, aspect, type, diameter and unit tables ship in the package. A background check updates them at most once a day; offline: true or TIGERTAG_OFFLINE=1 means zero network calls.", + "types_title": "Three kinds of tag", + "types_lead": "The SDK reads and writes the three tag types of TigerTag protocol v2.2, on NTAG213, NTAG215 and NTAG216 chips.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Every field on the chip, readable fully offline.", + "t2_name": "TigerTag Init", + "t2_text": "A blank template, ready to be programmed. TigerTag.asInit() creates one.", + "t3_name": "TigerTag+", + "t3_text": "Every field on the chip, plus a product ID that links to the cloud API for updates from the manufacturer. A signed chip can be verified with verify().", + "tools_title": "From the command line and inside Electron", + "tools_lead": "The package installs a tigertag command, and runs in the main process of an Electron app.", + "cli_title": "The tigertag command", + "cli_text": "Parse a dump as text, JSON or raw fields, and update the reference tables on demand.", + "electron_title": "Inside an Electron app", + "electron_text": "Parse chips in the main process, with no subprocess to ship.", + "pg_title": "Try it without a reader: the playground", + "pg_text": "A local web page, served by the SDK itself, that builds and decodes TigerTag chips and shows every byte. No NFC hardware is needed.", + "pg_point1": "SDK input and output side by side: the create() call, then pretty(), describe(), verify() and toDict().", + "pg_point2": "With one or more PC/SC readers such as the ACR122U, chips are read as soon as they are placed, and Burn writes the current payload.", + "pg_point3": "The playground never writes a signature: only a certified manufacturer can issue one.", + "eco_title": "Part of the TigerTag ecosystem", + "eco_lead": "The SDK implements the open TigerTag specification, next to the other official tools.", + "eco_spec_title": "TigerTag protocol specification", + "eco_spec_text": "The open specification the SDK implements: memory layout, tag types, signature scheme and reference database.", + "eco_py_title": "TigerTag SDK for Python", + "eco_py_text": "The Python port: parse, verify, write and diff. The playground page is the same file in both SDKs.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Open-source desktop inventory manager for Windows, macOS and Linux, working with TigerTag Pod and ACR122U readers.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "The official mobile app: read and write chips with the phone's built-in NFC.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repository", + "faq_title": "Questions", + "faq1_q": "How do I install the TigerTag JavaScript SDK?", + "faq1_a": "Run npm install tigertag, or pnpm add tigertag, yarn add tigertag or bun add tigertag. It needs Node.js 18 or later and no other package for parsing and verification.", + "faq2_q": "Does it need an internet connection?", + "faq2_a": "No. The reference tables and the signature public key ship in the package, so reading and verifying work offline. The network is only used for the daily table check, the product catalogue and the TigerTag+ cloud methods; offline: true or TIGERTAG_OFFLINE=1 turns it off.", + "faq3_q": "Which NFC chips and readers does it work with?", + "faq3_a": "TigerTag uses NTAG213, NTAG215 and NTAG216 chips (ISO 14443-3). The SDK takes the UID and the bytes your NFC library reads, so it is not tied to one reader; the README shows nfc-pcsc with an ACR122U.", + "faq4_q": "How does it check that a chip is genuine?", + "faq4_a": "verify() checks the chip's ECDSA-P256 signature over its UID and product identifiers, with the public key bundled in the package. It returns valid, invalid, unsigned (a Maker tag), no_key or no_uid, without a server call.", + "faq5_q": "Can it write chips?", + "faq5_a": "It builds the bytes: create(), patch(), asInit(), erase() and fromCatalog() give a TigerTag whose toBytes() is the payload your NFC library writes. The playground's Burn button writes it to the chips on connected readers.", + "faq6_q": "Is there a Python version?", + "faq6_a": "Yes: TigerTag-SDK-Python is the Python port of the same API, and both SDKs serve the same playground page.", + "faq7_q": "Which licence is the SDK under?", + "faq7_a": "Apache-2.0, which carries an express patent grant. Implementing the TigerTag protocol itself requires no licence.", + "join_title": "One command to start", + "join_text": "npm install tigertag, then TigerTag.fromPages(uid, payload).", + "join_btn": "View on npm", + "foot_licences": "SDK Apache-2.0 · Protocol specification CC-BY-4.0", + "foot_trademark": "TigerTag is a trademark of TigerTag Corp. Trademark policy", + "foot_protocol": "TigerTag protocol", + "foot_source": "Source of this page" +} diff --git a/site/i18n/es.json b/site/i18n/es.json new file mode 100644 index 0000000..6b1d56f --- /dev/null +++ b/site/i18n/es.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Español", + "lang_label": "Elegir idioma", + "theme_label": "Claro u oscuro", + "skip": "Ir al contenido", + "copy": "Copiar", + "copied": "Copiado", + "meta_title": "SDK JavaScript de TigerTag: leer y escribir etiquetas NFC", + "meta_description": "SDK de Node.js para leer, verificar, crear y modificar chips NFC TigerTag en bobinas de filamento y resina de impresión 3D. Sin conexión, Apache-2.0.", + "og_title": "SDK JavaScript de TigerTag", + "og_description": "Lee, verifica y escribe chips NFC TigerTag desde Node.js: material, color, ajustes de impresión, cantidad y firma comprobada sin conexión.", + "og_alt": "SDK JavaScript de TigerTag: npm install tigertag", + "nav_start": "Empezar", + "nav_features": "Funciones", + "nav_tools": "CLI y Electron", + "nav_playground": "Playground", + "nav_eco": "Ecosistema", + "nav_faq": "Preguntas", + "hero_kicker": "SDK JavaScript / Node.js", + "hero_headline": "Lee, verifica y escribe chips NFC TigerTag desde JavaScript.", + "hero_lead": "El SDK del protocolo abierto TigerTag para filamento y resina de impresión 3D: convierte los bytes de un chip en material, color, ajustes de impresión y cantidad, comprueba su firma ECDSA-P256 sin conexión y construye nuevas etiquetas byte a byte.", + "hero_caption": "El inicio rápido del README: funciona justo después de npm install, sin configuración.", + "cta_start": "Empezar", + "cta_npm": "Ver en npm", + "cta_github": "Código en GitHub", + "fact1_label": "Versión actual, publicada el {date}", + "fact2_label": "Usa crypto y fetch integrados", + "fact3_label": "Dependencias de ejecución para leer y verificar", + "fact4_label": "Licencia de código abierto, con concesión de patentes", + "start_title": "Empezar en tres pasos", + "start_lead": "Instala el paquete, lee un chip con tu biblioteca NFC y escribe uno nuevo. Las tablas de referencia incluidas permiten el primer uso sin red.", + "step1_title": "Instalar el paquete", + "step1_text": "Desde el registro de npm. pnpm, yarn y bun instalan el mismo paquete.", + "step2_title": "Leer un chip", + "step2_text": "Pasa el UID de 7 bytes y los 144 bytes que lee tu biblioteca NFC. Aquí con nfc-pcsc y un ACR122U.", + "step3_title": "Modificar y escribir", + "step3_text": "Cada cambio devuelve un nuevo TigerTag; toBytes() da los bytes que hay que escribir. erase() da un chip en blanco.", + "start_more": "Toda la API en el README", + "feat_title": "Qué hace el SDK", + "feat_lead": "Un solo paquete para todo lo relacionado con un chip TigerTag: leer, comprobar, escribir y mantener sus datos al día.", + "f1_title": "Leer cualquier volcado", + "f1_text": "fromPages(), fromDump() y fromFile() aceptan un UID con 80 o 144 bytes, o un volcado completo de 180 bytes. pretty(), describe(), toDict() y toRawDict() devuelven texto, un párrafo en lenguaje natural u objetos listos para JSON.", + "f2_title": "Verificar la autenticidad sin conexión", + "f2_text": "verify() comprueba la firma ECDSA-P256 que vincula el UID del chip con sus datos de producto, con la clave pública incluida en el paquete. El resultado es valid, invalid, unsigned, no_key o no_uid.", + "f3_title": "Crear y modificar etiquetas", + "f3_text": "create(), asInit(), patch() y erase() construyen los bytes del chip. Un TigerTag es inmutable: patch() devuelve uno nuevo y no permite cambiar idTigertag, idProduct, uid ni la firma.", + "f4_title": "Construir desde el catálogo", + "f4_text": "TigerTag.fromCatalog(productId) construye un TigerTag+ completo a partir del catálogo oficial de productos, descargado en el primer uso y guardado en una carpeta de caché por usuario.", + "f5_title": "Sincronizar con el fabricante", + "f5_text": "Para los chips TigerTag+, rawApi(), diffApi() y patchFromApi() obtienen los datos actuales de la marca, muestran lo que difiere del chip y lo aplican.", + "f6_title": "Datos de referencia, primero sin conexión", + "f6_text": "Las tablas de material, marca, aspecto, tipo, diámetro y unidad vienen en el paquete. Una comprobación en segundo plano las actualiza como mucho una vez al día; offline: true o TIGERTAG_OFFLINE=1 significa cero llamadas de red.", + "types_title": "Tres tipos de etiqueta", + "types_lead": "El SDK lee y escribe los tres tipos de etiqueta del protocolo TigerTag v2.2, en chips NTAG213, NTAG215 y NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Todos los campos en el chip, legibles completamente sin conexión.", + "t2_name": "TigerTag Init", + "t2_text": "Una plantilla en blanco, lista para programar. TigerTag.asInit() crea una.", + "t3_name": "TigerTag+", + "t3_text": "Todos los campos en el chip, más un ID de producto vinculado a la API en la nube para las actualizaciones del fabricante. Un chip firmado se comprueba con verify().", + "tools_title": "Desde la línea de comandos y dentro de Electron", + "tools_lead": "El paquete instala un comando tigertag y funciona en el proceso principal de una aplicación Electron.", + "cli_title": "El comando tigertag", + "cli_text": "Interpreta un volcado como texto, JSON o campos en bruto, y actualiza las tablas de referencia cuando quieras.", + "electron_title": "Dentro de una aplicación Electron", + "electron_text": "Interpreta los chips en el proceso principal, sin subprocesos que distribuir.", + "pg_title": "Pruébalo sin lector: el playground", + "pg_text": "Una página web local, servida por el propio SDK, que construye y decodifica chips TigerTag y muestra cada byte. No hace falta hardware NFC.", + "pg_point1": "Entrada y salida del SDK lado a lado: la llamada create(), luego pretty(), describe(), verify() y toDict().", + "pg_point2": "Con uno o varios lectores PC/SC como el ACR122U, los chips se leen en cuanto se colocan, y Burn escribe el contenido actual.", + "pg_point3": "El playground nunca escribe una firma: solo un fabricante certificado puede emitirla.", + "eco_title": "Parte del ecosistema TigerTag", + "eco_lead": "El SDK implementa la especificación abierta TigerTag, junto a las demás herramientas oficiales.", + "eco_spec_title": "Especificación del protocolo TigerTag", + "eco_spec_text": "La especificación abierta que implementa el SDK: estructura de memoria, tipos de etiqueta, esquema de firma y base de datos de referencia.", + "eco_py_title": "SDK de TigerTag para Python", + "eco_py_text": "La versión en Python: leer, verificar, escribir y comparar. La página del playground es el mismo archivo en ambos SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Gestor de inventario de escritorio de código abierto para Windows, macOS y Linux, compatible con TigerTag Pod y lectores ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "La aplicación móvil oficial: lee y escribe chips con el NFC integrado del teléfono.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repositorio", + "faq_title": "Preguntas", + "faq1_q": "¿Cómo se instala el SDK JavaScript de TigerTag?", + "faq1_a": "Ejecuta npm install tigertag, o pnpm add tigertag, yarn add tigertag o bun add tigertag. Necesita Node.js 18 o posterior y ningún otro paquete para leer y verificar.", + "faq2_q": "¿Necesita conexión a internet?", + "faq2_a": "No. Las tablas de referencia y la clave pública de firma vienen en el paquete, así que leer y verificar funcionan sin conexión. La red solo se usa para la comprobación diaria de tablas, el catálogo de productos y los métodos en la nube de TigerTag+; offline: true o TIGERTAG_OFFLINE=1 la desactiva.", + "faq3_q": "¿Con qué chips y lectores NFC funciona?", + "faq3_a": "TigerTag usa chips NTAG213, NTAG215 y NTAG216 (ISO 14443-3). El SDK recibe el UID y los bytes que lee tu biblioteca NFC, así que no depende de un lector concreto; el README muestra nfc-pcsc con un ACR122U.", + "faq4_q": "¿Cómo comprueba que un chip es auténtico?", + "faq4_a": "verify() comprueba la firma ECDSA-P256 del chip sobre su UID y sus identificadores de producto, con la clave pública incluida en el paquete. Devuelve valid, invalid, unsigned (una etiqueta Maker), no_key o no_uid, sin llamar a ningún servidor.", + "faq5_q": "¿Puede escribir chips?", + "faq5_a": "Construye los bytes: create(), patch(), asInit(), erase() y fromCatalog() dan un TigerTag cuyo toBytes() es el contenido que escribe tu biblioteca NFC. El botón Burn del playground lo escribe en los chips de los lectores conectados.", + "faq6_q": "¿Hay una versión para Python?", + "faq6_a": "Sí: TigerTag-SDK-Python es la versión en Python de la misma API, y ambos SDK sirven la misma página de playground.", + "faq7_q": "¿Bajo qué licencia está el SDK?", + "faq7_a": "Apache-2.0, que incluye una concesión expresa de patentes. Implementar el propio protocolo TigerTag no requiere ninguna licencia.", + "join_title": "Un comando para empezar", + "join_text": "npm install tigertag y después TigerTag.fromPages(uid, payload).", + "join_btn": "Ver en npm", + "foot_licences": "SDK Apache-2.0 · Especificación del protocolo CC-BY-4.0", + "foot_trademark": "TigerTag es una marca de TigerTag Corp. Política de marca", + "foot_protocol": "Protocolo TigerTag", + "foot_source": "Código de esta página" +} diff --git a/site/i18n/fr.json b/site/i18n/fr.json new file mode 100644 index 0000000..c295f3c --- /dev/null +++ b/site/i18n/fr.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Français", + "lang_label": "Choisir une langue", + "theme_label": "Clair ou sombre", + "skip": "Aller au contenu", + "copy": "Copier", + "copied": "Copié", + "meta_title": "SDK JavaScript TigerTag : lire et écrire les tags NFC", + "meta_description": "SDK Node.js pour lire, vérifier, créer et modifier les puces NFC TigerTag des bobines de filament et de résine. Hors ligne, Apache-2.0 : npm install tigertag.", + "og_title": "SDK JavaScript TigerTag", + "og_description": "Lisez, vérifiez et écrivez les puces NFC TigerTag depuis Node.js : matière, couleur, réglages, quantité et signature vérifiée hors ligne.", + "og_alt": "SDK JavaScript TigerTag : npm install tigertag", + "nav_start": "Démarrer", + "nav_features": "Fonctions", + "nav_tools": "CLI et Electron", + "nav_playground": "Playground", + "nav_eco": "Écosystème", + "nav_faq": "FAQ", + "hero_kicker": "SDK JavaScript / Node.js", + "hero_headline": "Lisez, vérifiez et écrivez les puces NFC TigerTag en JavaScript.", + "hero_lead": "Le SDK du protocole ouvert TigerTag pour le filament et la résine d'impression 3D : il transforme les octets d'une puce en matière, couleur, réglages d'impression et quantité, vérifie sa signature ECDSA-P256 hors ligne et construit de nouveaux tags octet par octet.", + "hero_caption": "Le démarrage rapide du README : il fonctionne dès npm install, sans configuration.", + "cta_start": "Démarrer", + "cta_npm": "Voir sur npm", + "cta_github": "Code source sur GitHub", + "fact1_label": "Version actuelle, publiée le {date}", + "fact2_label": "Utilise crypto et fetch intégrés", + "fact3_label": "Dépendance d'exécution pour lire et vérifier", + "fact4_label": "Licence open source, avec octroi de brevets", + "start_title": "Démarrer en trois étapes", + "start_lead": "Installez le paquet, lisez une puce avec votre bibliothèque NFC, écrivez-en une nouvelle. Les tables de référence incluses font fonctionner le premier lancement sans réseau.", + "step1_title": "Installer le paquet", + "step1_text": "Depuis le registre npm. pnpm, yarn et bun installent le même paquet.", + "step2_title": "Lire une puce", + "step2_text": "Passez l'UID de 7 octets et les 144 octets lus par votre bibliothèque NFC. Ici avec nfc-pcsc et un ACR122U.", + "step3_title": "Modifier et écrire", + "step3_text": "Chaque modification renvoie un nouveau TigerTag ; toBytes() donne les octets à réécrire. erase() donne une puce vierge.", + "start_more": "Toute l'API dans le README", + "feat_title": "Ce que fait le SDK", + "feat_lead": "Un seul paquet pour tous les usages d'une puce TigerTag : lire, vérifier, écrire et garder ses données à jour.", + "f1_title": "Lire tout dump", + "f1_text": "fromPages(), fromDump() et fromFile() acceptent un UID avec 80 ou 144 octets, ou un dump complet de 180 octets. pretty(), describe(), toDict() et toRawDict() renvoient du texte, un paragraphe en langage courant ou des objets prêts pour JSON.", + "f2_title": "Vérifier l'authenticité hors ligne", + "f2_text": "verify() contrôle la signature ECDSA-P256 qui lie l'UID de la puce à ses données produit, avec la clé publique incluse dans le paquet. Le résultat est valid, invalid, unsigned, no_key ou no_uid.", + "f3_title": "Créer et modifier des tags", + "f3_text": "create(), asInit(), patch() et erase() construisent les octets de la puce. Un TigerTag est immuable : patch() en renvoie un nouveau et refuse de changer idTigertag, idProduct, uid ou la signature.", + "f4_title": "Partir du catalogue", + "f4_text": "TigerTag.fromCatalog(productId) construit un TigerTag+ complet depuis le catalogue officiel des produits, téléchargé à la première utilisation et mis en cache dans un dossier propre à l'utilisateur.", + "f5_title": "Synchroniser avec le fabricant", + "f5_text": "Pour les puces TigerTag+, rawApi(), diffApi() et patchFromApi() récupèrent les données actuelles de la marque, listent ce qui diffère de la puce et l'appliquent.", + "f6_title": "Données de référence, hors ligne d'abord", + "f6_text": "Les tables matières, marques, aspects, types, diamètres et unités sont incluses dans le paquet. Une vérification en arrière-plan les met à jour au plus une fois par jour ; offline: true ou TIGERTAG_OFFLINE=1 coupe tout appel réseau.", + "types_title": "Trois types de tag", + "types_lead": "Le SDK lit et écrit les trois types de tag du protocole TigerTag v2.2, sur puces NTAG213, NTAG215 et NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Tous les champs sur la puce, lisibles entièrement hors ligne.", + "t2_name": "TigerTag Init", + "t2_text": "Un modèle vierge, prêt à être programmé. TigerTag.asInit() en crée un.", + "t3_name": "TigerTag+", + "t3_text": "Tous les champs sur la puce, plus un identifiant produit relié à l'API cloud pour les mises à jour du fabricant. Une puce signée se vérifie avec verify().", + "tools_title": "En ligne de commande et dans Electron", + "tools_lead": "Le paquet installe une commande tigertag et fonctionne dans le processus principal d'une application Electron.", + "cli_title": "La commande tigertag", + "cli_text": "Décode un dump en texte, en JSON ou en champs bruts, et met à jour les tables de référence à la demande.", + "electron_title": "Dans une application Electron", + "electron_text": "Décodez les puces dans le processus principal, sans sous-processus à embarquer.", + "pg_title": "Essayer sans lecteur : le playground", + "pg_text": "Une page web locale, servie par le SDK lui-même, qui construit et décode les puces TigerTag et montre chaque octet. Aucun matériel NFC n'est nécessaire.", + "pg_point1": "Entrée et sortie du SDK côte à côte : l'appel create(), puis pretty(), describe(), verify() et toDict().", + "pg_point2": "Avec un ou plusieurs lecteurs PC/SC comme l'ACR122U, les puces sont lues dès qu'elles sont posées, et Burn écrit le contenu courant.", + "pg_point3": "Le playground n'écrit jamais de signature : seul un fabricant certifié peut en émettre une.", + "eco_title": "Une partie de l'écosystème TigerTag", + "eco_lead": "Le SDK implémente la spécification ouverte TigerTag, aux côtés des autres outils officiels.", + "eco_spec_title": "Spécification du protocole TigerTag", + "eco_spec_text": "La spécification ouverte que le SDK implémente : organisation de la mémoire, types de tag, schéma de signature et base de référence.", + "eco_py_title": "SDK TigerTag pour Python", + "eco_py_text": "Le portage Python : lire, vérifier, écrire et comparer. La page du playground est le même fichier dans les deux SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Gestionnaire d'inventaire de bureau open source pour Windows, macOS et Linux, compatible avec le TigerTag Pod et les lecteurs ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "L'application mobile officielle : lire et écrire les puces avec le NFC intégré du téléphone.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Dépôt", + "faq_title": "Questions", + "faq1_q": "Comment installer le SDK JavaScript TigerTag ?", + "faq1_a": "Lancez npm install tigertag, ou pnpm add tigertag, yarn add tigertag ou bun add tigertag. Il faut Node.js 18 ou plus récent, et aucun autre paquet pour lire et vérifier.", + "faq2_q": "Faut-il une connexion internet ?", + "faq2_a": "Non. Les tables de référence et la clé publique de signature sont incluses dans le paquet : lecture et vérification fonctionnent hors ligne. Le réseau ne sert qu'à la vérification quotidienne des tables, au catalogue de produits et aux méthodes cloud TigerTag+ ; offline: true ou TIGERTAG_OFFLINE=1 le désactive.", + "faq3_q": "Avec quelles puces et quels lecteurs NFC fonctionne-t-il ?", + "faq3_a": "TigerTag utilise des puces NTAG213, NTAG215 et NTAG216 (ISO 14443-3). Le SDK prend l'UID et les octets lus par votre bibliothèque NFC : il n'est lié à aucun lecteur ; le README montre nfc-pcsc avec un ACR122U.", + "faq4_q": "Comment vérifie-t-il qu'une puce est authentique ?", + "faq4_a": "verify() contrôle la signature ECDSA-P256 de la puce sur son UID et ses identifiants produit, avec la clé publique incluse dans le paquet. Il renvoie valid, invalid, unsigned (un tag Maker), no_key ou no_uid, sans appel serveur.", + "faq5_q": "Peut-il écrire des puces ?", + "faq5_a": "Il construit les octets : create(), patch(), asInit(), erase() et fromCatalog() donnent un TigerTag dont toBytes() est le contenu que votre bibliothèque NFC écrit. Le bouton Burn du playground l'écrit sur les puces des lecteurs connectés.", + "faq6_q": "Existe-t-il une version Python ?", + "faq6_a": "Oui : TigerTag-SDK-Python est le portage Python de la même API, et les deux SDK servent la même page de playground.", + "faq7_q": "Sous quelle licence est le SDK ?", + "faq7_a": "Apache-2.0, qui comporte un octroi explicite de brevets. Implémenter le protocole TigerTag lui-même ne demande aucune licence.", + "join_title": "Une commande pour commencer", + "join_text": "npm install tigertag, puis TigerTag.fromPages(uid, payload).", + "join_btn": "Voir sur npm", + "foot_licences": "SDK Apache-2.0 · Spécification du protocole CC-BY-4.0", + "foot_trademark": "TigerTag est une marque de TigerTag Corp. Politique de marque", + "foot_protocol": "Protocole TigerTag", + "foot_source": "Source de cette page" +} diff --git a/site/i18n/it.json b/site/i18n/it.json new file mode 100644 index 0000000..4e36562 --- /dev/null +++ b/site/i18n/it.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Italiano", + "lang_label": "Scegli la lingua", + "theme_label": "Chiaro o scuro", + "skip": "Vai al contenuto", + "copy": "Copia", + "copied": "Copiato", + "meta_title": "SDK JavaScript TigerTag: leggere e scrivere tag NFC", + "meta_description": "SDK Node.js per leggere, verificare, creare e modificare i chip NFC TigerTag su bobine di filamento e resina per la stampa 3D. Offline, Apache-2.0.", + "og_title": "SDK JavaScript TigerTag", + "og_description": "Leggi, verifica e scrivi chip NFC TigerTag da Node.js: materiale, colore, parametri di stampa, quantità e firma verificata offline.", + "og_alt": "SDK JavaScript TigerTag: npm install tigertag", + "nav_start": "Inizia", + "nav_features": "Funzioni", + "nav_tools": "CLI ed Electron", + "nav_playground": "Playground", + "nav_eco": "Ecosistema", + "nav_faq": "FAQ", + "hero_kicker": "SDK JavaScript / Node.js", + "hero_headline": "Leggi, verifica e scrivi chip NFC TigerTag da JavaScript.", + "hero_lead": "L'SDK del protocollo aperto TigerTag per filamento e resina da stampa 3D: trasforma i byte di un chip in materiale, colore, parametri di stampa e quantità, verifica la sua firma ECDSA-P256 offline e costruisce nuovi tag byte per byte.", + "hero_caption": "L'avvio rapido del README: funziona subito dopo npm install, senza configurazione.", + "cta_start": "Inizia", + "cta_npm": "Vedi su npm", + "cta_github": "Codice su GitHub", + "fact1_label": "Versione attuale, pubblicata il {date}", + "fact2_label": "Usa crypto e fetch integrati", + "fact3_label": "Dipendenze di esecuzione per leggere e verificare", + "fact4_label": "Licenza open source, con concessione di brevetti", + "start_title": "Inizia in tre passi", + "start_lead": "Installa il pacchetto, leggi un chip con la tua libreria NFC, scrivine uno nuovo. Le tabelle di riferimento incluse fanno funzionare il primo avvio senza rete.", + "step1_title": "Installa il pacchetto", + "step1_text": "Dal registro npm. pnpm, yarn e bun installano lo stesso pacchetto.", + "step2_title": "Leggi un chip", + "step2_text": "Passa l'UID di 7 byte e i 144 byte letti dalla tua libreria NFC. Qui con nfc-pcsc e un ACR122U.", + "step3_title": "Modifica e scrivi", + "step3_text": "Ogni modifica restituisce un nuovo TigerTag; toBytes() dà i byte da riscrivere. erase() dà un chip vuoto.", + "start_more": "Tutta l'API nel README", + "feat_title": "Cosa fa l'SDK", + "feat_lead": "Un solo pacchetto per ogni aspetto di un chip TigerTag: leggere, verificare, scrivere e tenere aggiornati i dati.", + "f1_title": "Leggi qualsiasi dump", + "f1_text": "fromPages(), fromDump() e fromFile() accettano un UID con 80 o 144 byte, o un dump completo di 180 byte. pretty(), describe(), toDict() e toRawDict() restituiscono testo, un paragrafo in linguaggio naturale o oggetti pronti per JSON.", + "f2_title": "Verifica l'autenticità offline", + "f2_text": "verify() controlla la firma ECDSA-P256 che lega l'UID del chip ai suoi dati di prodotto, con la chiave pubblica inclusa nel pacchetto. Il risultato è valid, invalid, unsigned, no_key o no_uid.", + "f3_title": "Crea e modifica tag", + "f3_text": "create(), asInit(), patch() ed erase() costruiscono i byte del chip. Un TigerTag è immutabile: patch() ne restituisce uno nuovo e rifiuta di cambiare idTigertag, idProduct, uid o la firma.", + "f4_title": "Costruisci dal catalogo", + "f4_text": "TigerTag.fromCatalog(productId) costruisce un TigerTag+ completo dal catalogo ufficiale dei prodotti, scaricato al primo uso e salvato in una cartella di cache per utente.", + "f5_title": "Sincronizza con il produttore", + "f5_text": "Per i chip TigerTag+, rawApi(), diffApi() e patchFromApi() recuperano i dati attuali del marchio, elencano le differenze con il chip e le applicano.", + "f6_title": "Dati di riferimento, prima offline", + "f6_text": "Le tabelle di materiale, marca, aspetto, tipo, diametro e unità sono incluse nel pacchetto. Un controllo in background le aggiorna al massimo una volta al giorno; offline: true o TIGERTAG_OFFLINE=1 significa zero chiamate di rete.", + "types_title": "Tre tipi di tag", + "types_lead": "L'SDK legge e scrive i tre tipi di tag del protocollo TigerTag v2.2, su chip NTAG213, NTAG215 e NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Tutti i campi sul chip, leggibili completamente offline.", + "t2_name": "TigerTag Init", + "t2_text": "Un modello vuoto, pronto da programmare. TigerTag.asInit() ne crea uno.", + "t3_name": "TigerTag+", + "t3_text": "Tutti i campi sul chip, più un ID prodotto collegato all'API cloud per gli aggiornamenti del produttore. Un chip firmato si verifica con verify().", + "tools_title": "Da riga di comando e dentro Electron", + "tools_lead": "Il pacchetto installa un comando tigertag e funziona nel processo principale di un'app Electron.", + "cli_title": "Il comando tigertag", + "cli_text": "Interpreta un dump come testo, JSON o campi grezzi, e aggiorna le tabelle di riferimento su richiesta.", + "electron_title": "Dentro un'app Electron", + "electron_text": "Interpreta i chip nel processo principale, senza sottoprocessi da distribuire.", + "pg_title": "Provalo senza lettore: il playground", + "pg_text": "Una pagina web locale, servita dall'SDK stesso, che costruisce e decodifica chip TigerTag e mostra ogni byte. Non serve hardware NFC.", + "pg_point1": "Ingresso e uscita dell'SDK fianco a fianco: la chiamata create(), poi pretty(), describe(), verify() e toDict().", + "pg_point2": "Con uno o più lettori PC/SC come l'ACR122U, i chip vengono letti appena appoggiati, e Burn scrive il contenuto attuale.", + "pg_point3": "Il playground non scrive mai una firma: solo un produttore certificato può emetterne una.", + "eco_title": "Parte dell'ecosistema TigerTag", + "eco_lead": "L'SDK implementa la specifica aperta TigerTag, accanto agli altri strumenti ufficiali.", + "eco_spec_title": "Specifica del protocollo TigerTag", + "eco_spec_text": "La specifica aperta implementata dall'SDK: struttura della memoria, tipi di tag, schema di firma e database di riferimento.", + "eco_py_title": "SDK TigerTag per Python", + "eco_py_text": "La versione Python: leggere, verificare, scrivere e confrontare. La pagina del playground è lo stesso file nei due SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Gestore di inventario desktop open source per Windows, macOS e Linux, compatibile con TigerTag Pod e lettori ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "L'app mobile ufficiale: leggi e scrivi chip con l'NFC integrato del telefono.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repository", + "faq_title": "Domande", + "faq1_q": "Come si installa l'SDK JavaScript TigerTag?", + "faq1_a": "Esegui npm install tigertag, oppure pnpm add tigertag, yarn add tigertag o bun add tigertag. Richiede Node.js 18 o successivo e nessun altro pacchetto per leggere e verificare.", + "faq2_q": "Serve una connessione internet?", + "faq2_a": "No. Le tabelle di riferimento e la chiave pubblica di firma sono incluse nel pacchetto, quindi lettura e verifica funzionano offline. La rete serve solo per il controllo giornaliero delle tabelle, il catalogo prodotti e i metodi cloud TigerTag+; offline: true o TIGERTAG_OFFLINE=1 la disattiva.", + "faq3_q": "Con quali chip e lettori NFC funziona?", + "faq3_a": "TigerTag usa chip NTAG213, NTAG215 e NTAG216 (ISO 14443-3). L'SDK riceve l'UID e i byte letti dalla tua libreria NFC, quindi non è legato a un lettore; il README mostra nfc-pcsc con un ACR122U.", + "faq4_q": "Come verifica che un chip sia autentico?", + "faq4_a": "verify() controlla la firma ECDSA-P256 del chip sul suo UID e sui suoi identificativi di prodotto, con la chiave pubblica inclusa nel pacchetto. Restituisce valid, invalid, unsigned (un tag Maker), no_key o no_uid, senza chiamare alcun server.", + "faq5_q": "Può scrivere chip?", + "faq5_a": "Costruisce i byte: create(), patch(), asInit(), erase() e fromCatalog() danno un TigerTag il cui toBytes() è il contenuto che la tua libreria NFC scrive. Il pulsante Burn del playground lo scrive sui chip dei lettori collegati.", + "faq6_q": "Esiste una versione Python?", + "faq6_a": "Sì: TigerTag-SDK-Python è la versione Python della stessa API, e i due SDK servono la stessa pagina di playground.", + "faq7_q": "Con quale licenza è distribuito l'SDK?", + "faq7_a": "Apache-2.0, che include una concessione esplicita di brevetti. Implementare il protocollo TigerTag in sé non richiede alcuna licenza.", + "join_title": "Un comando per iniziare", + "join_text": "npm install tigertag, poi TigerTag.fromPages(uid, payload).", + "join_btn": "Vedi su npm", + "foot_licences": "SDK Apache-2.0 · Specifica del protocollo CC-BY-4.0", + "foot_trademark": "TigerTag è un marchio di TigerTag Corp. Politica sul marchio", + "foot_protocol": "Protocollo TigerTag", + "foot_source": "Sorgente di questa pagina" +} diff --git a/site/i18n/nl.json b/site/i18n/nl.json new file mode 100644 index 0000000..a9df2f0 --- /dev/null +++ b/site/i18n/nl.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Nederlands", + "lang_label": "Kies een taal", + "theme_label": "Licht of donker", + "skip": "Naar de inhoud", + "copy": "Kopiëren", + "copied": "Gekopieerd", + "meta_title": "TigerTag JavaScript-SDK: NFC-filamenttags lezen en schrijven", + "meta_description": "Node.js-SDK om TigerTag-NFC-chips op spoelen filament en hars voor 3D-printen te lezen, te controleren, aan te maken en aan te passen. Offline, Apache-2.0.", + "og_title": "TigerTag JavaScript-SDK", + "og_description": "Lees, controleer en schrijf TigerTag-NFC-chips vanuit Node.js: materiaal, kleur, printinstellingen, hoeveelheid en offline handtekeningcontrole.", + "og_alt": "TigerTag JavaScript-SDK: npm install tigertag", + "nav_start": "Aan de slag", + "nav_features": "Functies", + "nav_tools": "CLI en Electron", + "nav_playground": "Playground", + "nav_eco": "Ecosysteem", + "nav_faq": "Vragen", + "hero_kicker": "JavaScript- / Node.js-SDK", + "hero_headline": "Lees, controleer en schrijf TigerTag-NFC-chips met JavaScript.", + "hero_lead": "De SDK van het open TigerTag-protocol voor filament en hars voor 3D-printen: hij zet de bytes van een chip om in materiaal, kleur, printinstellingen en hoeveelheid, controleert de ECDSA-P256-handtekening offline en bouwt nieuwe tags byte voor byte.", + "hero_caption": "De snelstart uit de README: werkt direct na npm install, zonder configuratie.", + "cta_start": "Aan de slag", + "cta_npm": "Bekijk op npm", + "cta_github": "Broncode op GitHub", + "fact1_label": "Huidige versie, uitgebracht op {date}", + "fact2_label": "Gebruikt de ingebouwde crypto en fetch", + "fact3_label": "Runtime-afhankelijkheden voor lezen en controleren", + "fact4_label": "Opensourcelicentie, met octrooiverlening", + "start_title": "In drie stappen aan de slag", + "start_lead": "Installeer het pakket, lees een chip met je NFC-bibliotheek en schrijf een nieuwe. Dankzij de meegeleverde referentietabellen werkt de eerste start zonder netwerk.", + "step1_title": "Installeer het pakket", + "step1_text": "Uit het npm-register. pnpm, yarn en bun installeren hetzelfde pakket.", + "step2_title": "Lees een chip", + "step2_text": "Geef de UID van 7 bytes en de 144 bytes door die je NFC-bibliotheek leest. Hier met nfc-pcsc en een ACR122U.", + "step3_title": "Wijzig en schrijf", + "step3_text": "Elke wijziging geeft een nieuwe TigerTag terug; toBytes() levert de bytes om terug te schrijven. erase() levert een lege chip.", + "start_more": "De volledige API in de README", + "feat_title": "Wat de SDK doet", + "feat_lead": "Eén pakket voor alles rond een TigerTag-chip: lezen, controleren, schrijven en de gegevens actueel houden.", + "f1_title": "Elke dump lezen", + "f1_text": "fromPages(), fromDump() en fromFile() nemen een UID met 80 of 144 bytes, of een volledige dump van 180 bytes. pretty(), describe(), toDict() en toRawDict() geven tekst, een alinea in gewone taal of objecten die klaar zijn voor JSON.", + "f2_title": "Echtheid offline controleren", + "f2_text": "verify() controleert de ECDSA-P256-handtekening die de UID van de chip aan de productgegevens koppelt, met de publieke sleutel uit het pakket. Het resultaat is valid, invalid, unsigned, no_key of no_uid.", + "f3_title": "Tags maken en aanpassen", + "f3_text": "create(), asInit(), patch() en erase() bouwen chipbytes. Een TigerTag is onveranderlijk: patch() geeft een nieuwe terug en weigert idTigertag, idProduct, uid of de handtekening te wijzigen.", + "f4_title": "Bouwen vanuit de catalogus", + "f4_text": "TigerTag.fromCatalog(productId) bouwt een volledige TigerTag+ uit de officiële productcatalogus, die bij het eerste gebruik wordt gedownload en per gebruiker in een cachemap wordt bewaard.", + "f5_title": "Synchroniseren met de fabrikant", + "f5_text": "Voor TigerTag+-chips halen rawApi(), diffApi() en patchFromApi() de actuele productgegevens van het merk op, tonen ze wat afwijkt van de chip en passen ze het toe.", + "f6_title": "Referentiegegevens, eerst offline", + "f6_text": "De tabellen voor materiaal, merk, uiterlijk, type, diameter en eenheid zitten in het pakket. Een controle op de achtergrond werkt ze hooguit één keer per dag bij; offline: true of TIGERTAG_OFFLINE=1 betekent nul netwerkverzoeken.", + "types_title": "Drie soorten tags", + "types_lead": "De SDK leest en schrijft de drie tagtypen van TigerTag-protocol v2.2, op NTAG213-, NTAG215- en NTAG216-chips.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Alle velden op de chip, volledig offline leesbaar.", + "t2_name": "TigerTag Init", + "t2_text": "Een leeg sjabloon, klaar om te programmeren. TigerTag.asInit() maakt er een.", + "t3_name": "TigerTag+", + "t3_text": "Alle velden op de chip, plus een product-ID dat gekoppeld is aan de cloud-API voor updates van de fabrikant. Een ondertekende chip controleer je met verify().", + "tools_title": "Vanaf de opdrachtregel en in Electron", + "tools_lead": "Het pakket installeert de opdracht tigertag en draait in het hoofdproces van een Electron-app.", + "cli_title": "De opdracht tigertag", + "cli_text": "Leest een dump als tekst, JSON of ruwe velden en werkt de referentietabellen op verzoek bij.", + "electron_title": "In een Electron-app", + "electron_text": "Chips uitlezen in het hoofdproces, zonder extra subproces.", + "pg_title": "Probeer het zonder lezer: de playground", + "pg_text": "Een lokale webpagina, geserveerd door de SDK zelf, die TigerTag-chips bouwt en decodeert en elke byte toont. Geen NFC-hardware nodig.", + "pg_point1": "In- en uitvoer van de SDK naast elkaar: de aanroep create(), daarna pretty(), describe(), verify() en toDict().", + "pg_point2": "Met een of meer PC/SC-lezers zoals de ACR122U worden chips gelezen zodra ze erop liggen, en Burn schrijft de huidige inhoud.", + "pg_point3": "De playground schrijft nooit een handtekening: alleen een gecertificeerde fabrikant kan er een uitgeven.", + "eco_title": "Onderdeel van het TigerTag-ecosysteem", + "eco_lead": "De SDK implementeert de open TigerTag-specificatie, naast de andere officiële tools.", + "eco_spec_title": "Specificatie van het TigerTag-protocol", + "eco_spec_text": "De open specificatie die de SDK implementeert: geheugenindeling, tagtypen, handtekeningschema en referentiedatabase.", + "eco_py_title": "TigerTag-SDK voor Python", + "eco_py_text": "De Python-versie: lezen, controleren, schrijven en vergelijken. De playgroundpagina is in beide SDK's hetzelfde bestand.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Open-source voorraadbeheer voor de desktop op Windows, macOS en Linux, werkt met de TigerTag Pod en ACR122U-lezers.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "De officiële mobiele app: chips lezen en schrijven met de ingebouwde NFC van de telefoon.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repository", + "faq_title": "Vragen", + "faq1_q": "Hoe installeer ik de TigerTag JavaScript-SDK?", + "faq1_a": "Voer npm install tigertag uit, of pnpm add tigertag, yarn add tigertag of bun add tigertag. Hij vraagt Node.js 18 of nieuwer en geen ander pakket om te lezen en te controleren.", + "faq2_q": "Is er een internetverbinding nodig?", + "faq2_a": "Nee. De referentietabellen en de publieke handtekeningsleutel zitten in het pakket, dus lezen en controleren werken offline. Het netwerk wordt alleen gebruikt voor de dagelijkse tabelcontrole, de productcatalogus en de TigerTag+-cloudmethoden; offline: true of TIGERTAG_OFFLINE=1 zet het uit.", + "faq3_q": "Met welke NFC-chips en lezers werkt hij?", + "faq3_a": "TigerTag gebruikt NTAG213-, NTAG215- en NTAG216-chips (ISO 14443-3). De SDK neemt de UID en de bytes die je NFC-bibliotheek leest, dus hij is niet aan één lezer gebonden; de README toont nfc-pcsc met een ACR122U.", + "faq4_q": "Hoe controleert hij of een chip echt is?", + "faq4_a": "verify() controleert de ECDSA-P256-handtekening van de chip over de UID en de product-ID's, met de publieke sleutel uit het pakket. Het resultaat is valid, invalid, unsigned (een Maker-tag), no_key of no_uid, zonder serveraanroep.", + "faq5_q": "Kan hij chips schrijven?", + "faq5_a": "Hij bouwt de bytes: create(), patch(), asInit(), erase() en fromCatalog() geven een TigerTag waarvan toBytes() de inhoud is die je NFC-bibliotheek schrijft. De Burn-knop van de playground schrijft die naar de chips op aangesloten lezers.", + "faq6_q": "Is er een Python-versie?", + "faq6_a": "Ja: TigerTag-SDK-Python is de Python-versie van dezelfde API, en beide SDK's serveren dezelfde playgroundpagina.", + "faq7_q": "Onder welke licentie valt de SDK?", + "faq7_a": "Apache-2.0, met een uitdrukkelijke octrooiverlening. Voor het implementeren van het TigerTag-protocol zelf is geen licentie nodig.", + "join_title": "Eén opdracht om te beginnen", + "join_text": "npm install tigertag, daarna TigerTag.fromPages(uid, payload).", + "join_btn": "Bekijk op npm", + "foot_licences": "SDK Apache-2.0 · Protocolspecificatie CC-BY-4.0", + "foot_trademark": "TigerTag is een handelsmerk van TigerTag Corp. Merkbeleid", + "foot_protocol": "TigerTag-protocol", + "foot_source": "Bron van deze pagina" +} diff --git a/site/i18n/pl.json b/site/i18n/pl.json new file mode 100644 index 0000000..1ef7bc6 --- /dev/null +++ b/site/i18n/pl.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Polski", + "lang_label": "Wybierz język", + "theme_label": "Jasny lub ciemny", + "skip": "Przejdź do treści", + "copy": "Kopiuj", + "copied": "Skopiowano", + "meta_title": "TigerTag JavaScript SDK: odczyt i zapis tagów NFC filamentu", + "meta_description": "SDK dla Node.js do odczytu, weryfikacji, tworzenia i zmiany chipów NFC TigerTag na szpulach filamentu i żywicy do druku 3D. Offline, Apache-2.0.", + "og_title": "TigerTag JavaScript SDK dla Node.js", + "og_description": "Odczytuj, weryfikuj i zapisuj chipy NFC TigerTag w Node.js: materiał, kolor, ustawienia druku, ilość i podpis sprawdzany offline.", + "og_alt": "TigerTag JavaScript SDK: npm install tigertag", + "nav_start": "Pierwsze kroki", + "nav_features": "Funkcje", + "nav_tools": "CLI i Electron", + "nav_playground": "Playground", + "nav_eco": "Ekosystem", + "nav_faq": "FAQ", + "hero_kicker": "SDK dla JavaScript / Node.js", + "hero_headline": "Odczytuj, weryfikuj i zapisuj chipy NFC TigerTag w JavaScript.", + "hero_lead": "SDK otwartego protokołu TigerTag dla filamentu i żywicy do druku 3D: zamienia bajty chipu na materiał, kolor, ustawienia druku i ilość, sprawdza jego podpis ECDSA-P256 offline i buduje nowe tagi bajt po bajcie.", + "hero_caption": "Szybki start z README: działa zaraz po npm install, bez konfiguracji.", + "cta_start": "Zacznij", + "cta_npm": "Zobacz w npm", + "cta_github": "Kod źródłowy na GitHubie", + "fact1_label": "Bieżąca wersja, wydana {date}", + "fact2_label": "Używa wbudowanych crypto i fetch", + "fact3_label": "Zależności uruchomieniowe do odczytu i weryfikacji", + "fact4_label": "Licencja open source z udzieleniem patentów", + "start_title": "Zacznij w trzech krokach", + "start_lead": "Zainstaluj pakiet, odczytaj chip swoją biblioteką NFC i zapisz nowy. Dołączone tabele referencyjne pozwalają na pierwsze uruchomienie bez sieci.", + "step1_title": "Zainstaluj pakiet", + "step1_text": "Z rejestru npm. pnpm, yarn i bun instalują ten sam pakiet.", + "step2_title": "Odczytaj chip", + "step2_text": "Przekaż 7-bajtowy UID i 144 bajty odczytane przez bibliotekę NFC. Tutaj z nfc-pcsc i czytnikiem ACR122U.", + "step3_title": "Zmień i zapisz", + "step3_text": "Każda zmiana zwraca nowy TigerTag; toBytes() daje bajty do zapisania. erase() daje pusty chip.", + "start_more": "Pełne API w README", + "feat_title": "Co robi SDK", + "feat_lead": "Jeden pakiet do wszystkiego, co dotyczy chipu TigerTag: odczytu, sprawdzania, zapisu i aktualizacji danych.", + "f1_title": "Odczyt dowolnego zrzutu", + "f1_text": "fromPages(), fromDump() i fromFile() przyjmują UID z 80 lub 144 bajtami albo pełny zrzut 180 bajtów. pretty(), describe(), toDict() i toRawDict() zwracają tekst, akapit prostym językiem lub obiekty gotowe do JSON.", + "f2_title": "Weryfikacja autentyczności offline", + "f2_text": "verify() sprawdza podpis ECDSA-P256, który wiąże UID chipu z danymi produktu, kluczem publicznym dołączonym do pakietu. Wynik to valid, invalid, unsigned, no_key lub no_uid.", + "f3_title": "Tworzenie i zmiana tagów", + "f3_text": "create(), asInit(), patch() i erase() budują bajty chipu. TigerTag jest niezmienny: patch() zwraca nowy obiekt i nie pozwala zmienić idTigertag, idProduct, uid ani podpisu.", + "f4_title": "Budowanie z katalogu", + "f4_text": "TigerTag.fromCatalog(productId) buduje kompletny TigerTag+ z oficjalnego katalogu produktów, pobieranego przy pierwszym użyciu i przechowywanego w pamięci podręcznej użytkownika.", + "f5_title": "Synchronizacja z producentem", + "f5_text": "Dla chipów TigerTag+ rawApi(), diffApi() i patchFromApi() pobierają aktualne dane produktu marki, pokazują różnice względem chipu i je stosują.", + "f6_title": "Dane referencyjne, najpierw offline", + "f6_text": "Tabele materiałów, marek, wyglądu, typów, średnic i jednostek są w pakiecie. Sprawdzenie w tle aktualizuje je najwyżej raz dziennie; offline: true lub TIGERTAG_OFFLINE=1 oznacza zero połączeń sieciowych.", + "types_title": "Trzy rodzaje tagów", + "types_lead": "SDK odczytuje i zapisuje trzy typy tagów protokołu TigerTag v2.2 na chipach NTAG213, NTAG215 i NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Wszystkie pola na chipie, czytelne całkowicie offline.", + "t2_name": "TigerTag Init", + "t2_text": "Pusty szablon, gotowy do zaprogramowania. Tworzy go TigerTag.asInit().", + "t3_name": "TigerTag+", + "t3_text": "Wszystkie pola na chipie oraz identyfikator produktu powiązany z API w chmurze, z którego pochodzą aktualizacje producenta. Podpisany chip sprawdza się przez verify().", + "tools_title": "Z wiersza poleceń i w Electronie", + "tools_lead": "Pakiet instaluje polecenie tigertag i działa w procesie głównym aplikacji Electron.", + "cli_title": "Polecenie tigertag", + "cli_text": "Odczytuje zrzut jako tekst, JSON lub surowe pola i na żądanie aktualizuje tabele referencyjne.", + "electron_title": "W aplikacji Electron", + "electron_text": "Odczyt chipów w procesie głównym, bez dodatkowego podprocesu.", + "pg_title": "Wypróbuj bez czytnika: playground", + "pg_text": "Lokalna strona internetowa, serwowana przez samo SDK, która buduje i dekoduje chipy TigerTag i pokazuje każdy bajt. Sprzęt NFC nie jest potrzebny.", + "pg_point1": "Wejście i wyjście SDK obok siebie: wywołanie create(), a następnie pretty(), describe(), verify() i toDict().", + "pg_point2": "Z jednym lub kilkoma czytnikami PC/SC, np. ACR122U, chipy są odczytywane od razu po położeniu, a Burn zapisuje bieżącą zawartość.", + "pg_point3": "Playground nigdy nie zapisuje podpisu: może go wystawić tylko certyfikowany producent.", + "eco_title": "Część ekosystemu TigerTag", + "eco_lead": "SDK implementuje otwartą specyfikację TigerTag, obok pozostałych oficjalnych narzędzi.", + "eco_spec_title": "Specyfikacja protokołu TigerTag", + "eco_spec_text": "Otwarta specyfikacja implementowana przez SDK: układ pamięci, typy tagów, schemat podpisu i referencyjna baza danych.", + "eco_py_title": "TigerTag SDK dla Pythona", + "eco_py_text": "Wersja w Pythonie: odczyt, weryfikacja, zapis i porównywanie. Strona playground to ten sam plik w obu SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Otwartoźródłowy program do zarządzania magazynem dla Windows, macOS i Linuksa, współpracujący z TigerTag Pod i czytnikami ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "Oficjalna aplikacja mobilna: odczyt i zapis chipów wbudowanym NFC telefonu.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repozytorium", + "faq_title": "Pytania", + "faq1_q": "Jak zainstalować TigerTag JavaScript SDK?", + "faq1_a": "Uruchom npm install tigertag albo pnpm add tigertag, yarn add tigertag lub bun add tigertag. Wymaga Node.js 18 lub nowszego i żadnego innego pakietu do odczytu i weryfikacji.", + "faq2_q": "Czy potrzebne jest połączenie z internetem?", + "faq2_a": "Nie. Tabele referencyjne i klucz publiczny podpisu są w pakiecie, więc odczyt i weryfikacja działają offline. Sieć służy tylko do codziennego sprawdzenia tabel, katalogu produktów i metod chmurowych TigerTag+; offline: true lub TIGERTAG_OFFLINE=1 ją wyłącza.", + "faq3_q": "Z jakimi chipami i czytnikami NFC działa?", + "faq3_a": "TigerTag używa chipów NTAG213, NTAG215 i NTAG216 (ISO 14443-3). SDK przyjmuje UID i bajty odczytane przez bibliotekę NFC, więc nie jest związane z jednym czytnikiem; README pokazuje nfc-pcsc z ACR122U.", + "faq4_q": "Jak sprawdza, czy chip jest oryginalny?", + "faq4_a": "verify() sprawdza podpis ECDSA-P256 chipu obejmujący jego UID i identyfikatory produktu, kluczem publicznym dołączonym do pakietu. Zwraca valid, invalid, unsigned (tag Maker), no_key lub no_uid, bez łączenia się z serwerem.", + "faq5_q": "Czy może zapisywać chipy?", + "faq5_a": "Buduje bajty: create(), patch(), asInit(), erase() i fromCatalog() zwracają TigerTag, którego toBytes() to zawartość zapisywana przez bibliotekę NFC. Przycisk Burn w playground zapisuje ją na chipach podłączonych czytników.", + "faq6_q": "Czy jest wersja w Pythonie?", + "faq6_a": "Tak: TigerTag-SDK-Python to wersja tego samego API w Pythonie, a oba SDK serwują tę samą stronę playground.", + "faq7_q": "Na jakiej licencji jest SDK?", + "faq7_a": "Apache-2.0, która zawiera wyraźne udzielenie praw patentowych. Implementacja samego protokołu TigerTag nie wymaga żadnej licencji.", + "join_title": "Jedno polecenie na start", + "join_text": "npm install tigertag, a potem TigerTag.fromPages(uid, payload).", + "join_btn": "Zobacz w npm", + "foot_licences": "SDK Apache-2.0 · Specyfikacja protokołu CC-BY-4.0", + "foot_trademark": "TigerTag jest znakiem towarowym TigerTag Corp. Zasady korzystania ze znaku", + "foot_protocol": "Protokół TigerTag", + "foot_source": "Źródło tej strony" +} diff --git a/site/i18n/pt-pt.json b/site/i18n/pt-pt.json new file mode 100644 index 0000000..e9662f4 --- /dev/null +++ b/site/i18n/pt-pt.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Português (PT)", + "lang_label": "Escolher idioma", + "theme_label": "Claro ou escuro", + "skip": "Saltar para o conteúdo", + "copy": "Copiar", + "copied": "Copiado", + "meta_title": "SDK JavaScript TigerTag: ler e escrever etiquetas NFC", + "meta_description": "SDK Node.js para ler, verificar, criar e alterar chips NFC TigerTag em bobinas de filamento e resina para impressão 3D. Sem ligação, Apache-2.0.", + "og_title": "SDK JavaScript da TigerTag", + "og_description": "Leia, verifique e escreva chips NFC TigerTag a partir de Node.js: material, cor, definições de impressão, quantidade e assinatura verificada sem ligação.", + "og_alt": "SDK JavaScript da TigerTag: npm install tigertag", + "nav_start": "Começar", + "nav_features": "Funcionalidades", + "nav_tools": "CLI e Electron", + "nav_playground": "Playground", + "nav_eco": "Ecossistema", + "nav_faq": "Perguntas", + "hero_kicker": "SDK JavaScript / Node.js", + "hero_headline": "Leia, verifique e escreva chips NFC TigerTag em JavaScript.", + "hero_lead": "O SDK do protocolo aberto TigerTag para filamento e resina de impressão 3D: converte os bytes de um chip em material, cor, definições de impressão e quantidade, verifica a assinatura ECDSA-P256 sem ligação e constrói novas etiquetas byte a byte.", + "hero_caption": "O arranque rápido do README: funciona logo após o npm install, sem configuração.", + "cta_start": "Começar", + "cta_npm": "Ver no npm", + "cta_github": "Código-fonte no GitHub", + "fact1_label": "Versão atual, publicada a {date}", + "fact2_label": "Utiliza o crypto e o fetch incorporados", + "fact3_label": "Dependências de execução para ler e verificar", + "fact4_label": "Licença de código aberto, com concessão de patentes", + "start_title": "Começar em três passos", + "start_lead": "Instale o pacote, leia um chip com a sua biblioteca NFC e escreva um novo. As tabelas de referência incluídas permitem a primeira execução sem rede.", + "step1_title": "Instalar o pacote", + "step1_text": "A partir do registo npm. pnpm, yarn e bun instalam o mesmo pacote.", + "step2_title": "Ler um chip", + "step2_text": "Passe o UID de 7 bytes e os 144 bytes lidos pela sua biblioteca NFC. Aqui com nfc-pcsc e um ACR122U.", + "step3_title": "Alterar e escrever", + "step3_text": "Cada alteração devolve um novo TigerTag; toBytes() dá os bytes a escrever. erase() dá um chip em branco.", + "start_more": "A API completa no README", + "feat_title": "O que o SDK faz", + "feat_lead": "Um único pacote para tudo o que diz respeito a um chip TigerTag: ler, verificar, escrever e manter os dados atualizados.", + "f1_title": "Ler qualquer dump", + "f1_text": "fromPages(), fromDump() e fromFile() aceitam um UID com 80 ou 144 bytes, ou um dump completo de 180 bytes. pretty(), describe(), toDict() e toRawDict() devolvem texto, um parágrafo em linguagem corrente ou objetos prontos para JSON.", + "f2_title": "Verificar a autenticidade sem ligação", + "f2_text": "verify() verifica a assinatura ECDSA-P256 que associa o UID do chip aos dados do produto, com a chave pública incluída no pacote. O resultado é valid, invalid, unsigned, no_key ou no_uid.", + "f3_title": "Criar e alterar etiquetas", + "f3_text": "create(), asInit(), patch() e erase() constroem os bytes do chip. Um TigerTag é imutável: patch() devolve um novo e recusa alterar idTigertag, idProduct, uid ou a assinatura.", + "f4_title": "Construir a partir do catálogo", + "f4_text": "TigerTag.fromCatalog(productId) constrói um TigerTag+ completo a partir do catálogo oficial de produtos, transferido na primeira utilização e guardado em cache numa pasta por utilizador.", + "f5_title": "Sincronizar com o fabricante", + "f5_text": "Para chips TigerTag+, rawApi(), diffApi() e patchFromApi() obtêm os dados atuais da marca, listam o que difere do chip e aplicam-no.", + "f6_title": "Dados de referência, primeiro sem ligação", + "f6_text": "As tabelas de material, marca, aspeto, tipo, diâmetro e unidade vêm no pacote. Uma verificação em segundo plano atualiza-as no máximo uma vez por dia; offline: true ou TIGERTAG_OFFLINE=1 significa zero chamadas de rede.", + "types_title": "Três tipos de etiqueta", + "types_lead": "O SDK lê e escreve os três tipos de etiqueta do protocolo TigerTag v2.2, em chips NTAG213, NTAG215 e NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Todos os campos no chip, legíveis totalmente sem ligação.", + "t2_name": "TigerTag Init", + "t2_text": "Um modelo em branco, pronto a programar. TigerTag.asInit() cria um.", + "t3_name": "TigerTag+", + "t3_text": "Todos os campos no chip, mais um identificador de produto associado à API na nuvem para as atualizações do fabricante. Um chip assinado verifica-se com verify().", + "tools_title": "Na linha de comandos e dentro do Electron", + "tools_lead": "O pacote instala o comando tigertag e funciona no processo principal de uma aplicação Electron.", + "cli_title": "O comando tigertag", + "cli_text": "Interpreta um dump como texto, JSON ou campos em bruto, e atualiza as tabelas de referência quando pretender.", + "electron_title": "Dentro de uma aplicação Electron", + "electron_text": "Interprete os chips no processo principal, sem subprocessos para distribuir.", + "pg_title": "Experimente sem leitor: o playground", + "pg_text": "Uma página web local, servida pelo próprio SDK, que constrói e descodifica chips TigerTag e mostra cada byte. Não é preciso hardware NFC.", + "pg_point1": "Entrada e saída do SDK lado a lado: a chamada create(), depois pretty(), describe(), verify() e toDict().", + "pg_point2": "Com um ou mais leitores PC/SC como o ACR122U, os chips são lidos logo que pousados, e Burn escreve o conteúdo atual.", + "pg_point3": "O playground nunca escreve uma assinatura: só um fabricante certificado a pode emitir.", + "eco_title": "Parte do ecossistema TigerTag", + "eco_lead": "O SDK implementa a especificação aberta TigerTag, a par das outras ferramentas oficiais.", + "eco_spec_title": "Especificação do protocolo TigerTag", + "eco_spec_text": "A especificação aberta que o SDK implementa: organização da memória, tipos de etiqueta, esquema de assinatura e base de dados de referência.", + "eco_py_title": "SDK TigerTag para Python", + "eco_py_text": "A versão Python: ler, verificar, escrever e comparar. A página do playground é o mesmo ficheiro nos dois SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Gestor de inventário de secretária, de código aberto, para Windows, macOS e Linux, compatível com o TigerTag Pod e leitores ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "A aplicação móvel oficial: leia e escreva chips com o NFC do telemóvel.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repositório", + "faq_title": "Perguntas", + "faq1_q": "Como se instala o SDK JavaScript da TigerTag?", + "faq1_a": "Execute npm install tigertag, ou pnpm add tigertag, yarn add tigertag ou bun add tigertag. Requer Node.js 18 ou mais recente e nenhum outro pacote para ler e verificar.", + "faq2_q": "É preciso ligação à internet?", + "faq2_a": "Não. As tabelas de referência e a chave pública de assinatura vêm no pacote, pelo que a leitura e a verificação funcionam sem ligação. A rede só é usada para a verificação diária das tabelas, o catálogo de produtos e os métodos na nuvem do TigerTag+; offline: true ou TIGERTAG_OFFLINE=1 desliga-a.", + "faq3_q": "Com que chips e leitores NFC funciona?", + "faq3_a": "A TigerTag usa chips NTAG213, NTAG215 e NTAG216 (ISO 14443-3). O SDK recebe o UID e os bytes lidos pela sua biblioteca NFC, pelo que não depende de um leitor; o README mostra nfc-pcsc com um ACR122U.", + "faq4_q": "Como verifica que um chip é autêntico?", + "faq4_a": "verify() verifica a assinatura ECDSA-P256 do chip sobre o UID e os identificadores do produto, com a chave pública incluída no pacote. Devolve valid, invalid, unsigned (uma etiqueta Maker), no_key ou no_uid, sem contactar nenhum servidor.", + "faq5_q": "Consegue escrever chips?", + "faq5_a": "Constrói os bytes: create(), patch(), asInit(), erase() e fromCatalog() dão um TigerTag cujo toBytes() é o conteúdo que a sua biblioteca NFC escreve. O botão Burn do playground escreve-o nos chips dos leitores ligados.", + "faq6_q": "Existe uma versão em Python?", + "faq6_a": "Sim: TigerTag-SDK-Python é a versão Python da mesma API, e os dois SDK servem a mesma página de playground.", + "faq7_q": "Qual é a licença do SDK?", + "faq7_a": "Apache-2.0, que inclui uma concessão expressa de patentes. Implementar o próprio protocolo TigerTag não requer qualquer licença.", + "join_title": "Um comando para começar", + "join_text": "npm install tigertag e, a seguir, TigerTag.fromPages(uid, payload).", + "join_btn": "Ver no npm", + "foot_licences": "SDK Apache-2.0 · Especificação do protocolo CC-BY-4.0", + "foot_trademark": "TigerTag é uma marca da TigerTag Corp. Política de marca", + "foot_protocol": "Protocolo TigerTag", + "foot_source": "Código-fonte desta página" +} diff --git a/site/i18n/pt.json b/site/i18n/pt.json new file mode 100644 index 0000000..a21dfc7 --- /dev/null +++ b/site/i18n/pt.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Português (BR)", + "lang_label": "Escolher idioma", + "theme_label": "Claro ou escuro", + "skip": "Pular para o conteúdo", + "copy": "Copiar", + "copied": "Copiado", + "meta_title": "SDK JavaScript TigerTag: ler e gravar tags NFC de filamento", + "meta_description": "SDK Node.js para ler, verificar, criar e editar chips NFC TigerTag em carretéis de filamento e resina de impressão 3D. Offline, Apache-2.0.", + "og_title": "SDK JavaScript TigerTag", + "og_description": "Leia, verifique e grave chips NFC TigerTag com Node.js: material, cor, ajustes de impressão, quantidade e assinatura conferida offline.", + "og_alt": "SDK JavaScript TigerTag: npm install tigertag", + "nav_start": "Começar", + "nav_features": "Recursos", + "nav_tools": "CLI e Electron", + "nav_playground": "Playground", + "nav_eco": "Ecossistema", + "nav_faq": "Perguntas", + "hero_kicker": "SDK JavaScript / Node.js", + "hero_headline": "Leia, verifique e grave chips NFC TigerTag com JavaScript.", + "hero_lead": "O SDK do protocolo aberto TigerTag para filamento e resina de impressão 3D: transforma os bytes de um chip em material, cor, ajustes de impressão e quantidade, confere a assinatura ECDSA-P256 offline e monta novas tags byte a byte.", + "hero_caption": "O início rápido do README: funciona logo após o npm install, sem configuração.", + "cta_start": "Começar", + "cta_npm": "Ver no npm", + "cta_github": "Código no GitHub", + "fact1_label": "Versão atual, lançada em {date}", + "fact2_label": "Usa crypto e fetch nativos", + "fact3_label": "Dependências de execução para ler e verificar", + "fact4_label": "Licença de código aberto, com concessão de patentes", + "start_title": "Comece em três passos", + "start_lead": "Instale o pacote, leia um chip com sua biblioteca NFC e grave um novo. As tabelas de referência incluídas fazem a primeira execução funcionar sem rede.", + "step1_title": "Instalar o pacote", + "step1_text": "Pelo registro do npm. pnpm, yarn e bun instalam o mesmo pacote.", + "step2_title": "Ler um chip", + "step2_text": "Passe o UID de 7 bytes e os 144 bytes lidos pela sua biblioteca NFC. Aqui com nfc-pcsc e um ACR122U.", + "step3_title": "Alterar e gravar", + "step3_text": "Cada alteração retorna um novo TigerTag; toBytes() dá os bytes para gravar. erase() dá um chip em branco.", + "start_more": "A API completa no README", + "feat_title": "O que o SDK faz", + "feat_lead": "Um só pacote para tudo em um chip TigerTag: ler, conferir, gravar e manter os dados atualizados.", + "f1_title": "Ler qualquer dump", + "f1_text": "fromPages(), fromDump() e fromFile() aceitam um UID com 80 ou 144 bytes, ou um dump completo de 180 bytes. pretty(), describe(), toDict() e toRawDict() retornam texto, um parágrafo em linguagem simples ou objetos prontos para JSON.", + "f2_title": "Verificar a autenticidade offline", + "f2_text": "verify() confere a assinatura ECDSA-P256 que liga o UID do chip aos dados do produto, com a chave pública incluída no pacote. O resultado é valid, invalid, unsigned, no_key ou no_uid.", + "f3_title": "Criar e editar tags", + "f3_text": "create(), asInit(), patch() e erase() montam os bytes do chip. Um TigerTag é imutável: patch() retorna um novo e não deixa alterar idTigertag, idProduct, uid nem a assinatura.", + "f4_title": "Montar a partir do catálogo", + "f4_text": "TigerTag.fromCatalog(productId) monta um TigerTag+ completo a partir do catálogo oficial de produtos, baixado no primeiro uso e guardado em cache numa pasta por usuário.", + "f5_title": "Sincronizar com o fabricante", + "f5_text": "Para chips TigerTag+, rawApi(), diffApi() e patchFromApi() buscam os dados atuais da marca, listam o que difere do chip e aplicam as mudanças.", + "f6_title": "Dados de referência, offline primeiro", + "f6_text": "As tabelas de material, marca, aspecto, tipo, diâmetro e unidade vêm no pacote. Uma verificação em segundo plano as atualiza no máximo uma vez por dia; offline: true ou TIGERTAG_OFFLINE=1 significa nenhuma chamada de rede.", + "types_title": "Três tipos de tag", + "types_lead": "O SDK lê e grava os três tipos de tag do protocolo TigerTag v2.2, em chips NTAG213, NTAG215 e NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Todos os campos no chip, legíveis totalmente offline.", + "t2_name": "TigerTag Init", + "t2_text": "Um modelo em branco, pronto para programar. TigerTag.asInit() cria um.", + "t3_name": "TigerTag+", + "t3_text": "Todos os campos no chip, mais um ID de produto ligado à API na nuvem para atualizações do fabricante. Um chip assinado é conferido com verify().", + "tools_title": "Na linha de comando e dentro do Electron", + "tools_lead": "O pacote instala o comando tigertag e roda no processo principal de um app Electron.", + "cli_title": "O comando tigertag", + "cli_text": "Interpreta um dump como texto, JSON ou campos brutos, e atualiza as tabelas de referência quando você quiser.", + "electron_title": "Dentro de um app Electron", + "electron_text": "Interprete os chips no processo principal, sem subprocesso para distribuir.", + "pg_title": "Experimente sem leitor: o playground", + "pg_text": "Uma página web local, servida pelo próprio SDK, que monta e decodifica chips TigerTag e mostra cada byte. Não precisa de hardware NFC.", + "pg_point1": "Entrada e saída do SDK lado a lado: a chamada create(), depois pretty(), describe(), verify() e toDict().", + "pg_point2": "Com um ou mais leitores PC/SC como o ACR122U, os chips são lidos assim que colocados, e Burn grava o conteúdo atual.", + "pg_point3": "O playground nunca grava uma assinatura: só um fabricante certificado pode emiti-la.", + "eco_title": "Parte do ecossistema TigerTag", + "eco_lead": "O SDK implementa a especificação aberta TigerTag, ao lado das outras ferramentas oficiais.", + "eco_spec_title": "Especificação do protocolo TigerTag", + "eco_spec_text": "A especificação aberta que o SDK implementa: layout de memória, tipos de tag, esquema de assinatura e banco de dados de referência.", + "eco_py_title": "SDK TigerTag para Python", + "eco_py_text": "A versão em Python: ler, verificar, gravar e comparar. A página do playground é o mesmo arquivo nos dois SDKs.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Gerenciador de estoque para desktop, de código aberto, para Windows, macOS e Linux, compatível com TigerTag Pod e leitores ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "O app móvel oficial: leia e grave chips com o NFC do celular.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Repositório", + "faq_title": "Perguntas", + "faq1_q": "Como instalo o SDK JavaScript TigerTag?", + "faq1_a": "Rode npm install tigertag, ou pnpm add tigertag, yarn add tigertag ou bun add tigertag. Ele precisa do Node.js 18 ou mais recente e de nenhum outro pacote para ler e verificar.", + "faq2_q": "Precisa de conexão com a internet?", + "faq2_a": "Não. As tabelas de referência e a chave pública de assinatura vêm no pacote, então leitura e verificação funcionam offline. A rede só é usada para a verificação diária das tabelas, o catálogo de produtos e os métodos de nuvem do TigerTag+; offline: true ou TIGERTAG_OFFLINE=1 a desliga.", + "faq3_q": "Com quais chips e leitores NFC ele funciona?", + "faq3_a": "O TigerTag usa chips NTAG213, NTAG215 e NTAG216 (ISO 14443-3). O SDK recebe o UID e os bytes lidos pela sua biblioteca NFC, então não depende de um leitor específico; o README mostra nfc-pcsc com um ACR122U.", + "faq4_q": "Como ele confere se um chip é autêntico?", + "faq4_a": "verify() confere a assinatura ECDSA-P256 do chip sobre o UID e os identificadores do produto, com a chave pública incluída no pacote. Retorna valid, invalid, unsigned (uma tag Maker), no_key ou no_uid, sem chamar servidor.", + "faq5_q": "Ele grava chips?", + "faq5_a": "Ele monta os bytes: create(), patch(), asInit(), erase() e fromCatalog() dão um TigerTag cujo toBytes() é o conteúdo que sua biblioteca NFC grava. O botão Burn do playground grava nos chips dos leitores conectados.", + "faq6_q": "Existe uma versão em Python?", + "faq6_a": "Sim: TigerTag-SDK-Python é a versão em Python da mesma API, e os dois SDKs servem a mesma página de playground.", + "faq7_q": "Qual é a licença do SDK?", + "faq7_a": "Apache-2.0, que traz uma concessão expressa de patentes. Implementar o próprio protocolo TigerTag não exige licença.", + "join_title": "Um comando para começar", + "join_text": "npm install tigertag e depois TigerTag.fromPages(uid, payload).", + "join_btn": "Ver no npm", + "foot_licences": "SDK Apache-2.0 · Especificação do protocolo CC-BY-4.0", + "foot_trademark": "TigerTag é uma marca da TigerTag Corp. Política de marca", + "foot_protocol": "Protocolo TigerTag", + "foot_source": "Código desta página" +} diff --git a/site/i18n/ru.json b/site/i18n/ru.json new file mode 100644 index 0000000..92bbccf --- /dev/null +++ b/site/i18n/ru.json @@ -0,0 +1,107 @@ +{ + "lang_name": "Русский", + "lang_label": "Выбрать язык", + "theme_label": "Светлая или тёмная тема", + "skip": "Перейти к содержимому", + "copy": "Копировать", + "copied": "Скопировано", + "meta_title": "TigerTag JavaScript SDK: чтение и запись NFC-меток филамента", + "meta_description": "SDK для Node.js: чтение, проверка, создание и изменение NFC-чипов TigerTag на катушках филамента и смолы для 3D-печати. Офлайн, Apache-2.0.", + "og_title": "TigerTag JavaScript SDK для Node.js", + "og_description": "Читайте, проверяйте и записывайте NFC-чипы TigerTag в Node.js: материал, цвет, параметры печати, количество и офлайн-проверка подписи.", + "og_alt": "TigerTag JavaScript SDK: npm install tigertag", + "nav_start": "Начало работы", + "nav_features": "Возможности", + "nav_tools": "CLI и Electron", + "nav_playground": "Playground", + "nav_eco": "Экосистема", + "nav_faq": "Вопросы", + "hero_kicker": "SDK для JavaScript / Node.js", + "hero_headline": "Читайте, проверяйте и записывайте NFC-чипы TigerTag на JavaScript.", + "hero_lead": "SDK открытого протокола TigerTag для филамента и смолы для 3D-печати: превращает байты чипа в материал, цвет, параметры печати и количество, проверяет его подпись ECDSA-P256 без сети и собирает новые метки байт за байтом.", + "hero_caption": "Быстрый старт из README: работает сразу после npm install, без настройки.", + "cta_start": "Начать", + "cta_npm": "Открыть в npm", + "cta_github": "Исходный код на GitHub", + "fact1_label": "Текущая версия, выпущена {date}", + "fact2_label": "Использует встроенные crypto и fetch", + "fact3_label": "Зависимостей времени выполнения для чтения и проверки", + "fact4_label": "Открытая лицензия с предоставлением патентных прав", + "start_title": "Начните за три шага", + "start_lead": "Установите пакет, прочитайте чип своей NFC-библиотекой, запишите новый. Встроенные справочные таблицы позволяют первому запуску работать без сети.", + "step1_title": "Установите пакет", + "step1_text": "Из реестра npm. pnpm, yarn и bun устанавливают тот же пакет.", + "step2_title": "Прочитайте чип", + "step2_text": "Передайте 7-байтовый UID и 144 байта, прочитанные NFC-библиотекой. Здесь — с nfc-pcsc и ACR122U.", + "step3_title": "Измените и запишите", + "step3_text": "Каждое изменение возвращает новый TigerTag; toBytes() даёт байты для записи. erase() даёт пустой чип.", + "start_more": "Полный API в README", + "feat_title": "Что умеет SDK", + "feat_lead": "Один пакет для всего, что связано с чипом TigerTag: чтения, проверки, записи и обновления данных.", + "f1_title": "Чтение любого дампа", + "f1_text": "fromPages(), fromDump() и fromFile() принимают UID с 80 или 144 байтами либо полный дамп на 180 байт. pretty(), describe(), toDict() и toRawDict() возвращают текст, абзац простым языком или объекты, готовые для JSON.", + "f2_title": "Проверка подлинности без сети", + "f2_text": "verify() проверяет подпись ECDSA-P256, связывающую UID чипа с данными продукта, открытым ключом из пакета. Результат: valid, invalid, unsigned, no_key или no_uid.", + "f3_title": "Создание и изменение меток", + "f3_text": "create(), asInit(), patch() и erase() формируют байты чипа. TigerTag неизменяем: patch() возвращает новый объект и не позволяет менять idTigertag, idProduct, uid или подпись.", + "f4_title": "Сборка из каталога", + "f4_text": "TigerTag.fromCatalog(productId) собирает полный TigerTag+ из официального каталога продуктов, который загружается при первом использовании и кешируется в папке пользователя.", + "f5_title": "Синхронизация с производителем", + "f5_text": "Для чипов TigerTag+ методы rawApi(), diffApi() и patchFromApi() получают актуальные данные бренда, показывают отличия от чипа и применяют их.", + "f6_title": "Справочные данные, прежде всего офлайн", + "f6_text": "Таблицы материалов, брендов, видов поверхности, типов, диаметров и единиц входят в пакет. Фоновая проверка обновляет их не чаще раза в сутки; offline: true или TIGERTAG_OFFLINE=1 означает ноль сетевых запросов.", + "types_title": "Три вида меток", + "types_lead": "SDK читает и записывает три типа меток протокола TigerTag v2.2 на чипах NTAG213, NTAG215 и NTAG216.", + "t1_name": "TigerTag (Maker)", + "t1_text": "Все поля на чипе, читаются полностью без сети.", + "t2_name": "TigerTag Init", + "t2_text": "Пустой шаблон, готовый к программированию. Создаётся через TigerTag.asInit().", + "t3_name": "TigerTag+", + "t3_text": "Все поля на чипе плюс идентификатор продукта, связанный с облачным API для обновлений от производителя. Подписанный чип проверяется через verify().", + "tools_title": "В командной строке и внутри Electron", + "tools_lead": "Пакет устанавливает команду tigertag и работает в главном процессе приложения Electron.", + "cli_title": "Команда tigertag", + "cli_text": "Разбирает дамп в текст, JSON или сырые поля и по запросу обновляет справочные таблицы.", + "electron_title": "Внутри приложения Electron", + "electron_text": "Разбор чипов в главном процессе, без дополнительного подпроцесса.", + "pg_title": "Попробуйте без считывателя: playground", + "pg_text": "Локальная веб-страница, которую обслуживает сам SDK: она собирает и декодирует чипы TigerTag и показывает каждый байт. NFC-оборудование не нужно.", + "pg_point1": "Вход и выход SDK рядом: вызов create(), затем pretty(), describe(), verify() и toDict().", + "pg_point2": "С одним или несколькими считывателями PC/SC, например ACR122U, чипы читаются сразу при поднесении, а Burn записывает текущее содержимое.", + "pg_point3": "Playground никогда не записывает подпись: выпустить её может только сертифицированный производитель.", + "eco_title": "Часть экосистемы TigerTag", + "eco_lead": "SDK реализует открытую спецификацию TigerTag наряду с другими официальными инструментами.", + "eco_spec_title": "Спецификация протокола TigerTag", + "eco_spec_text": "Открытая спецификация, которую реализует SDK: структура памяти, типы меток, схема подписи и справочная база данных.", + "eco_py_title": "TigerTag SDK для Python", + "eco_py_text": "Версия для Python: чтение, проверка, запись и сравнение. Страница playground — один и тот же файл в обоих SDK.", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "Открытое настольное приложение для учёта запасов под Windows, macOS и Linux, работает с TigerTag Pod и считывателями ACR122U.", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "Официальное мобильное приложение: чтение и запись чипов встроенным NFC телефона.", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "Репозиторий", + "faq_title": "Вопросы", + "faq1_q": "Как установить TigerTag JavaScript SDK?", + "faq1_a": "Выполните npm install tigertag, либо pnpm add tigertag, yarn add tigertag или bun add tigertag. Нужен Node.js 18 или новее; для чтения и проверки другие пакеты не нужны.", + "faq2_q": "Нужно ли подключение к интернету?", + "faq2_a": "Нет. Справочные таблицы и открытый ключ подписи входят в пакет, поэтому чтение и проверка работают без сети. Сеть используется только для ежедневной проверки таблиц, каталога продуктов и облачных методов TigerTag+; offline: true или TIGERTAG_OFFLINE=1 её отключает.", + "faq3_q": "С какими NFC-чипами и считывателями он работает?", + "faq3_a": "TigerTag использует чипы NTAG213, NTAG215 и NTAG216 (ISO 14443-3). SDK принимает UID и байты, прочитанные вашей NFC-библиотекой, поэтому не привязан к одному считывателю; в README показан nfc-pcsc с ACR122U.", + "faq4_q": "Как он проверяет подлинность чипа?", + "faq4_a": "verify() проверяет подпись ECDSA-P256 чипа по его UID и идентификаторам продукта открытым ключом из пакета. Возвращает valid, invalid, unsigned (метка Maker), no_key или no_uid без обращения к серверу.", + "faq5_q": "Может ли он записывать чипы?", + "faq5_a": "Он формирует байты: create(), patch(), asInit(), erase() и fromCatalog() дают TigerTag, чей toBytes() — содержимое, которое записывает ваша NFC-библиотека. Кнопка Burn в playground записывает его на чипы подключённых считывателей.", + "faq6_q": "Есть ли версия для Python?", + "faq6_a": "Да: TigerTag-SDK-Python — версия того же API для Python, и оба SDK обслуживают одну и ту же страницу playground.", + "faq7_q": "Под какой лицензией SDK?", + "faq7_a": "Apache-2.0, с явным предоставлением патентных прав. Для реализации самого протокола TigerTag лицензия не требуется.", + "join_title": "Одна команда для старта", + "join_text": "npm install tigertag, затем TigerTag.fromPages(uid, payload).", + "join_btn": "Открыть в npm", + "foot_licences": "SDK Apache-2.0 · Спецификация протокола CC-BY-4.0", + "foot_trademark": "TigerTag — товарный знак TigerTag Corp. Политика товарного знака", + "foot_protocol": "Протокол TigerTag", + "foot_source": "Исходный код страницы" +} diff --git a/site/i18n/zh.json b/site/i18n/zh.json new file mode 100644 index 0000000..ff4aa96 --- /dev/null +++ b/site/i18n/zh.json @@ -0,0 +1,107 @@ +{ + "lang_name": "中文", + "lang_label": "选择语言", + "theme_label": "浅色或深色", + "skip": "跳到正文", + "copy": "复制", + "copied": "已复制", + "meta_title": "TigerTag JavaScript SDK:读写 3D 打印耗材 NFC 标签", + "meta_description": "用于读取、验证、创建和修改 3D 打印耗材与树脂上 TigerTag NFC 芯片的 Node.js SDK。离线可用,Apache-2.0 许可:npm install tigertag。", + "og_title": "TigerTag JavaScript SDK(Node.js)", + "og_description": "在 Node.js 中读取、验证和写入 TigerTag NFC 芯片:材料、颜色、打印参数、余量,以及离线签名验证。", + "og_alt": "TigerTag JavaScript SDK:npm install tigertag", + "nav_start": "快速开始", + "nav_features": "功能", + "nav_tools": "CLI 与 Electron", + "nav_playground": "Playground", + "nav_eco": "生态系统", + "nav_faq": "常见问题", + "hero_kicker": "JavaScript / Node.js SDK", + "hero_headline": "用 JavaScript 读取、验证和写入 TigerTag NFC 芯片。", + "hero_lead": "开放的 TigerTag 协议的 SDK,面向 3D 打印耗材与树脂:它把芯片上的字节解析为材料、颜色、打印参数和余量,离线验证其 ECDSA-P256 签名,并逐字节生成新标签。", + "hero_caption": "README 中的快速开始示例:npm install 之后即可运行,无需任何配置。", + "cta_start": "快速开始", + "cta_npm": "在 npm 上查看", + "cta_github": "GitHub 源代码", + "fact1_label": "当前版本,发布于 {date}", + "fact2_label": "使用内置的 crypto 和 fetch", + "fact3_label": "解析和验证所需的运行时依赖", + "fact4_label": "开源许可证,含专利授权", + "start_title": "三步上手", + "start_lead": "安装软件包,用你的 NFC 库读取芯片,再写入新芯片。内置的参考数据表让首次运行无需联网。", + "step1_title": "安装软件包", + "step1_text": "从 npm 仓库安装。pnpm、yarn 和 bun 安装的是同一个软件包。", + "step2_title": "读取芯片", + "step2_text": "传入 7 字节 UID 和 NFC 库读取的 144 字节。此处示例使用 nfc-pcsc 和 ACR122U。", + "step3_title": "修改并写入", + "step3_text": "每次修改都返回一个新的 TigerTag;toBytes() 给出要写回的字节。erase() 给出一个空白芯片。", + "start_more": "README 中的完整 API", + "feat_title": "SDK 能做什么", + "feat_lead": "一个软件包覆盖 TigerTag 芯片的各个方面:读取、校验、写入,并保持数据最新。", + "f1_title": "读取任何转储", + "f1_text": "fromPages()、fromDump() 和 fromFile() 接受 UID 加 80 或 144 字节,或完整的 180 字节转储。pretty()、describe()、toDict() 和 toRawDict() 返回文本、通俗的说明段落或可直接转为 JSON 的对象。", + "f2_title": "离线验证真伪", + "f2_text": "verify() 使用软件包内置的公钥,检查将芯片 UID 与产品数据绑定在一起的 ECDSA-P256 签名。结果为 valid、invalid、unsigned、no_key 或 no_uid。", + "f3_title": "创建和修改标签", + "f3_text": "create()、asInit()、patch() 和 erase() 生成芯片字节。TigerTag 对象不可变:patch() 返回新对象,并拒绝修改 idTigertag、idProduct、uid 或签名。", + "f4_title": "从产品目录生成", + "f4_text": "TigerTag.fromCatalog(productId) 根据官方产品目录生成完整的 TigerTag+;目录在首次使用时下载,并缓存在用户专属文件夹中。", + "f5_title": "与制造商同步", + "f5_text": "对于 TigerTag+ 芯片,rawApi()、diffApi() 和 patchFromApi() 获取品牌当前的产品数据,列出与芯片的差异并应用。", + "f6_title": "参考数据,离线优先", + "f6_text": "材料、品牌、外观、类型、直径和单位数据表随软件包提供。后台检查每天最多更新一次;offline: true 或 TIGERTAG_OFFLINE=1 表示完全不发起网络请求。", + "types_title": "三种标签", + "types_lead": "SDK 可读写 TigerTag 协议 v2.2 的三种标签类型,适用于 NTAG213、NTAG215 和 NTAG216 芯片。", + "t1_name": "TigerTag (Maker)", + "t1_text": "所有字段都在芯片上,可完全离线读取。", + "t2_name": "TigerTag Init", + "t2_text": "空白模板,可直接编程。TigerTag.asInit() 用于创建。", + "t3_name": "TigerTag+", + "t3_text": "所有字段都在芯片上,另有一个产品 ID,可通过云端 API 获取制造商的更新。已签名的芯片可用 verify() 验证。", + "tools_title": "命令行与 Electron", + "tools_lead": "软件包会安装 tigertag 命令,也可在 Electron 应用的主进程中运行。", + "cli_title": "tigertag 命令", + "cli_text": "以文本、JSON 或原始字段解析转储,并按需更新参考数据表。", + "electron_title": "在 Electron 应用中", + "electron_text": "在主进程中解析芯片,无需附带子进程。", + "pg_title": "无需读卡器即可试用:Playground", + "pg_text": "由 SDK 自身提供的本地网页,可生成和解码 TigerTag 芯片并显示每个字节。无需 NFC 硬件。", + "pg_point1": "SDK 输入与输出并排显示:create() 调用,以及 pretty()、describe()、verify() 和 toDict()。", + "pg_point2": "连接一个或多个 PC/SC 读卡器(如 ACR122U)时,芯片放上即被读取,Burn 会写入当前内容。", + "pg_point3": "Playground 从不写入签名:只有经过认证的制造商才能签发签名。", + "eco_title": "TigerTag 生态系统的一部分", + "eco_lead": "SDK 实现了开放的 TigerTag 规范,与其他官方工具相互配合。", + "eco_spec_title": "TigerTag 协议规范", + "eco_spec_text": "SDK 所实现的开放规范:内存布局、标签类型、签名方案和参考数据库。", + "eco_py_title": "TigerTag Python SDK", + "eco_py_text": "Python 移植版:读取、验证、写入和比对。两个 SDK 中的 Playground 页面是同一个文件。", + "eco_studio_title": "TigerTag Studio Manager", + "eco_studio_text": "开源桌面库存管理软件,支持 Windows、macOS 和 Linux,可配合 TigerTag Pod 和 ACR122U 读卡器使用。", + "eco_connect_title": "TigerTag RFID Connect", + "eco_connect_text": "官方移动应用:使用手机内置 NFC 读写芯片。", + "eco_appstore": "App Store", + "eco_play": "Google Play", + "repo": "代码仓库", + "faq_title": "常见问题", + "faq1_q": "如何安装 TigerTag JavaScript SDK?", + "faq1_a": "运行 npm install tigertag,或 pnpm add tigertag、yarn add tigertag、bun add tigertag。需要 Node.js 18 或更高版本,解析和验证不需要其他软件包。", + "faq2_q": "需要联网吗?", + "faq2_a": "不需要。参考数据表和签名公钥都随软件包提供,因此读取和验证可离线完成。网络仅用于每日数据表检查、产品目录和 TigerTag+ 云端方法;offline: true 或 TIGERTAG_OFFLINE=1 可将其关闭。", + "faq3_q": "支持哪些 NFC 芯片和读卡器?", + "faq3_a": "TigerTag 使用 NTAG213、NTAG215 和 NTAG216 芯片(ISO 14443-3)。SDK 接收 NFC 库读取的 UID 和字节,因此不绑定任何读卡器;README 中示例使用 nfc-pcsc 和 ACR122U。", + "faq4_q": "如何验证芯片是否为正品?", + "faq4_a": "verify() 使用软件包内置的公钥,检查芯片基于其 UID 和产品标识的 ECDSA-P256 签名。返回 valid、invalid、unsigned(Maker 标签)、no_key 或 no_uid,无需调用服务器。", + "faq5_q": "能写入芯片吗?", + "faq5_a": "它负责生成字节:create()、patch()、asInit()、erase() 和 fromCatalog() 返回一个 TigerTag,其 toBytes() 就是 NFC 库要写入的内容。Playground 的 Burn 按钮会把它写入已连接读卡器上的芯片。", + "faq6_q": "有 Python 版本吗?", + "faq6_a": "有:TigerTag-SDK-Python 是同一 API 的 Python 移植版,两个 SDK 提供同一个 Playground 页面。", + "faq7_q": "SDK 采用什么许可证?", + "faq7_a": "Apache-2.0,包含明确的专利授权。实现 TigerTag 协议本身无需任何许可。", + "join_title": "一条命令即可开始", + "join_text": "npm install tigertag,然后调用 TigerTag.fromPages(uid, payload)。", + "join_btn": "在 npm 上查看", + "foot_licences": "SDK Apache-2.0 · 协议规范 CC-BY-4.0", + "foot_trademark": "TigerTag 是 TigerTag Corp 的商标。商标政策", + "foot_protocol": "TigerTag 协议", + "foot_source": "本页源代码" +} diff --git a/site/seo_check.py b/site/seo_check.py new file mode 100644 index 0000000..72656f8 --- /dev/null +++ b/site/seo_check.py @@ -0,0 +1,225 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# TigerTag SDK +# Copyright (c) 2025-2026 TigerTag Corp. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Check the built site for what search engines and answer engines need. + +The rule behind each check is in .agents/rules/seo-geo.md. Run after +`site/build.py`, on the folder it wrote: + + python site/build.py --out _site && python site/seo_check.py _site + +Errors fail the run (exit 1): they break indexing or mislead a reader. +Warnings are printed and do not: they cost snippet quality. +""" + +import json +import re +import sys +from html.parser import HTMLParser +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from build import LANGS, HTML_LANG, SITE_URL, page_path, package # noqa: E402 + +PACKAGE = package() +EMOJI = re.compile("[\U0001F300-\U0001FAFF\u2600-\u27BF\u2B50\u2B55\uFE0F]") + +TITLE_MAX, TITLE_HARD = 60, 70 # characters shown by search results / clearly broken +DESC_MIN, DESC_MAX, DESC_HARD = 50, 160, 200 +REQUIRED_LD = {"Organization", "WebSite", "SoftwareSourceCode", "FAQPage"} + + +class Page(HTMLParser): + """Collects what the checks read, without a third-party parser.""" + + def __init__(self): + super().__init__(convert_charrefs=True) + self.title, self.meta, self.links, self.imgs = "", {}, [], [] + self.ids, self.hrefs, self.h1, self.faq_items, self.ld = set(), [], 0, 0, [] + self._in_title = self._in_ld = False + self._ld_buf = "" + + def handle_starttag(self, tag, attrs): + a = dict(attrs) + if "id" in a: + self.ids.add(a["id"]) + if tag == "title": + self._in_title = True + elif tag == "meta" and ("name" in a or "property" in a): + self.meta.setdefault(a.get("name") or a.get("property"), []).append(a.get("content", "")) + elif tag == "link": + self.links.append(a) + elif tag == "img": + self.imgs.append(a) + elif tag == "a" and "href" in a: + self.hrefs.append(a["href"]) + elif tag == "h1": + self.h1 += 1 + elif tag == "details" and self._faq_depth(): + self.faq_items += 1 + elif tag == "script" and a.get("type") == "application/ld+json": + self._in_ld, self._ld_buf = True, "" + if tag == "div" and "faq" in (a.get("class") or "").split(): + self._faq = True + + def _faq_depth(self): + return getattr(self, "_faq", False) + + def handle_endtag(self, tag): + if tag == "title": + self._in_title = False + elif tag == "script" and self._in_ld: + self._in_ld = False + self.ld.append(self._ld_buf) + elif tag == "section": + self._faq = False + + def handle_data(self, data): + if self._in_title: + self.title += data + if self._in_ld: + self._ld_buf += data + + +def check(out): + errors, warnings = [], [] + expected_alternates = {HTML_LANG.get(l, l): SITE_URL + page_path(l) for l in LANGS} + expected_alternates["x-default"] = SITE_URL + titles, descs = {}, {} + + for lang in LANGS: + rel = page_path(lang) + "index.html" + path = out / rel + if not path.exists(): + errors.append(f"{rel}: page missing") + continue + p = Page() + p.feed(path.read_text(encoding="utf-8")) + where = rel + + # Title and description: present, sized for a result snippet, unique per language. + title = p.title.strip() + desc = (p.meta.get("description") or [""])[0] + for label, value, lo, hi, hard in (("title", title, 10, TITLE_MAX, TITLE_HARD), ("description", desc, DESC_MIN, DESC_MAX, DESC_HARD)): + if not value: + errors.append(f"{where}: no {label}") + elif len(value) > hard: + errors.append(f"{where}: {label} is {len(value)} characters (over {hard}): {value!r}") + elif len(value) > hi: + warnings.append(f"{where}: {label} is {len(value)} characters (results show ~{hi})") + elif len(value) < lo and HTML_LANG.get(lang, lang) != "zh-Hans": + warnings.append(f"{where}: {label} is only {len(value)} characters") + titles.setdefault(title, []).append(lang) + descs.setdefault(desc, []).append(lang) + + # One h1: the page's single subject. + if p.h1 != 1: + errors.append(f"{where}: {p.h1}

    (exactly one expected)") + + # Canonical and hreflang: every language points to every language, and x-default. + canon = [l.get("href") for l in p.links if l.get("rel") == "canonical"] + if canon != [SITE_URL + page_path(lang)]: + errors.append(f"{where}: canonical is {canon}, expected {SITE_URL + page_path(lang)}") + alts = {l.get("hreflang"): l.get("href") for l in p.links if l.get("rel") == "alternate" and l.get("hreflang")} + if alts != expected_alternates: + missing = sorted(set(expected_alternates) - set(alts)) + wrong = sorted(k for k in alts if alts[k] != expected_alternates.get(k)) + errors.append(f"{where}: hreflang set incomplete or wrong (missing {missing}, wrong {wrong})") + + # Sharing cards. + for prop in ("og:title", "og:description", "og:image", "og:image:alt", "og:url", "twitter:card"): + if not (p.meta.get(prop) or [""])[0]: + errors.append(f"{where}: no {prop}") + + # Images: an alt attribute on every one (empty only for a decorative duplicate). + for img in p.imgs: + if "alt" not in img: + errors.append(f"{where}: has no alt attribute") + # The sharing card picture exists where og:image points. + og = (p.meta.get("og:image") or [""])[0] + if og.startswith(SITE_URL) and not (out / og[len(SITE_URL):]).exists(): + errors.append(f"{where}: og:image {og} is not in the build") + + # Structured data: valid JSON, the expected types, the FAQ the reader sees. + types = [] + for raw in p.ld: + try: + data = json.loads(raw) + except ValueError as e: + errors.append(f"{where}: JSON-LD does not parse ({e})") + continue + types.append(data.get("@type")) + if data.get("@type") == "SoftwareSourceCode" and data.get("version") != PACKAGE["version"]: + errors.append(f"{where}: SoftwareSourceCode version {data.get('version')!r} is not package.json's {PACKAGE['version']!r}") + if data.get("@type") == "FAQPage" and len(data.get("mainEntity", [])) != p.faq_items: + errors.append(f"{where}: FAQPage lists {len(data.get('mainEntity', []))} questions, the page shows {p.faq_items}") + missing_ld = REQUIRED_LD - set(types) + if missing_ld: + errors.append(f"{where}: JSON-LD missing {sorted(missing_ld)}") + + # No emoji anywhere on the site: inline SVG icons, or words. + text = path.read_text(encoding="utf-8") + emoji = EMOJI.findall(text) + if emoji: + errors.append(f"{where}: emoji on the page: {' '.join(sorted(set(emoji)))}") + + # The install command the README documents is on the page. + if f"npm install {PACKAGE['name']}" not in text: + errors.append(f"{where}: no `npm install {PACKAGE['name']}` on the page") + + # In-page links land somewhere. + for href in p.hrefs: + if href.startswith("#") and len(href) > 1 and href[1:] not in p.ids: + errors.append(f"{where}: link {href} has no target") + + # The machine-readable description is advertised. + if not any(l.get("rel") == "alternate" and (l.get("href") or "").endswith("llms.txt") for l in p.links): + warnings.append(f"{where}: no to llms.txt") + + for value, langs in list(titles.items()) + list(descs.items()): + if value and len(langs) > 1: + errors.append(f"same title or description in {', '.join(langs)}: {value!r} (an untranslated page?)") + + sitemap = (out / "sitemap.xml").read_text(encoding="utf-8") if (out / "sitemap.xml").exists() else "" + for lang in LANGS: + if f"{SITE_URL + page_path(lang)}" not in sitemap: + errors.append(f"sitemap.xml: {SITE_URL + page_path(lang)} missing") + if "" not in sitemap: + warnings.append("sitemap.xml: no ") + robots = (out / "robots.txt").read_text(encoding="utf-8") if (out / "robots.txt").exists() else "" + if f"Sitemap: {SITE_URL}sitemap.xml" not in robots: + errors.append("robots.txt: no Sitemap line") + if re.search(r"(?m)^Disallow: /\s*$", robots): + errors.append("robots.txt: disallows the whole site") + if not (out / "llms.txt").exists(): + errors.append("llms.txt missing at the site root") + return errors, warnings + + +def main(): + out = Path(sys.argv[1] if len(sys.argv) > 1 else "_site") + errors, warnings = check(out) + for w in warnings: + print(f"warning: {w}") + for e in errors: + print(f"error: {e}") + print(f"seo_check: {len(LANGS)} pages, {len(errors)} error(s), {len(warnings)} warning(s)") + sys.exit(1 if errors else 0) + + +if __name__ == "__main__": + main() diff --git a/site/site.css b/site/site.css new file mode 100644 index 0000000..02319b7 --- /dev/null +++ b/site/site.css @@ -0,0 +1,199 @@ +/* The TigerTag JavaScript SDK site — the look of the TigerTag protocol site (dark by + default, light when the system asks for it or when chosen with the button). */ +:root { + --bg: #14161a; --card: #1e2128; --line: #2b2f38; --text: #f2f2f2; + --muted: #9aa0aa; --accent: #f58220; --link: #f58220; --code: #101216; + --c-comment: #7d8590; --c-string: #a5d6a7; + --shadow: rgba(0, 0, 0, .45); + color-scheme: dark; +} +@media (prefers-color-scheme: light) { + :root:not([data-theme="dark"]) { + --bg: #f4f5f7; --card: #ffffff; --line: #dde0e6; --text: #16181d; + --muted: #5d6470; --accent: #e06d0c; --link: #b04a08; --code: #f0f1f4; + --c-comment: #6a737d; --c-string: #1a7f37; + --shadow: rgba(0, 0, 0, .12); + color-scheme: light; + } +} +:root[data-theme="light"] { + --bg: #f4f5f7; --card: #ffffff; --line: #dde0e6; --text: #16181d; + --muted: #5d6470; --accent: #e06d0c; --link: #b04a08; --code: #f0f1f4; + --c-comment: #6a737d; --c-string: #1a7f37; + --shadow: rgba(0, 0, 0, .12); + color-scheme: light; +} + +* { box-sizing: border-box; } +html { scroll-behavior: smooth; overflow-x: clip; } +body { + margin: 0; min-height: 100vh; background: var(--bg); color: var(--text); + font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + overflow-x: clip; +} +a { color: var(--link); } +img { max-width: 100%; height: auto; } +.wrap { width: min(1200px, 100% - 48px); margin-left: auto; margin-right: auto; } +.skip { position: absolute; left: -999px; top: 8px; background: var(--card); padding: 8px 12px; border-radius: 8px; } +.skip:focus { left: 8px; z-index: 10; } +.muted, p { color: var(--muted); } +p { margin: 0 0 14px; } +b { color: var(--text); } +h1, h2, h3 { line-height: 1.2; color: var(--text); } +h2 { font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.2rem); margin: 0 0 8px; letter-spacing: -.01em; } +h3 { font-size: 1.08rem; margin: 0 0 8px; } +.sub { font-size: 1.05rem; max-width: 760px; margin-bottom: 22px; } +.gap { margin-top: 48px; } +.center { text-align: center; } +section { margin-top: 64px; scroll-margin-top: 80px; } + +/* Header: the logo, the sections, GitHub, the theme and the language. */ +header.bar { position: sticky; top: 0; z-index: 5; background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); } +header.bar .wrap { display: flex; align-items: center; gap: 18px; min-height: 64px; } +.brandmark { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--text); font-weight: 800; font-size: 1.2rem; letter-spacing: -.01em; white-space: nowrap; } +.brandmark b { color: var(--accent); } +.brandmark small { font-size: .78rem; font-weight: 700; color: var(--muted); border: 1px solid var(--line); border-radius: 6px; padding: 1px 6px; margin-left: 4px; vertical-align: 2px; } +.brandmark img { height: 36px; width: auto; } +.logo-light { display: none; } +:root[data-shown="light"] .logo-light { display: block; } +:root[data-shown="light"] .logo-dark { display: none; } +.nav { display: flex; gap: 18px; flex: 1; justify-content: center; flex-wrap: wrap; } +@media (max-width: 1320px) { .nav { gap: 12px; } .nav a { font-size: .88rem; } } +.nav a { color: var(--muted); text-decoration: none; font-size: .95rem; } +.nav a:hover { color: var(--text); } +.tools { display: flex; gap: 8px; align-items: center; } +.themebtn, .langbtn, .iconbtn { + display: flex; align-items: center; gap: 6px; height: 40px; cursor: pointer; color: var(--muted); + background: var(--bg); border: 1px solid var(--line); border-radius: 10px; font: inherit; font-size: .9rem; +} +.themebtn, .iconbtn { width: 40px; justify-content: center; } +.langbtn { padding: 0 12px; list-style: none; } +.langbtn::-webkit-details-marker { display: none; } +.themebtn:hover, .langbtn:hover, .iconbtn:hover { border-color: var(--accent); color: var(--text); } +.themebtn svg, .langbtn svg, .iconbtn svg { width: 17px; height: 17px; flex: none; } +.themebtn .sun { display: none; } +:root[data-shown="light"] .themebtn .sun { display: block; } +:root[data-shown="light"] .themebtn .moon { display: none; } +.langbox { position: relative; } +.langlist { + position: absolute; right: 0; top: 46px; margin: 0; padding: 6px; list-style: none; min-width: 180px; + background: var(--card); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 12px 30px var(--shadow); +} +.langlist a { display: block; padding: 7px 10px; border-radius: 8px; color: var(--text); text-decoration: none; } +.langlist a:hover, .langlist a[aria-current="true"] { background: var(--bg); color: var(--accent); } + +/* Hero: the words and the install command, the quick start beside them. */ +.hero { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr); gap: 40px; align-items: center; margin-top: 40px; } +h1 { margin: 0; } +h1 .kicker, h1 .name { display: block; } +.kicker { color: var(--accent); font-weight: 650; text-transform: uppercase; letter-spacing: .08em; font-size: .85rem; margin-bottom: 10px; } +.hero .name { margin: 0; font-size: clamp(2.8rem, 1.6rem + 4.4vw, 4.8rem); font-weight: 800; letter-spacing: -.02em; line-height: 1; } +.hero .name span { color: var(--accent); } +.headline { text-wrap: balance; margin: 16px 0 12px; color: var(--text); font-weight: 650; line-height: 1.25; font-size: clamp(1.2rem, 1rem + 1vw, 1.65rem); } +.lead { font-size: 1.05rem; } +.install { display: flex; align-items: stretch; gap: 8px; margin-top: 20px; max-width: 460px; } +.install pre { flex: 1; margin: 0; font-size: 1.05rem; padding: 12px 16px; } +.install pre code::before { content: "$ "; color: var(--accent); } +.copybtn { flex: none; cursor: pointer; font: inherit; font-weight: 600; font-size: .9rem; padding: 0 16px; border-radius: 10px; + color: var(--text); background: var(--card); border: 1px solid var(--line); min-width: 84px; } +.copybtn:hover { border-color: var(--accent); } +.ctas { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; } +.btn { + display: inline-flex; align-items: center; padding: 12px 20px; border-radius: 999px; text-decoration: none; + border: 1px solid var(--line); color: var(--text); background: var(--card); font-weight: 600; +} +.btn:hover { border-color: var(--accent); } +.btn.primary { background: linear-gradient(135deg, #ff8a2b 0%, #ff7a18 55%, #e6352b 100%); border-color: transparent; color: #fff; } +.btn.small { padding: 8px 14px; font-size: .9rem; } +.show { margin: 0; } +.show figcaption { color: var(--muted); font-size: .92rem; text-align: center; margin-top: 12px; } +.window { background: var(--code); border: 1px solid var(--line); border-radius: 16px; box-shadow: 0 24px 60px -20px var(--shadow); overflow: hidden; } +.window-bar { display: flex; align-items: center; gap: 7px; padding: 11px 14px; border-bottom: 1px solid var(--line); background: var(--card); } +.window-bar i { width: 11px; height: 11px; border-radius: 50%; background: var(--line); } +.window-bar i:first-child { background: #e6352b; } .window-bar i:nth-child(2) { background: #f58220; } .window-bar i:nth-child(3) { background: #3fb950; } +.window-bar span { margin-left: 8px; font: .82rem ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted); } +.window pre { margin: 0; border: 0; border-radius: 0; padding: 18px 20px; background: transparent; } +.window code { font-size: .8rem; line-height: 1.75; } + +/* Four facts. */ +.facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; margin-top: 44px; } +.fact { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 18px 20px; } +.fact b { display: block; font-size: 1.5rem; color: var(--accent); line-height: 1.2; } +.fact span { color: var(--muted); font-size: .93rem; } + +/* Cards and grids. */ +.card { background: var(--card); border: 1px solid var(--line); border-radius: 18px; padding: 24px; min-width: 0; } +.card p:last-child { margin-bottom: 0; } +.card.accent { border-color: var(--accent); } +.grid3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; } +.grid2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; } +.grid2.tools-grid { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); } +.intro, .show { min-width: 0; } +.ico { width: 28px; height: 28px; fill: none; stroke: var(--accent); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; margin-bottom: 10px; } +.pill { display: inline-block; font: 600 .78rem ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--accent); border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px; margin-bottom: 12px; } +.more { display: inline-block; font-weight: 600; text-decoration: none; margin-top: 4px; } +.more:hover { text-decoration: underline; } +.links { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; } +pre { margin: 0 0 14px; background: var(--code); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; overflow-x: auto; } +pre:last-child { margin-bottom: 0; } +code { font: .88rem/1.6 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); } +code .c { color: var(--c-comment); } +code .s { color: var(--c-string); } +pre.alt code { color: var(--muted); } + +/* Get started. */ +.steps { list-style: none; padding: 0; margin: 0 0 16px; display: grid; gap: 16px; } +.steps li { display: grid; grid-template-columns: minmax(0, .75fr) minmax(0, 1.25fr); grid-template-rows: auto auto 1fr; column-gap: 28px; align-items: start; } +.steps li > .num, .steps li > h3, .steps li > p { grid-column: 1; } +.steps li > .code { grid-column: 2; grid-row: 1 / span 3; min-width: 0; } +.num { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--accent); color: #1a0f05; font-weight: 800; margin-bottom: 12px; } +.steps code { font-size: .84rem; } + +/* Playground. */ +.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px; align-items: center; } +.ticks { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; } +.ticks li { position: relative; padding-left: 26px; color: var(--muted); } +.ticks li::before { content: ""; position: absolute; left: 4px; top: 6px; width: 10px; height: 6px; border-left: 2px solid var(--accent); border-bottom: 2px solid var(--accent); transform: rotate(-45deg); } + +/* FAQ. */ +.faq { display: grid; gap: 10px; } +.faq details { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 4px 20px; } +.faq summary { cursor: pointer; font-weight: 650; padding: 12px 0; color: var(--text); } +.faq details p { padding-bottom: 6px; } + +/* The call at the end. */ +.join { + display: flex; align-items: center; justify-content: space-between; gap: 24px; + padding: 30px 32px; border-radius: 18px; color: #fff; + background: linear-gradient(120deg, #c2410c 0%, #b03a12 55%, #9a2f12 100%); +} +.join h2, .join p { color: #fff; margin: 0 0 6px; } +.joinbtn { flex: none; padding: 14px 26px; border-radius: 999px; text-decoration: none; background: #fff; color: #9a2f12; font-weight: 700; white-space: nowrap; } +footer.foot { margin-top: 48px; padding: 28px 0 48px; border-top: 1px solid var(--line); text-align: center; font-size: .9rem; } +footer.foot .links { justify-content: center; } + +/* Tablet. */ +@media (max-width: 999px) { + .nav { display: none; } + header.bar .wrap { justify-content: space-between; } + .hero { grid-template-columns: minmax(0, 1fr); gap: 24px; } + .intro { text-align: center; } + .install { margin-left: auto; margin-right: auto; } + .ctas { justify-content: center; } + .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .grid3 { grid-template-columns: minmax(0, 1fr); } + .steps li { grid-template-columns: minmax(0, 1fr); } + .steps li > .code { grid-column: 1; grid-row: auto; } + .split { grid-template-columns: minmax(0, 1fr); } +} +/* Phone. */ +@media (max-width: 639px) { + .wrap { width: calc(100% - 32px); } + section { margin-top: 44px; } + .facts, .grid2, .grid2.tools-grid { grid-template-columns: minmax(0, 1fr); } + .card { padding: 20px; border-radius: 14px; } + .join { flex-direction: column; align-items: flex-start; padding: 24px 20px; } + .joinbtn { align-self: stretch; text-align: center; } + .langbtn span, .brandmark small { display: none; } + .window code { font-size: .78rem; } +} diff --git a/site/site.js b/site/site.js new file mode 100644 index 0000000..dca39ce --- /dev/null +++ b/site/site.js @@ -0,0 +1,59 @@ +/* The theme button and the remembered language. Everything else on the site is + static HTML: each language is its own page, built by site/build.py. */ +(function () { + const root = document.documentElement; + const media = window.matchMedia("(prefers-color-scheme: light)"); + + function shown() { + const chosen = root.dataset.theme; + root.dataset.shown = chosen || (media.matches ? "light" : "dark"); + } + shown(); + media.addEventListener("change", shown); + + const btn = document.getElementById("themebtn"); + if (btn) { + btn.addEventListener("click", function () { + const next = root.dataset.shown === "light" ? "dark" : "light"; + root.dataset.theme = next; + try { localStorage.setItem("tigertag-theme", next); } catch (e) {} + shown(); + }); + } + + /* The language menu closes on a click outside it, or on Escape. */ + const box = document.querySelector(".langbox"); + if (box) { + document.addEventListener("click", function (e) { if (box.open && !box.contains(e.target)) box.open = false; }); + document.addEventListener("keydown", function (e) { if (e.key === "Escape") box.open = false; }); + } + + /* A language picked in the menu is kept for the next visit to the English + root page, which then forwards to it. Search engines read every page as it + is: the redirect only follows a choice the reader made. */ + document.querySelectorAll(".langlist a[data-lang]").forEach(function (a) { + a.addEventListener("click", function () { + try { localStorage.setItem("tigertag-lang", a.dataset.lang); } catch (e) {} + }); + }); + if (root.dataset.lang === "en") { + let saved = null; + try { saved = localStorage.getItem("tigertag-lang"); } catch (e) {} + const target = saved && saved !== "en" && document.querySelector('.langlist a[data-lang="' + saved + '"]'); + if (target) window.location.replace(target.href + window.location.hash); + } +})(); + +/* Copy buttons: the install command to the clipboard, with a short confirmation. */ +(function () { + const body = document.body; + document.querySelectorAll(".copybtn[data-copy-text]").forEach(function (btn) { + btn.addEventListener("click", function () { + const done = function () { + btn.textContent = body.dataset.copied; + setTimeout(function () { btn.textContent = body.dataset.copy; }, 1600); + }; + if (navigator.clipboard) navigator.clipboard.writeText(btn.dataset.copyText).then(done, function () {}); + }); + }); +})(); diff --git a/site/template.html b/site/template.html new file mode 100644 index 0000000..add847c --- /dev/null +++ b/site/template.html @@ -0,0 +1,212 @@ + + + + + +{{meta_title}} + + +{{@alternates}} + + + +{{@oglocalealt}} + + + + + + + + + + + + + + + + + + + +{{@jsonld}} + + + +
    +
    + + + + TigerTag SDK JS + + +
    + + + + +
    + + + {{lang_name}} + +
      +{{@langmenu}} +
    +
    +
    +
    +
    + +
    +
    +
    +

    {{hero_kicker}}TigerTag SDK

    +

    {{hero_headline}}

    +

    {{hero_lead}}

    +
    +
    {{@code_install}}
    + +
    + +
    +
    +
    +
    quick-start.js
    +
    {{@code_quick}}
    +
    +
    {{hero_caption}}
    +
    +
    + +
    +
    {{@version}}{{fact1_label}}
    +
    Node.js 18+{{fact2_label}}
    +
    0{{fact3_label}}
    +
    Apache-2.0{{fact4_label}}
    +
    + +
    +

    {{start_title}}

    +

    {{start_lead}}

    +
      +
    1. 1

      {{step1_title}}

      {{step1_text}}

      +
      {{@code_install}}
      +
      {{@code_install_alt}}
    2. +
    3. 2

      {{step2_title}}

      {{step2_text}}

      +
      {{@code_read}}
    4. +
    5. 3

      {{step3_title}}

      {{step3_text}}

      +
      {{@code_write}}
    6. +
    +

    {{start_more}}

    +
    + +
    +

    {{feat_title}}

    +

    {{feat_lead}}

    +
    +

    {{f1_title}}

    {{f1_text}}

    +

    {{f2_title}}

    {{f2_text}}

    +

    {{f3_title}}

    {{f3_text}}

    +

    {{f4_title}}

    {{f4_text}}

    +

    {{f5_title}}

    {{f5_text}}

    +

    {{f6_title}}

    {{f6_text}}

    +
    +

    {{types_title}}

    +

    {{types_lead}}

    +
    +
    idProduct 0xFFFFFFFF

    {{t1_name}}

    {{t1_text}}

    +
    idProduct 0x00000000

    {{t2_name}}

    {{t2_text}}

    +
    ECDSA-P256

    {{t3_name}}

    {{t3_text}}

    +
    +
    + +
    +

    {{tools_title}}

    +

    {{tools_lead}}

    +
    +

    {{cli_title}}

    {{cli_text}}

    {{@code_cli}}
    +

    {{electron_title}}

    {{electron_text}}

    {{@code_electron}}
    +
    +
    + +
    +
    +
    +

    {{pg_title}}

    +

    {{pg_text}}

    +
      +
    • {{pg_point1}}
    • +
    • {{pg_point2}}
    • +
    • {{pg_point3}}
    • +
    +
    +
    {{@code_playground}}
    +
    +
    + +
    +

    {{eco_title}}

    +

    {{eco_lead}}

    +
    + + + + +
    +
    + +
    +

    {{faq_title}}

    +
    +
    {{faq1_q}}

    {{faq1_a}}

    +
    {{faq2_q}}

    {{faq2_a}}

    +
    {{faq3_q}}

    {{faq3_a}}

    +
    {{faq4_q}}

    {{faq4_a}}

    +
    {{faq5_q}}

    {{faq5_a}}

    +
    {{faq6_q}}

    {{faq6_a}}

    +
    {{faq7_q}}

    {{faq7_a}}

    +
    +
    + +
    +
    +

    {{join_title}}

    {{join_text}}

    + {{join_btn}} +
    +
    +
    + + + From b1731d8be9581de4370c86eead2d1bcf03ec25b1 Mon Sep 17 00:00:00 2001 From: BenGlut Date: Fri, 6 Nov 2026 08:54:35 +0100 Subject: [PATCH 2/2] TigerTag Corp is now TigerSystem The publisher and trademark holder is named TigerSystem in the copyright headers, the licences, the README and the showcase site (footer in every language and structured data). --- LICENSE | 2 +- bin/tigertag.js | 2 +- site/build.py | 4 ++-- site/i18n/de.json | 2 +- site/i18n/en.json | 2 +- site/i18n/es.json | 2 +- site/i18n/fr.json | 2 +- site/i18n/it.json | 2 +- site/i18n/nl.json | 2 +- site/i18n/pl.json | 2 +- site/i18n/pt-pt.json | 2 +- site/i18n/pt.json | 2 +- site/i18n/ru.json | 2 +- site/i18n/zh.json | 2 +- site/seo_check.py | 2 +- src/catalog.js | 2 +- src/datadir.js | 2 +- src/db.js | 2 +- src/index.js | 2 +- src/signature.js | 2 +- src/tag.js | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/LICENSE b/LICENSE index dc3c748..dd0cbab 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025-2026 TigerTag Corp. + Copyright 2025-2026 TigerSystem. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/bin/tigertag.js b/bin/tigertag.js index 9903577..f549cb2 100644 --- a/bin/tigertag.js +++ b/bin/tigertag.js @@ -4,7 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/site/build.py b/site/build.py index 304777c..2f2a3f5 100644 --- a/site/build.py +++ b/site/build.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # # TigerTag SDK -# Copyright (c) 2025-2026 TigerTag Corp. +# Copyright (c) 2025-2026 TigerSystem. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -238,7 +238,7 @@ def render(template, lang, words, dicts, site_url, ctx): faq = [{"@type": "Question", "name": words[f"faq{i}_q"], "acceptedAnswer": {"@type": "Answer", "text": words[f"faq{i}_a"]}} for i in range(1, FAQ_COUNT + 1)] jsonld = [ - {"@context": "https://schema.org", "@type": "Organization", "@id": ORG_ID, "name": "TigerTag Corp", + {"@context": "https://schema.org", "@type": "Organization", "@id": ORG_ID, "name": "TigerSystem", "url": "https://tigersystem.io", "logo": site_url + "assets/apple-touch-icon.png", "sameAs": ["https://github.com/TigerTag-Project", "https://tigertag-project.github.io/TigerTag-RFID-Guide/"]}, {"@context": "https://schema.org", "@type": "WebSite", "name": "TigerTag JavaScript SDK", "url": url, diff --git a/site/i18n/de.json b/site/i18n/de.json index 6352b6a..7e851c6 100644 --- a/site/i18n/de.json +++ b/site/i18n/de.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, dann TigerTag.fromPages(uid, payload).", "join_btn": "Auf npm ansehen", "foot_licences": "SDK Apache-2.0 · Protokollspezifikation CC-BY-4.0", - "foot_trademark": "TigerTag ist eine Marke der TigerTag Corp. Markenrichtlinie", + "foot_trademark": "TigerTag ist eine Marke von TigerSystem. Markenrichtlinie", "foot_protocol": "TigerTag-Protokoll", "foot_source": "Quelle dieser Seite" } diff --git a/site/i18n/en.json b/site/i18n/en.json index a3b66ee..167a46d 100644 --- a/site/i18n/en.json +++ b/site/i18n/en.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, then TigerTag.fromPages(uid, payload).", "join_btn": "View on npm", "foot_licences": "SDK Apache-2.0 · Protocol specification CC-BY-4.0", - "foot_trademark": "TigerTag is a trademark of TigerTag Corp. Trademark policy", + "foot_trademark": "TigerTag is a trademark of TigerSystem. Trademark policy", "foot_protocol": "TigerTag protocol", "foot_source": "Source of this page" } diff --git a/site/i18n/es.json b/site/i18n/es.json index 6b1d56f..ca5e122 100644 --- a/site/i18n/es.json +++ b/site/i18n/es.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag y después TigerTag.fromPages(uid, payload).", "join_btn": "Ver en npm", "foot_licences": "SDK Apache-2.0 · Especificación del protocolo CC-BY-4.0", - "foot_trademark": "TigerTag es una marca de TigerTag Corp. Política de marca", + "foot_trademark": "TigerTag es una marca de TigerSystem. Política de marca", "foot_protocol": "Protocolo TigerTag", "foot_source": "Código de esta página" } diff --git a/site/i18n/fr.json b/site/i18n/fr.json index c295f3c..5e66878 100644 --- a/site/i18n/fr.json +++ b/site/i18n/fr.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, puis TigerTag.fromPages(uid, payload).", "join_btn": "Voir sur npm", "foot_licences": "SDK Apache-2.0 · Spécification du protocole CC-BY-4.0", - "foot_trademark": "TigerTag est une marque de TigerTag Corp. Politique de marque", + "foot_trademark": "TigerTag est une marque de TigerSystem. Politique de marque", "foot_protocol": "Protocole TigerTag", "foot_source": "Source de cette page" } diff --git a/site/i18n/it.json b/site/i18n/it.json index 4e36562..0b73c46 100644 --- a/site/i18n/it.json +++ b/site/i18n/it.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, poi TigerTag.fromPages(uid, payload).", "join_btn": "Vedi su npm", "foot_licences": "SDK Apache-2.0 · Specifica del protocollo CC-BY-4.0", - "foot_trademark": "TigerTag è un marchio di TigerTag Corp. Politica sul marchio", + "foot_trademark": "TigerTag è un marchio di TigerSystem. Politica sul marchio", "foot_protocol": "Protocollo TigerTag", "foot_source": "Sorgente di questa pagina" } diff --git a/site/i18n/nl.json b/site/i18n/nl.json index a9df2f0..8dd44b4 100644 --- a/site/i18n/nl.json +++ b/site/i18n/nl.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, daarna TigerTag.fromPages(uid, payload).", "join_btn": "Bekijk op npm", "foot_licences": "SDK Apache-2.0 · Protocolspecificatie CC-BY-4.0", - "foot_trademark": "TigerTag is een handelsmerk van TigerTag Corp. Merkbeleid", + "foot_trademark": "TigerTag is een handelsmerk van TigerSystem. Merkbeleid", "foot_protocol": "TigerTag-protocol", "foot_source": "Bron van deze pagina" } diff --git a/site/i18n/pl.json b/site/i18n/pl.json index 1ef7bc6..1c9217c 100644 --- a/site/i18n/pl.json +++ b/site/i18n/pl.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, a potem TigerTag.fromPages(uid, payload).", "join_btn": "Zobacz w npm", "foot_licences": "SDK Apache-2.0 · Specyfikacja protokołu CC-BY-4.0", - "foot_trademark": "TigerTag jest znakiem towarowym TigerTag Corp. Zasady korzystania ze znaku", + "foot_trademark": "TigerTag jest znakiem towarowym TigerSystem. Zasady korzystania ze znaku", "foot_protocol": "Protokół TigerTag", "foot_source": "Źródło tej strony" } diff --git a/site/i18n/pt-pt.json b/site/i18n/pt-pt.json index e9662f4..b547478 100644 --- a/site/i18n/pt-pt.json +++ b/site/i18n/pt-pt.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag e, a seguir, TigerTag.fromPages(uid, payload).", "join_btn": "Ver no npm", "foot_licences": "SDK Apache-2.0 · Especificação do protocolo CC-BY-4.0", - "foot_trademark": "TigerTag é uma marca da TigerTag Corp. Política de marca", + "foot_trademark": "TigerTag é uma marca da TigerSystem. Política de marca", "foot_protocol": "Protocolo TigerTag", "foot_source": "Código-fonte desta página" } diff --git a/site/i18n/pt.json b/site/i18n/pt.json index a21dfc7..270ac35 100644 --- a/site/i18n/pt.json +++ b/site/i18n/pt.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag e depois TigerTag.fromPages(uid, payload).", "join_btn": "Ver no npm", "foot_licences": "SDK Apache-2.0 · Especificação do protocolo CC-BY-4.0", - "foot_trademark": "TigerTag é uma marca da TigerTag Corp. Política de marca", + "foot_trademark": "TigerTag é uma marca da TigerSystem. Política de marca", "foot_protocol": "Protocolo TigerTag", "foot_source": "Código desta página" } diff --git a/site/i18n/ru.json b/site/i18n/ru.json index 92bbccf..b1f2ccd 100644 --- a/site/i18n/ru.json +++ b/site/i18n/ru.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag, затем TigerTag.fromPages(uid, payload).", "join_btn": "Открыть в npm", "foot_licences": "SDK Apache-2.0 · Спецификация протокола CC-BY-4.0", - "foot_trademark": "TigerTag — товарный знак TigerTag Corp. Политика товарного знака", + "foot_trademark": "TigerTag — товарный знак TigerSystem. Политика товарного знака", "foot_protocol": "Протокол TigerTag", "foot_source": "Исходный код страницы" } diff --git a/site/i18n/zh.json b/site/i18n/zh.json index ff4aa96..3742d45 100644 --- a/site/i18n/zh.json +++ b/site/i18n/zh.json @@ -101,7 +101,7 @@ "join_text": "npm install tigertag,然后调用 TigerTag.fromPages(uid, payload)。", "join_btn": "在 npm 上查看", "foot_licences": "SDK Apache-2.0 · 协议规范 CC-BY-4.0", - "foot_trademark": "TigerTag 是 TigerTag Corp 的商标。商标政策", + "foot_trademark": "TigerTag 是 TigerSystem 的商标。商标政策", "foot_protocol": "TigerTag 协议", "foot_source": "本页源代码" } diff --git a/site/seo_check.py b/site/seo_check.py index 72656f8..3053166 100644 --- a/site/seo_check.py +++ b/site/seo_check.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # # TigerTag SDK -# Copyright (c) 2025-2026 TigerTag Corp. +# Copyright (c) 2025-2026 TigerSystem. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/catalog.js b/src/catalog.js index 0737d41..c3738b7 100644 --- a/src/catalog.js +++ b/src/catalog.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/datadir.js b/src/datadir.js index 1a46463..255cdb1 100644 --- a/src/datadir.js +++ b/src/datadir.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/db.js b/src/db.js index df65a91..71bf6ec 100644 --- a/src/db.js +++ b/src/db.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/index.js b/src/index.js index f8f683d..7ddbc37 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/signature.js b/src/signature.js index 4a125fb..44d9325 100644 --- a/src/signature.js +++ b/src/signature.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/tag.js b/src/tag.js index 46e5123..2577369 100644 --- a/src/tag.js +++ b/src/tag.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // // TigerTag SDK -// Copyright (c) 2025-2026 TigerTag Corp. +// Copyright (c) 2025-2026 TigerSystem. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.