This is page 12 of 16. Use http://codebase.md/lingodotdev/lingo.dev?page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── README.md
├── .claude
│ ├── agents
│ │ └── code-architect-reviewer.md
│ └── commands
│ ├── analyze-bucket-type.md
│ └── create-bucket-docs.md
├── .editorconfig
├── .github
│ ├── dependabot.yml
│ └── workflows
│ ├── docker.yml
│ ├── lingodotdev.yml
│ ├── pr-check.yml
│ ├── pr-lint.yml
│ └── release.yml
├── .gitignore
├── .husky
│ └── commit-msg
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── action.yml
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── composer.json
├── content
│ ├── banner.compiler.png
│ ├── banner.dark.png
│ └── banner.launch.png
├── CONTRIBUTING.md
├── DEBUGGING.md
├── demo
│ ├── adonisjs
│ │ ├── .editorconfig
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── ace.js
│ │ ├── adonisrc.ts
│ │ ├── app
│ │ │ ├── exceptions
│ │ │ │ └── handler.ts
│ │ │ └── middleware
│ │ │ └── container_bindings_middleware.ts
│ │ ├── bin
│ │ │ ├── console.ts
│ │ │ ├── server.ts
│ │ │ └── test.ts
│ │ ├── CHANGELOG.md
│ │ ├── config
│ │ │ ├── app.ts
│ │ │ ├── bodyparser.ts
│ │ │ ├── cors.ts
│ │ │ ├── hash.ts
│ │ │ ├── inertia.ts
│ │ │ ├── logger.ts
│ │ │ ├── session.ts
│ │ │ ├── shield.ts
│ │ │ ├── static.ts
│ │ │ └── vite.ts
│ │ ├── eslint.config.js
│ │ ├── inertia
│ │ │ ├── app
│ │ │ │ ├── app.tsx
│ │ │ │ └── ssr.tsx
│ │ │ ├── css
│ │ │ │ └── app.css
│ │ │ ├── lingo
│ │ │ │ ├── dictionary.js
│ │ │ │ └── meta.json
│ │ │ ├── pages
│ │ │ │ ├── errors
│ │ │ │ │ ├── not_found.tsx
│ │ │ │ │ └── server_error.tsx
│ │ │ │ └── home.tsx
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── resources
│ │ │ └── views
│ │ │ └── inertia_layout.edge
│ │ ├── start
│ │ │ ├── env.ts
│ │ │ ├── kernel.ts
│ │ │ └── routes.ts
│ │ ├── tests
│ │ │ └── bootstrap.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── next-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── eslint.config.mjs
│ │ ├── next.config.ts
│ │ ├── package.json
│ │ ├── postcss.config.mjs
│ │ ├── public
│ │ │ ├── file.svg
│ │ │ ├── globe.svg
│ │ │ ├── next.svg
│ │ │ ├── vercel.svg
│ │ │ └── window.svg
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── app
│ │ │ │ ├── client-component.tsx
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── globals.css
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── lingo-dot-dev.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ └── test
│ │ │ │ └── page.tsx
│ │ │ ├── components
│ │ │ │ ├── hero-actions.tsx
│ │ │ │ ├── hero-subtitle.tsx
│ │ │ │ ├── hero-title.tsx
│ │ │ │ └── index.ts
│ │ │ └── lingo
│ │ │ ├── dictionary.js
│ │ │ └── meta.json
│ │ └── tsconfig.json
│ ├── react-router-app
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── app.css
│ │ │ ├── lingo
│ │ │ │ ├── dictionary.js
│ │ │ │ └── meta.json
│ │ │ ├── root.tsx
│ │ │ ├── routes
│ │ │ │ ├── home.tsx
│ │ │ │ └── test.tsx
│ │ │ ├── routes.ts
│ │ │ └── welcome
│ │ │ ├── lingo-dot-dev.tsx
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo-light.svg
│ │ │ └── welcome.tsx
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ ├── public
│ │ │ └── favicon.ico
│ │ ├── react-router.config.ts
│ │ ├── README.md
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ └── vite-project
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── vite.svg
│ ├── README.md
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── assets
│ │ │ └── react.svg
│ │ ├── components
│ │ │ └── test.tsx
│ │ ├── index.css
│ │ ├── lingo
│ │ │ ├── dictionary.js
│ │ │ └── meta.json
│ │ ├── lingo-dot-dev.tsx
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── Dockerfile
├── i18n.json
├── i18n.lock
├── integrations
│ └── directus
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ ├── api.ts
│ │ ├── app.ts
│ │ └── index.spec.ts
│ ├── tsconfig.json
│ ├── tsconfig.test.json
│ └── tsup.config.ts
├── ISSUE_TEMPLATE.md
├── legacy
│ ├── cli
│ │ ├── bin
│ │ │ └── cli.mjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ └── readme.md
│ └── sdk
│ ├── CHANGELOG.md
│ ├── index.d.ts
│ ├── index.js
│ ├── package.json
│ └── README.md
├── LICENSE.md
├── mcp.md
├── package.json
├── packages
│ ├── cli
│ │ ├── assets
│ │ │ ├── failure.mp3
│ │ │ └── success.mp3
│ │ ├── bin
│ │ │ └── cli.mjs
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── android
│ │ │ │ ├── en
│ │ │ │ │ └── example.xml
│ │ │ │ ├── es
│ │ │ │ │ └── example.xml
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── csv
│ │ │ │ ├── example.csv
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── demo.spec.ts
│ │ │ ├── ejs
│ │ │ │ ├── en
│ │ │ │ │ └── example.ejs
│ │ │ │ ├── es
│ │ │ │ │ └── example.ejs
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── flutter
│ │ │ │ ├── en
│ │ │ │ │ └── example.arb
│ │ │ │ ├── es
│ │ │ │ │ └── example.arb
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── html
│ │ │ │ ├── en
│ │ │ │ │ └── example.html
│ │ │ │ ├── es
│ │ │ │ │ └── example.html
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── json
│ │ │ │ ├── en
│ │ │ │ │ └── example.json
│ │ │ │ ├── es
│ │ │ │ │ └── example.json
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── json-dictionary
│ │ │ │ ├── example.json
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── json5
│ │ │ │ ├── en
│ │ │ │ │ └── example.json5
│ │ │ │ ├── es
│ │ │ │ │ └── example.json5
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── jsonc
│ │ │ │ ├── en
│ │ │ │ │ └── example.jsonc
│ │ │ │ ├── es
│ │ │ │ │ └── example.jsonc
│ │ │ │ ├── i18n.json
│ │ │ │ ├── i18n.lock
│ │ │ │ └── ru
│ │ │ │ └── example.jsonc
│ │ │ ├── markdoc
│ │ │ │ ├── en
│ │ │ │ │ └── example.markdoc
│ │ │ │ ├── es
│ │ │ │ │ └── example.markdoc
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── markdown
│ │ │ │ ├── en
│ │ │ │ │ └── example.md
│ │ │ │ ├── es
│ │ │ │ │ └── example.md
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── mdx
│ │ │ │ ├── en
│ │ │ │ │ └── example.mdx
│ │ │ │ ├── es
│ │ │ │ │ └── example.mdx
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── php
│ │ │ │ ├── en
│ │ │ │ │ └── example.php
│ │ │ │ ├── es
│ │ │ │ │ └── example.php
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── po
│ │ │ │ ├── en
│ │ │ │ │ └── example.po
│ │ │ │ ├── es
│ │ │ │ │ └── example.po
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── properties
│ │ │ │ ├── en
│ │ │ │ │ └── example.properties
│ │ │ │ ├── es
│ │ │ │ │ └── example.properties
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── run_i18n.sh
│ │ │ ├── srt
│ │ │ │ ├── en
│ │ │ │ │ └── example.srt
│ │ │ │ ├── es
│ │ │ │ │ └── example.srt
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── txt
│ │ │ │ ├── en
│ │ │ │ │ └── example.txt
│ │ │ │ ├── es
│ │ │ │ │ └── example.txt
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── typescript
│ │ │ │ ├── en
│ │ │ │ │ └── example.ts
│ │ │ │ ├── es
│ │ │ │ │ └── example.ts
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── vtt
│ │ │ │ ├── en
│ │ │ │ │ └── example.vtt
│ │ │ │ ├── es
│ │ │ │ │ └── example.vtt
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── vue-json
│ │ │ │ ├── example.vue
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xcode-strings
│ │ │ │ ├── en
│ │ │ │ │ └── example.strings
│ │ │ │ ├── es
│ │ │ │ │ └── example.strings
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xcode-stringsdict
│ │ │ │ ├── en
│ │ │ │ │ └── example.stringsdict
│ │ │ │ ├── es
│ │ │ │ │ └── example.stringsdict
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xcode-xcstrings
│ │ │ │ ├── example.xcstrings
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xcode-xcstrings-v2
│ │ │ │ ├── complex-example.xcstrings
│ │ │ │ ├── example.xcstrings
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xliff
│ │ │ │ ├── en
│ │ │ │ │ ├── example-v1.2.xliff
│ │ │ │ │ └── example-v2.xliff
│ │ │ │ ├── es
│ │ │ │ │ ├── example-v1.2.xliff
│ │ │ │ │ ├── example-v2.xliff
│ │ │ │ │ └── example.xliff
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── xml
│ │ │ │ ├── en
│ │ │ │ │ └── example.xml
│ │ │ │ ├── es
│ │ │ │ │ └── example.xml
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ ├── yaml
│ │ │ │ ├── en
│ │ │ │ │ └── example.yml
│ │ │ │ ├── es
│ │ │ │ │ └── example.yml
│ │ │ │ ├── i18n.json
│ │ │ │ └── i18n.lock
│ │ │ └── yaml-root-key
│ │ │ ├── en
│ │ │ │ └── example.yml
│ │ │ ├── es
│ │ │ │ └── example.yml
│ │ │ ├── i18n.json
│ │ │ └── i18n.lock
│ │ ├── i18n.json
│ │ ├── i18n.lock
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── cli
│ │ │ │ ├── cmd
│ │ │ │ │ ├── auth.ts
│ │ │ │ │ ├── ci
│ │ │ │ │ │ ├── flows
│ │ │ │ │ │ │ ├── _base.ts
│ │ │ │ │ │ │ ├── in-branch.ts
│ │ │ │ │ │ │ └── pull-request.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── platforms
│ │ │ │ │ │ ├── _base.ts
│ │ │ │ │ │ ├── bitbucket.ts
│ │ │ │ │ │ ├── github.ts
│ │ │ │ │ │ ├── gitlab.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── cleanup.ts
│ │ │ │ │ ├── config
│ │ │ │ │ │ ├── get.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── set.ts
│ │ │ │ │ │ └── unset.ts
│ │ │ │ │ ├── i18n.ts
│ │ │ │ │ ├── init.ts
│ │ │ │ │ ├── lockfile.ts
│ │ │ │ │ ├── login.ts
│ │ │ │ │ ├── logout.ts
│ │ │ │ │ ├── may-the-fourth.ts
│ │ │ │ │ ├── mcp.ts
│ │ │ │ │ ├── purge.ts
│ │ │ │ │ ├── run
│ │ │ │ │ │ ├── _const.ts
│ │ │ │ │ │ ├── _types.ts
│ │ │ │ │ │ ├── _utils.ts
│ │ │ │ │ │ ├── execute.spec.ts
│ │ │ │ │ │ ├── execute.ts
│ │ │ │ │ │ ├── frozen.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── plan.ts
│ │ │ │ │ │ ├── setup.ts
│ │ │ │ │ │ └── watch.ts
│ │ │ │ │ ├── show
│ │ │ │ │ │ ├── _shared-key-command.ts
│ │ │ │ │ │ ├── config.ts
│ │ │ │ │ │ ├── files.ts
│ │ │ │ │ │ ├── ignored-keys.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── locale.ts
│ │ │ │ │ │ └── locked-keys.ts
│ │ │ │ │ └── status.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── index.spec.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loaders
│ │ │ │ │ ├── _types.ts
│ │ │ │ │ ├── _utils.ts
│ │ │ │ │ ├── android.spec.ts
│ │ │ │ │ ├── android.ts
│ │ │ │ │ ├── csv.spec.ts
│ │ │ │ │ ├── csv.ts
│ │ │ │ │ ├── dato
│ │ │ │ │ │ ├── _base.ts
│ │ │ │ │ │ ├── _utils.ts
│ │ │ │ │ │ ├── api.ts
│ │ │ │ │ │ ├── extract.ts
│ │ │ │ │ │ ├── filter.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── ejs.spec.ts
│ │ │ │ │ ├── ejs.ts
│ │ │ │ │ ├── ensure-key-order.spec.ts
│ │ │ │ │ ├── ensure-key-order.ts
│ │ │ │ │ ├── flat.spec.ts
│ │ │ │ │ ├── flat.ts
│ │ │ │ │ ├── flutter.spec.ts
│ │ │ │ │ ├── flutter.ts
│ │ │ │ │ ├── formatters
│ │ │ │ │ │ ├── _base.ts
│ │ │ │ │ │ ├── biome.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── prettier.ts
│ │ │ │ │ ├── html.ts
│ │ │ │ │ ├── icu-safety.spec.ts
│ │ │ │ │ ├── ignored-keys-buckets.spec.ts
│ │ │ │ │ ├── ignored-keys.spec.ts
│ │ │ │ │ ├── ignored-keys.ts
│ │ │ │ │ ├── index.spec.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── inject-locale.spec.ts
│ │ │ │ │ ├── inject-locale.ts
│ │ │ │ │ ├── json-dictionary.spec.ts
│ │ │ │ │ ├── json-dictionary.ts
│ │ │ │ │ ├── json-sorting.test.ts
│ │ │ │ │ ├── json-sorting.ts
│ │ │ │ │ ├── json.ts
│ │ │ │ │ ├── json5.spec.ts
│ │ │ │ │ ├── json5.ts
│ │ │ │ │ ├── jsonc.spec.ts
│ │ │ │ │ ├── jsonc.ts
│ │ │ │ │ ├── locked-keys.spec.ts
│ │ │ │ │ ├── locked-keys.ts
│ │ │ │ │ ├── locked-patterns.spec.ts
│ │ │ │ │ ├── locked-patterns.ts
│ │ │ │ │ ├── markdoc.spec.ts
│ │ │ │ │ ├── markdoc.ts
│ │ │ │ │ ├── markdown.ts
│ │ │ │ │ ├── mdx.spec.ts
│ │ │ │ │ ├── mdx.ts
│ │ │ │ │ ├── mdx2
│ │ │ │ │ │ ├── _types.ts
│ │ │ │ │ │ ├── _utils.ts
│ │ │ │ │ │ ├── code-placeholder.spec.ts
│ │ │ │ │ │ ├── code-placeholder.ts
│ │ │ │ │ │ ├── frontmatter-split.spec.ts
│ │ │ │ │ │ ├── frontmatter-split.ts
│ │ │ │ │ │ ├── localizable-document.spec.ts
│ │ │ │ │ │ ├── localizable-document.ts
│ │ │ │ │ │ ├── section-split.spec.ts
│ │ │ │ │ │ ├── section-split.ts
│ │ │ │ │ │ └── sections-split-2.ts
│ │ │ │ │ ├── passthrough.ts
│ │ │ │ │ ├── php.ts
│ │ │ │ │ ├── plutil-json-loader.ts
│ │ │ │ │ ├── po
│ │ │ │ │ │ ├── _types.ts
│ │ │ │ │ │ ├── index.spec.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── properties.ts
│ │ │ │ │ ├── root-key.ts
│ │ │ │ │ ├── srt.ts
│ │ │ │ │ ├── sync.ts
│ │ │ │ │ ├── text-file.ts
│ │ │ │ │ ├── txt.ts
│ │ │ │ │ ├── typescript
│ │ │ │ │ │ ├── cjs-interop.ts
│ │ │ │ │ │ ├── index.spec.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── unlocalizable.spec.ts
│ │ │ │ │ ├── unlocalizable.ts
│ │ │ │ │ ├── variable
│ │ │ │ │ │ ├── index.spec.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── vtt.ts
│ │ │ │ │ ├── vue-json.ts
│ │ │ │ │ ├── xcode-strings
│ │ │ │ │ │ ├── escape.ts
│ │ │ │ │ │ ├── parser.ts
│ │ │ │ │ │ ├── tokenizer.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── xcode-strings.spec.ts
│ │ │ │ │ ├── xcode-strings.ts
│ │ │ │ │ ├── xcode-stringsdict.ts
│ │ │ │ │ ├── xcode-xcstrings-icu.spec.ts
│ │ │ │ │ ├── xcode-xcstrings-icu.ts
│ │ │ │ │ ├── xcode-xcstrings-lock-compatibility.spec.ts
│ │ │ │ │ ├── xcode-xcstrings-v2-loader.ts
│ │ │ │ │ ├── xcode-xcstrings.spec.ts
│ │ │ │ │ ├── xcode-xcstrings.ts
│ │ │ │ │ ├── xliff.spec.ts
│ │ │ │ │ ├── xliff.ts
│ │ │ │ │ ├── xml.ts
│ │ │ │ │ └── yaml.ts
│ │ │ │ ├── localizer
│ │ │ │ │ ├── _types.ts
│ │ │ │ │ ├── explicit.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lingodotdev.ts
│ │ │ │ ├── processor
│ │ │ │ │ ├── _base.ts
│ │ │ │ │ ├── basic.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── lingo.ts
│ │ │ │ └── utils
│ │ │ │ ├── auth.ts
│ │ │ │ ├── buckets.spec.ts
│ │ │ │ ├── buckets.ts
│ │ │ │ ├── cache.ts
│ │ │ │ ├── cloudflare-status.ts
│ │ │ │ ├── config.ts
│ │ │ │ ├── delta.spec.ts
│ │ │ │ ├── delta.ts
│ │ │ │ ├── ensure-patterns.ts
│ │ │ │ ├── errors.ts
│ │ │ │ ├── exec.spec.ts
│ │ │ │ ├── exec.ts
│ │ │ │ ├── exit-gracefully.spec.ts
│ │ │ │ ├── exit-gracefully.ts
│ │ │ │ ├── exp-backoff.ts
│ │ │ │ ├── find-locale-paths.spec.ts
│ │ │ │ ├── find-locale-paths.ts
│ │ │ │ ├── fs.ts
│ │ │ │ ├── init-ci-cd.ts
│ │ │ │ ├── key-matching.spec.ts
│ │ │ │ ├── key-matching.ts
│ │ │ │ ├── lockfile.ts
│ │ │ │ ├── md5.ts
│ │ │ │ ├── observability.ts
│ │ │ │ ├── plutil-formatter.spec.ts
│ │ │ │ ├── plutil-formatter.ts
│ │ │ │ ├── settings.ts
│ │ │ │ ├── ui.ts
│ │ │ │ └── update-gitignore.ts
│ │ │ ├── compiler
│ │ │ │ └── index.ts
│ │ │ ├── locale-codes
│ │ │ │ └── index.ts
│ │ │ ├── react
│ │ │ │ ├── client.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── react-router.ts
│ │ │ │ └── rsc.ts
│ │ │ ├── sdk
│ │ │ │ └── index.ts
│ │ │ └── spec
│ │ │ └── index.ts
│ │ ├── tests
│ │ │ └── mock-storage.ts
│ │ ├── troubleshooting.md
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── tsup.config.ts
│ │ ├── types
│ │ │ ├── vtt.d.ts
│ │ │ └── xliff.d.ts
│ │ ├── vitest.config.ts
│ │ └── WATCH_MODE.md
│ ├── compiler
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── _base.ts
│ │ │ ├── _const.ts
│ │ │ ├── _loader-utils.spec.ts
│ │ │ ├── _loader-utils.ts
│ │ │ ├── _utils.spec.ts
│ │ │ ├── _utils.ts
│ │ │ ├── client-dictionary-loader.ts
│ │ │ ├── i18n-directive.spec.ts
│ │ │ ├── i18n-directive.ts
│ │ │ ├── index.spec.ts
│ │ │ ├── index.ts
│ │ │ ├── jsx-attribute-flag.spec.ts
│ │ │ ├── jsx-attribute-flag.ts
│ │ │ ├── jsx-attribute-scope-inject.spec.ts
│ │ │ ├── jsx-attribute-scope-inject.ts
│ │ │ ├── jsx-attribute-scopes-export.spec.ts
│ │ │ ├── jsx-attribute-scopes-export.ts
│ │ │ ├── jsx-attribute.spec.ts
│ │ │ ├── jsx-attribute.ts
│ │ │ ├── jsx-fragment.spec.ts
│ │ │ ├── jsx-fragment.ts
│ │ │ ├── jsx-html-lang.spec.ts
│ │ │ ├── jsx-html-lang.ts
│ │ │ ├── jsx-provider.spec.ts
│ │ │ ├── jsx-provider.ts
│ │ │ ├── jsx-remove-attributes.spec.ts
│ │ │ ├── jsx-remove-attributes.ts
│ │ │ ├── jsx-root-flag.spec.ts
│ │ │ ├── jsx-root-flag.ts
│ │ │ ├── jsx-scope-flag.spec.ts
│ │ │ ├── jsx-scope-flag.ts
│ │ │ ├── jsx-scope-inject.spec.ts
│ │ │ ├── jsx-scope-inject.ts
│ │ │ ├── jsx-scopes-export.spec.ts
│ │ │ ├── jsx-scopes-export.ts
│ │ │ ├── lib
│ │ │ │ └── lcp
│ │ │ │ ├── api
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── prompt.spec.ts
│ │ │ │ │ ├── prompt.ts
│ │ │ │ │ ├── provider-details.spec.ts
│ │ │ │ │ ├── provider-details.ts
│ │ │ │ │ ├── shots.ts
│ │ │ │ │ ├── xml2obj.spec.ts
│ │ │ │ │ └── xml2obj.ts
│ │ │ │ ├── api.spec.ts
│ │ │ │ ├── cache.spec.ts
│ │ │ │ ├── cache.ts
│ │ │ │ ├── index.spec.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── schema.ts
│ │ │ │ ├── server.spec.ts
│ │ │ │ └── server.ts
│ │ │ ├── lingo-turbopack-loader.ts
│ │ │ ├── react-router-dictionary-loader.ts
│ │ │ ├── rsc-dictionary-loader.ts
│ │ │ └── utils
│ │ │ ├── ast-key.spec.ts
│ │ │ ├── ast-key.ts
│ │ │ ├── create-locale-import-map.spec.ts
│ │ │ ├── create-locale-import-map.ts
│ │ │ ├── env.spec.ts
│ │ │ ├── env.ts
│ │ │ ├── hash.spec.ts
│ │ │ ├── hash.ts
│ │ │ ├── index.spec.ts
│ │ │ ├── index.ts
│ │ │ ├── invokations.spec.ts
│ │ │ ├── invokations.ts
│ │ │ ├── jsx-attribute-scope.ts
│ │ │ ├── jsx-attribute.spec.ts
│ │ │ ├── jsx-attribute.ts
│ │ │ ├── jsx-content-whitespace.spec.ts
│ │ │ ├── jsx-content.spec.ts
│ │ │ ├── jsx-content.ts
│ │ │ ├── jsx-element.spec.ts
│ │ │ ├── jsx-element.ts
│ │ │ ├── jsx-expressions.test.ts
│ │ │ ├── jsx-expressions.ts
│ │ │ ├── jsx-functions.spec.ts
│ │ │ ├── jsx-functions.ts
│ │ │ ├── jsx-scope.spec.ts
│ │ │ ├── jsx-scope.ts
│ │ │ ├── jsx-variables.spec.ts
│ │ │ ├── jsx-variables.ts
│ │ │ ├── llm-api-key.ts
│ │ │ ├── llm-api-keys.spec.ts
│ │ │ ├── locales.spec.ts
│ │ │ ├── locales.ts
│ │ │ ├── module-params.spec.ts
│ │ │ ├── module-params.ts
│ │ │ ├── observability.spec.ts
│ │ │ ├── observability.ts
│ │ │ ├── rc.spec.ts
│ │ │ └── rc.ts
│ │ ├── tsconfig.json
│ │ ├── tsup.config.ts
│ │ └── vitest.config.ts
│ ├── locales
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ ├── names
│ │ │ │ ├── index.spec.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── integration.spec.ts
│ │ │ │ └── loader.ts
│ │ │ ├── parser.spec.ts
│ │ │ ├── parser.ts
│ │ │ ├── types.ts
│ │ │ ├── validation.spec.ts
│ │ │ └── validation.ts
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── react
│ │ ├── build.config.ts
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── client
│ │ │ │ ├── attribute-component.spec.tsx
│ │ │ │ ├── attribute-component.tsx
│ │ │ │ ├── component.lingo-component.spec.tsx
│ │ │ │ ├── component.spec.tsx
│ │ │ │ ├── component.tsx
│ │ │ │ ├── context.spec.tsx
│ │ │ │ ├── context.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loader.spec.ts
│ │ │ │ ├── loader.ts
│ │ │ │ ├── locale-switcher.spec.tsx
│ │ │ │ ├── locale-switcher.tsx
│ │ │ │ ├── locale.spec.ts
│ │ │ │ ├── locale.ts
│ │ │ │ ├── provider.spec.tsx
│ │ │ │ ├── provider.tsx
│ │ │ │ ├── utils.spec.ts
│ │ │ │ └── utils.ts
│ │ │ ├── core
│ │ │ │ ├── attribute-component.spec.tsx
│ │ │ │ ├── attribute-component.tsx
│ │ │ │ ├── component.spec.tsx
│ │ │ │ ├── component.tsx
│ │ │ │ ├── const.ts
│ │ │ │ ├── get-dictionary.spec.ts
│ │ │ │ ├── get-dictionary.ts
│ │ │ │ └── index.ts
│ │ │ ├── react-router
│ │ │ │ ├── index.ts
│ │ │ │ ├── loader.spec.ts
│ │ │ │ └── loader.ts
│ │ │ ├── rsc
│ │ │ │ ├── attribute-component.tsx
│ │ │ │ ├── component.lingo-component.spec.tsx
│ │ │ │ ├── component.spec.tsx
│ │ │ │ ├── component.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── loader.spec.ts
│ │ │ │ ├── loader.ts
│ │ │ │ ├── provider.spec.tsx
│ │ │ │ ├── provider.tsx
│ │ │ │ ├── utils.spec.ts
│ │ │ │ └── utils.ts
│ │ │ └── test
│ │ │ └── setup.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── sdk
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── abort-controller.specs.ts
│ │ │ ├── index.spec.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ └── tsup.config.ts
│ └── spec
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ ├── config.spec.ts
│ │ ├── config.ts
│ │ ├── formats.ts
│ │ ├── index.spec.ts
│ │ ├── index.ts
│ │ ├── json-schema.ts
│ │ ├── locales.spec.ts
│ │ └── locales.ts
│ ├── tsconfig.json
│ ├── tsconfig.test.json
│ └── tsup.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── readme
│ ├── ar.md
│ ├── bn.md
│ ├── de.md
│ ├── en.md
│ ├── es.md
│ ├── fa.md
│ ├── fr.md
│ ├── he.md
│ ├── hi.md
│ ├── it.md
│ ├── ja.md
│ ├── ko.md
│ ├── pl.md
│ ├── pt-BR.md
│ ├── ru.md
│ ├── tr.md
│ ├── uk-UA.md
│ └── zh-Hans.md
├── readme.md
├── scripts
│ ├── docs
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── generate-cli-docs.ts
│ │ │ ├── generate-config-docs.ts
│ │ │ ├── json-schema
│ │ │ │ ├── markdown-renderer.test.ts
│ │ │ │ ├── markdown-renderer.ts
│ │ │ │ ├── parser.test.ts
│ │ │ │ ├── parser.ts
│ │ │ │ └── types.ts
│ │ │ ├── utils.test.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ └── packagist-publish.php
└── turbo.json
```
# Files
--------------------------------------------------------------------------------
/demo/next-app/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
# next-app
## 0.2.81
### Patch Changes
- Updated dependencies [[`68fb3ea`](https://github.com/lingodotdev/lingo.dev/commit/68fb3ea64fc0191ecee66403432e0c8efabab2b9)]:
- [email protected]
## 0.2.80
### Patch Changes
- Updated dependencies [[`e70385b`](https://github.com/lingodotdev/lingo.dev/commit/e70385bd1ac676bf5bd31b212d8510e6b7ebf793)]:
- [email protected]
## 0.2.79
### Patch Changes
- Updated dependencies [[`f7215c1`](https://github.com/lingodotdev/lingo.dev/commit/f7215c1e435378aac8fc953765335cd478cbf507)]:
- [email protected]
## 0.2.78
### Patch Changes
- Updated dependencies [[`898bd36`](https://github.com/lingodotdev/lingo.dev/commit/898bd36cc2e444641560d2ad2b28065a57072183)]:
- [email protected]
## 0.2.77
### Patch Changes
- Updated dependencies [[`060680c`](https://github.com/lingodotdev/lingo.dev/commit/060680cd13c05dd77dd9d5447c064d948bd21cb0), [`f102356`](https://github.com/lingodotdev/lingo.dev/commit/f102356e1ea12c800399ac11f074c42708c304b1), [`a956e53`](https://github.com/lingodotdev/lingo.dev/commit/a956e537d0d45565c3243dd0c5ba4eec8bed69c6), [`3fd38c2`](https://github.com/lingodotdev/lingo.dev/commit/3fd38c2d38e4b22dcd824c865fe31abbc56bc862)]:
- [email protected]
## 0.2.76
### Patch Changes
- Updated dependencies [[`03671f7`](https://github.com/lingodotdev/lingo.dev/commit/03671f7cb252d6bee3debce2f4a4eb989dc0050b)]:
- [email protected]
## 0.2.75
### Patch Changes
- Updated dependencies [[`4f5ffe6`](https://github.com/lingodotdev/lingo.dev/commit/4f5ffe62189949bb26a6c7825cb72c217aefa32f)]:
- [email protected]
## 0.2.74
### Patch Changes
- Updated dependencies [[`be8de32`](https://github.com/lingodotdev/lingo.dev/commit/be8de3280bb5dc5f409fc7680c0e5ff6a53e2fe5)]:
- [email protected]
## 0.2.73
### Patch Changes
- Updated dependencies [[`79c4c00`](https://github.com/lingodotdev/lingo.dev/commit/79c4c00108b9c102cf53e1c090b286070a43e3d5)]:
- [email protected]
## 0.2.72
### Patch Changes
- Updated dependencies [[`b45347c`](https://github.com/lingodotdev/lingo.dev/commit/b45347c38572ee371b2bc494261b7e3e90c4aed1)]:
- [email protected]
## 0.2.71
### Patch Changes
- Updated dependencies [[`74d8efe`](https://github.com/lingodotdev/lingo.dev/commit/74d8efef8d4789f9baa5b7837e053c2571df0308)]:
- [email protected]
## 0.2.70
### Patch Changes
- Updated dependencies [[`3d3c3d7`](https://github.com/lingodotdev/lingo.dev/commit/3d3c3d783a61443da50a5d182391db33a0d29c84)]:
- [email protected]
## 0.2.69
### Patch Changes
- Updated dependencies [[`38139c8`](https://github.com/lingodotdev/lingo.dev/commit/38139c81a85001739cece60873c0c6ad711327a4)]:
- [email protected]
## 0.2.68
### Patch Changes
- Updated dependencies [[`3413dad`](https://github.com/lingodotdev/lingo.dev/commit/3413dad22af688a6d26649c4f25e18304b3caee6)]:
- [email protected]
## 0.2.67
### Patch Changes
- Updated dependencies [[`26d2ec1`](https://github.com/lingodotdev/lingo.dev/commit/26d2ec155c5868a5bdce1027cd76a5a2d4f8f2b1)]:
- [email protected]
## 0.2.66
### Patch Changes
- Updated dependencies [[`82f5e7c`](https://github.com/lingodotdev/lingo.dev/commit/82f5e7cdde9a2a15b4c2a7fcb8c67ed64eab596b), [`e858174`](https://github.com/lingodotdev/lingo.dev/commit/e858174fd5165e0ea3e3f25fa1fc3edb292bc58f)]:
- [email protected]
## 0.2.65
### Patch Changes
- Updated dependencies [[`f3d4987`](https://github.com/lingodotdev/lingo.dev/commit/f3d4987ddc393c28d488f030c087f3e99a667975), [`a933b81`](https://github.com/lingodotdev/lingo.dev/commit/a933b8102763e0481f088c847da53e0eee3f0617)]:
- [email protected]
## 0.2.64
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.63
### Patch Changes
- Updated dependencies [[`dd0663f`](https://github.com/lingodotdev/lingo.dev/commit/dd0663fdcdd0ff4fd5748386758a8c20f9e52a4b)]:
- [email protected]
## 0.2.62
### Patch Changes
- Updated dependencies [[`762396b`](https://github.com/lingodotdev/lingo.dev/commit/762396bb37110dbe3e4e000edb27892b318aa3ef)]:
- [email protected]
## 0.2.61
### Patch Changes
- Updated dependencies [[`468a59b`](https://github.com/lingodotdev/lingo.dev/commit/468a59b89736c72253b1f32abbf30a950e5434ec)]:
- [email protected]
## 0.2.60
### Patch Changes
- Updated dependencies [[`bbc71b9`](https://github.com/lingodotdev/lingo.dev/commit/bbc71b9948ccc289c9669d8b0c276c9596f6a5e7)]:
- [email protected]
## 0.2.59
### Patch Changes
- Updated dependencies [[`0e6d605`](https://github.com/lingodotdev/lingo.dev/commit/0e6d605a9ad6835bef26c40895760c652a69b7a2)]:
- [email protected]
## 0.2.58
### Patch Changes
- Updated dependencies [[`03138da`](https://github.com/lingodotdev/lingo.dev/commit/03138dac37e869e2e99702ffd3c76532f1c58aa6), [`9557fe5`](https://github.com/lingodotdev/lingo.dev/commit/9557fe572d3e4a1a4d8c1e35417fe3b7531c3d52)]:
- [email protected]
## 0.2.57
### Patch Changes
- Updated dependencies [[`64225d0`](https://github.com/lingodotdev/lingo.dev/commit/64225d073999d599ba86f65fee8e08e3e5f2800b)]:
- [email protected]
## 0.2.56
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.55
### Patch Changes
- Updated dependencies [[`88b7e31`](https://github.com/lingodotdev/lingo.dev/commit/88b7e3132c77d0a1e823de4ee6ef5a96a3098b97)]:
- [email protected]
## 0.2.54
### Patch Changes
- Updated dependencies [[`d9294c0`](https://github.com/lingodotdev/lingo.dev/commit/d9294c0bbb993454ad3654f77dd48d82211e0465)]:
- [email protected]
## 0.2.53
### Patch Changes
- Updated dependencies [[`100b141`](https://github.com/lingodotdev/lingo.dev/commit/100b141d2143e33b603830475ba55089dc421e3d)]:
- [email protected]
## 0.2.52
### Patch Changes
- Updated dependencies [[`8741a20`](https://github.com/lingodotdev/lingo.dev/commit/8741a20dcaa3983131a1919f875dd2c264cb29fb)]:
- [email protected]
## 0.2.51
### Patch Changes
- Updated dependencies [[`bd3f69d`](https://github.com/lingodotdev/lingo.dev/commit/bd3f69dde76814146f775bc87241fa2fad012ab0)]:
- [email protected]
## 0.2.50
### Patch Changes
- Updated dependencies [[`6c174c3`](https://github.com/lingodotdev/lingo.dev/commit/6c174c38f3cf28c2af24ead18503658c3c641026)]:
- [email protected]
## 0.2.49
### Patch Changes
- Updated dependencies [[`3a642f3`](https://github.com/lingodotdev/lingo.dev/commit/3a642f33c04378706a8382aa0fde36e747fd6af5)]:
- [email protected]
## 0.2.48
### Patch Changes
- Updated dependencies [[`bc7b08e`](https://github.com/lingodotdev/lingo.dev/commit/bc7b08ef1245d1af0c68813cb18193d4f14bc7e0)]:
- [email protected]
## 0.2.47
### Patch Changes
- Updated dependencies [[`b6071e4`](https://github.com/lingodotdev/lingo.dev/commit/b6071e4f19dd1823f4f2ce54ba5495538a94d4fd)]:
- [email protected]
## 0.2.46
### Patch Changes
- Updated dependencies [[`e898c1e`](https://github.com/lingodotdev/lingo.dev/commit/e898c1eeb34e4dd3e74df26465802b520018acf9)]:
- [email protected]
## 0.2.45
### Patch Changes
- Updated dependencies [[`410825c`](https://github.com/lingodotdev/lingo.dev/commit/410825c8bf0029d8ee458514d6f203a7397c8f22)]:
- [email protected]
## 0.2.44
### Patch Changes
- Updated dependencies [[`555384d`](https://github.com/lingodotdev/lingo.dev/commit/555384dacf79167e1bb8b9e6871e153fea763471)]:
- [email protected]
## 0.2.43
### Patch Changes
- Updated dependencies [[`c0486ca`](https://github.com/lingodotdev/lingo.dev/commit/c0486ca9b0451ea75d070e199f502507ba418e5e)]:
- [email protected]
## 0.2.42
### Patch Changes
- Updated dependencies [[`99aae2d`](https://github.com/lingodotdev/lingo.dev/commit/99aae2d09a26060c810913f740893a4a5874d9d4)]:
- [email protected]
## 0.2.41
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.40
### Patch Changes
- Updated dependencies [[`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e)]:
- [email protected]
## 0.2.39
### Patch Changes
- Updated dependencies [[`1ff847b`](https://github.com/lingodotdev/lingo.dev/commit/1ff847b9273a3082178553e70c22524f5831ad36), [`55e9e68`](https://github.com/lingodotdev/lingo.dev/commit/55e9e687a3d0efa84b808818a848a276b1a42015), [`b9e2551`](https://github.com/lingodotdev/lingo.dev/commit/b9e2551f349e33542212f941b3407e8517b5fb27)]:
- [email protected]
## 0.2.38
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.37
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.36
### Patch Changes
- Updated dependencies [[`20a3737`](https://github.com/lingodotdev/lingo.dev/commit/20a3737ddb50b2a97699e57e03ea353b8912b78f)]:
- [email protected]
## 0.2.35
### Patch Changes
- Updated dependencies [[`afbb978`](https://github.com/lingodotdev/lingo.dev/commit/afbb978fec83d574f2c43b7d68457e435fca9b57)]:
- [email protected]
## 0.2.34
### Patch Changes
- Updated dependencies [[`1f1e33f`](https://github.com/lingodotdev/lingo.dev/commit/1f1e33fe4d0767c2f026214a505a2aa9f3785996), [`9266fd0`](https://github.com/lingodotdev/lingo.dev/commit/9266fd0bcddf4b07ca51d2609af92a9473106f9d)]:
- [email protected]
## 0.2.33
### Patch Changes
- Updated dependencies [[`6baa1a7`](https://github.com/lingodotdev/lingo.dev/commit/6baa1a7e88dbfac3783d1d49695595077fd8d209)]:
- [email protected]
## 0.2.32
### Patch Changes
- Updated dependencies [[`925997d`](https://github.com/lingodotdev/lingo.dev/commit/925997d75a1edbb4211a3be8db2b186cb139327e)]:
- [email protected]
## 0.2.31
### Patch Changes
- Updated dependencies [[`cb2aa0f`](https://github.com/lingodotdev/lingo.dev/commit/cb2aa0f505d6b7dbc435b526e8a6f62265d1f453)]:
- [email protected]
## 0.2.30
### Patch Changes
- Updated dependencies [[`bfcb424`](https://github.com/lingodotdev/lingo.dev/commit/bfcb424eb4479d0d3b767e062d30f02c5bcaeb14)]:
- [email protected]
## 0.2.29
### Patch Changes
- Updated dependencies [[`2b297ba`](https://github.com/lingodotdev/lingo.dev/commit/2b297babe76f9799c5154d9421fecd1ebbe1bb72)]:
- [email protected]
## 0.2.28
### Patch Changes
- Updated dependencies [[`30faa6d`](https://github.com/lingodotdev/lingo.dev/commit/30faa6d10e851a38ced86ae403b3a1fd48440bca)]:
- [email protected]
## 0.2.27
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.26
### Patch Changes
- Updated dependencies [[`4e9e368`](https://github.com/lingodotdev/lingo.dev/commit/4e9e36830ee4277ef9d65eee9ee92380a95a622c)]:
- [email protected]
## 0.2.25
### Patch Changes
- Updated dependencies [[`65701e5`](https://github.com/lingodotdev/lingo.dev/commit/65701e5b9694e811587ef600227251a1ff1384a0), [`4e55355`](https://github.com/lingodotdev/lingo.dev/commit/4e5535535029743b7a0edc4fdab3d4ee71374035)]:
- [email protected]
## 0.2.24
### Patch Changes
- Updated dependencies [[`f644123`](https://github.com/lingodotdev/lingo.dev/commit/f644123ddf6a6254790d08af50141e4dd78c3677)]:
- [email protected]
## 0.2.23
### Patch Changes
- Updated dependencies [[`29cf6a7`](https://github.com/lingodotdev/lingo.dev/commit/29cf6a7359707e0e341c11942d1ce6dedf7e66e5)]:
- [email protected]
## 0.2.22
### Patch Changes
- Updated dependencies [[`b249484`](https://github.com/lingodotdev/lingo.dev/commit/b249484d6f0060e29cd5b50b3d8ce68b857ccad5)]:
- [email protected]
## 0.2.21
### Patch Changes
- Updated dependencies [[`f7debef`](https://github.com/lingodotdev/lingo.dev/commit/f7debef9f004e670bb1f6a45ae17067a72a6e53f)]:
- [email protected]
## 0.2.20
### Patch Changes
- Updated dependencies [[`da6f0c8`](https://github.com/lingodotdev/lingo.dev/commit/da6f0c85e69687615df943323d261078742ba3f2)]:
- [email protected]
## 0.2.19
### Patch Changes
- Updated dependencies [[`8b306bc`](https://github.com/lingodotdev/lingo.dev/commit/8b306bcd0a3231ffd8bde283414b6d069b7a5b99), [`013fca0`](https://github.com/lingodotdev/lingo.dev/commit/013fca0f4252103ee3009fe3cdcfce2a87c80058)]:
- [email protected]
## 0.2.18
### Patch Changes
- Updated dependencies [[`84fd214`](https://github.com/lingodotdev/lingo.dev/commit/84fd214a21766e7683c5d645fcb8c4c0162eb0b6), [`0fc6385`](https://github.com/lingodotdev/lingo.dev/commit/0fc63856c6f49ac68a220b6e2f1c4f060e7ce78e), [`cac5429`](https://github.com/lingodotdev/lingo.dev/commit/cac54296d512d436dc3861441d5d1a3f1076792b)]:
- [email protected]
## 0.2.17
### Patch Changes
- Updated dependencies [[`ce0e5cd`](https://github.com/lingodotdev/lingo.dev/commit/ce0e5cd6d1ec17f5c593d394ceb63a28666df924)]:
- [email protected]
## 0.2.16
### Patch Changes
- Updated dependencies [[`ce8c75c`](https://github.com/lingodotdev/lingo.dev/commit/ce8c75c7fc1a2124d3e18444bc356c4dfce26434)]:
- [email protected]
## 0.2.15
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.14
### Patch Changes
- Updated dependencies [[`d80285a`](https://github.com/lingodotdev/lingo.dev/commit/d80285a9b12bd85425564cb00e558812fd0aee40)]:
- [email protected]
## 0.2.13
### Patch Changes
- Updated dependencies [[`81eff21`](https://github.com/lingodotdev/lingo.dev/commit/81eff2104a4401b1c1b6cdf4dcc7ca75b7411ba4)]:
- [email protected]
## 0.2.12
### Patch Changes
- Updated dependencies [[`b39b04a`](https://github.com/lingodotdev/lingo.dev/commit/b39b04ad83d3c8001008c3cefe309d8e762b2adc), [`4e5983d`](https://github.com/lingodotdev/lingo.dev/commit/4e5983d7e59ebf9eb529c4b7c1c87689432ac873)]:
- [email protected]
## 0.2.11
### Patch Changes
- Updated dependencies [[`1a3cbc1`](https://github.com/lingodotdev/lingo.dev/commit/1a3cbc1751c64e5617e91812506b3c061475f16a)]:
- [email protected]
## 0.2.10
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.9
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.8
### Patch Changes
- [`8e97256`](https://github.com/lingodotdev/lingo.dev/commit/8e97256ca4e78dd09a967539ca9dec359bd558ef) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix dictionary merging
- Updated dependencies []:
- [email protected]
## 0.2.7
### Patch Changes
- [#925](https://github.com/lingodotdev/lingo.dev/pull/925) [`215af19`](https://github.com/lingodotdev/lingo.dev/commit/215af1944667cce66e9c5966f4fb627186687b74) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - improved compiler concurrency, caching, added lingo.dev engine to the compiler, and updated demo apps
- Updated dependencies [[`1b9b113`](https://github.com/lingodotdev/lingo.dev/commit/1b9b11301978e8caa2555832d027ff93216aa6e1), [`0329a9c`](https://github.com/lingodotdev/lingo.dev/commit/0329a9cdb5e5a63fcecab4efcd7cce22f155a0e9)]:
- [email protected]
## 0.2.6
### Patch Changes
- Updated dependencies [[`3b6574f`](https://github.com/lingodotdev/lingo.dev/commit/3b6574f0499f3f4d3c48f66ba2b828d2c1c0ceb0), [`6b4b9e6`](https://github.com/lingodotdev/lingo.dev/commit/6b4b9e6cc9a0cb5da8a4df9e9ebda474bf2a18ed), [`6b4b9e6`](https://github.com/lingodotdev/lingo.dev/commit/6b4b9e6cc9a0cb5da8a4df9e9ebda474bf2a18ed)]:
- [email protected]
## 0.2.5
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.4
### Patch Changes
- Updated dependencies [[`2dd8170`](https://github.com/lingodotdev/lingo.dev/commit/2dd8170ff0101268f2253c9248409d184da5f75c)]:
- [email protected]
## 0.2.3
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.2.2
### Patch Changes
- Updated dependencies [[`cc232eb`](https://github.com/lingodotdev/lingo.dev/commit/cc232eb72d0e54b3571bbb70e88cdad24ba6372a)]:
- [email protected]
## 0.2.1
### Patch Changes
- Updated dependencies [[`fead8e0`](https://github.com/lingodotdev/lingo.dev/commit/fead8e08dc2b2869a093cb25a04f6e0aa78cf6b7)]:
- [email protected]
## 0.2.0
### Minor Changes
- [#897](https://github.com/lingodotdev/lingo.dev/pull/897) [`a5da697`](https://github.com/lingodotdev/lingo.dev/commit/a5da697f7efd46de31d17b202d06eb5f655ed9b9) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add support for other providers in the compiler and implement Google AI as a provider.
### Patch Changes
- Updated dependencies [[`a5da697`](https://github.com/lingodotdev/lingo.dev/commit/a5da697f7efd46de31d17b202d06eb5f655ed9b9), [`10a0139`](https://github.com/lingodotdev/lingo.dev/commit/10a0139edc9ffbc1c52ac2226f6b0f345cc19878)]:
- [email protected]
## 0.1.23
### Patch Changes
- Updated dependencies [[`3bd4045`](https://github.com/lingodotdev/lingo.dev/commit/3bd40450cbb5c8aabce61d7f1f3ab9c7293323d9)]:
- [email protected]
## 0.1.22
### Patch Changes
- Updated dependencies [[`f140f82`](https://github.com/lingodotdev/lingo.dev/commit/f140f820d00b15f99214a7eece1a9c7f0d098e90)]:
- [email protected]
## 0.1.21
### Patch Changes
- Updated dependencies [[`145fb74`](https://github.com/lingodotdev/lingo.dev/commit/145fb74c09b42c8810f351be5a641b1366881ae1), [`0c45acc`](https://github.com/lingodotdev/lingo.dev/commit/0c45accfc45e63f597758c47033bc58d2f6059b5)]:
- [email protected]
## 0.1.20
### Patch Changes
- Updated dependencies [[`511a2ec`](https://github.com/lingodotdev/lingo.dev/commit/511a2ecd68a9c5e2800035d5c6a6b5b31b2dc80f)]:
- [email protected]
## 0.1.19
### Patch Changes
- Updated dependencies [[`7191444`](https://github.com/lingodotdev/lingo.dev/commit/7191444f67864ea5b5a91a9be759b2445bf186d3)]:
- [email protected]
## 0.1.18
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.17
### Patch Changes
- Updated dependencies [[`af011b1`](https://github.com/lingodotdev/lingo.dev/commit/af011b18fe96f15287609278f4d4d2b343b6c2cc)]:
- [email protected]
## 0.1.16
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.15
### Patch Changes
- Updated dependencies [[`3750c9c`](https://github.com/lingodotdev/lingo.dev/commit/3750c9ca25a78280b04e4a2b2e6641dd21f9f3b0)]:
- [email protected]
## 0.1.14
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.13
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.12
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.11
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.10
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.9
### Patch Changes
- Updated dependencies [[`cb7d5e2`](https://github.com/lingodotdev/lingo.dev/commit/cb7d5e213282c00af658159472183a763f84ca3d)]:
- [email protected]
## 0.1.8
### Patch Changes
- Updated dependencies [[`5d27455`](https://github.com/lingodotdev/lingo.dev/commit/5d2745545044cbaddb099f7920c96fe198879ba3)]:
- [email protected]
## 0.1.7
### Patch Changes
- Updated dependencies [[`b67a331`](https://github.com/lingodotdev/lingo.dev/commit/b67a33141253fa755b5531e52cd690bf5824d4b6)]:
- [email protected]
## 0.1.6
### Patch Changes
- Updated dependencies []:
- [email protected]
## 0.1.5
### Patch Changes
- Updated dependencies [[`f42cff8`](https://github.com/lingodotdev/lingo.dev/commit/f42cff8355b1ff7bba1445bd04d11ee4672903c2)]:
- [email protected]
## 0.1.4
### Patch Changes
- Updated dependencies [[`920e3f5`](https://github.com/lingodotdev/lingo.dev/commit/920e3f5c3ca1fd51b0919db13a4787cfd616de54)]:
- [email protected]
## 0.1.3
### Patch Changes
- Updated dependencies [[`cdb59dd`](https://github.com/lingodotdev/lingo.dev/commit/cdb59dddcd14da1ba3181a33c4c119af877cb4f3)]:
- [email protected]
## 0.1.2
### Patch Changes
- Updated dependencies [[`caef325`](https://github.com/lingodotdev/lingo.dev/commit/caef3253bc99fa7bf7a0b40e5604c3590dcb4958)]:
- [email protected]
## 0.1.1
### Patch Changes
- Updated dependencies [[`e980e84`](https://github.com/lingodotdev/lingo.dev/commit/e980e84178439ad70417d38b425acf9148cfc4b6)]:
- [email protected]
```
--------------------------------------------------------------------------------
/packages/sdk/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import Z from "zod";
import { LocaleCode, localeCodeSchema } from "@lingo.dev/_spec";
import { createId } from "@paralleldrive/cuid2";
const engineParamsSchema = Z.object({
apiKey: Z.string(),
apiUrl: Z.string().url().default("https://engine.lingo.dev"),
batchSize: Z.number().int().gt(0).lte(250).default(25),
idealBatchItemSize: Z.number().int().gt(0).lte(2500).default(250),
}).passthrough();
const payloadSchema = Z.record(Z.string(), Z.any());
const referenceSchema = Z.record(localeCodeSchema, payloadSchema);
const hintsSchema = Z.record(Z.string(), Z.array(Z.string()));
const localizationParamsSchema = Z.object({
sourceLocale: Z.union([localeCodeSchema, Z.null()]),
targetLocale: localeCodeSchema,
fast: Z.boolean().optional(),
reference: referenceSchema.optional(),
hints: hintsSchema.optional(),
});
/**
* LingoDotDevEngine class for interacting with the LingoDotDev API
* A powerful localization engine that supports various content types including
* plain text, objects, chat sequences, and HTML documents.
*/
export class LingoDotDevEngine {
protected config: Z.infer<typeof engineParamsSchema>;
/**
* Create a new LingoDotDevEngine instance
* @param config - Configuration options for the Engine
*/
constructor(config: Partial<Z.infer<typeof engineParamsSchema>>) {
this.config = engineParamsSchema.parse(config);
}
/**
* Localize content using the Lingo.dev API
* @param payload - The content to be localized
* @param params - Localization parameters including source/target locales and fast mode option
* @param progressCallback - Optional callback function to report progress (0-100)
* @param signal - Optional AbortSignal to cancel the operation
* @returns Localized content
* @internal
*/
async _localizeRaw(
payload: Z.infer<typeof payloadSchema>,
params: Z.infer<typeof localizationParamsSchema>,
progressCallback?: (
progress: number,
sourceChunk: Record<string, string>,
processedChunk: Record<string, string>,
) => void,
signal?: AbortSignal,
): Promise<Record<string, string>> {
const finalPayload = payloadSchema.parse(payload);
const finalParams = localizationParamsSchema.parse(params);
const chunkedPayload = this.extractPayloadChunks(finalPayload);
const processedPayloadChunks: Record<string, string>[] = [];
const workflowId = createId();
for (let i = 0; i < chunkedPayload.length; i++) {
const chunk = chunkedPayload[i];
const percentageCompleted = Math.round(
((i + 1) / chunkedPayload.length) * 100,
);
const processedPayloadChunk = await this.localizeChunk(
finalParams.sourceLocale,
finalParams.targetLocale,
{ data: chunk, reference: params.reference, hints: params.hints },
workflowId,
params.fast || false,
signal,
);
if (progressCallback) {
progressCallback(percentageCompleted, chunk, processedPayloadChunk);
}
processedPayloadChunks.push(processedPayloadChunk);
}
return Object.assign({}, ...processedPayloadChunks);
}
/**
* Localize a single chunk of content
* @param sourceLocale - Source locale
* @param targetLocale - Target locale
* @param payload - Payload containing the chunk to be localized
* @param workflowId - Workflow ID for tracking
* @param fast - Whether to use fast mode
* @param signal - Optional AbortSignal to cancel the operation
* @returns Localized chunk
*/
private async localizeChunk(
sourceLocale: string | null,
targetLocale: string,
payload: {
data: Z.infer<typeof payloadSchema>;
reference?: Z.infer<typeof referenceSchema>;
hints?: Z.infer<typeof hintsSchema>;
},
workflowId: string,
fast: boolean,
signal?: AbortSignal,
): Promise<Record<string, string>> {
const res = await fetch(`${this.config.apiUrl}/i18n`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify(
{
params: { workflowId, fast },
locale: {
source: sourceLocale,
target: targetLocale,
},
data: payload.data,
reference: payload.reference,
hints: payload.hints,
},
null,
2,
),
signal,
});
if (!res.ok) {
if (res.status >= 500 && res.status < 600) {
const errorText = await res.text();
throw new Error(
`Server error (${res.status}): ${res.statusText}. ${errorText}. This may be due to temporary service issues.`,
);
} else if (res.status === 400) {
throw new Error(`Invalid request: ${res.statusText}`);
} else {
const errorText = await res.text();
throw new Error(errorText);
}
}
const jsonResponse = await res.json();
// when streaming the error is returned in the response body
if (!jsonResponse.data && jsonResponse.error) {
throw new Error(jsonResponse.error);
}
return jsonResponse.data || {};
}
/**
* Extract payload chunks based on the ideal chunk size
* @param payload - The payload to be chunked
* @returns An array of payload chunks
*/
private extractPayloadChunks(
payload: Record<string, string>,
): Record<string, string>[] {
const result: Record<string, string>[] = [];
let currentChunk: Record<string, string> = {};
let currentChunkItemCount = 0;
const payloadEntries = Object.entries(payload);
for (let i = 0; i < payloadEntries.length; i++) {
const [key, value] = payloadEntries[i];
currentChunk[key] = value;
currentChunkItemCount++;
const currentChunkSize = this.countWordsInRecord(currentChunk);
if (
currentChunkSize > this.config.idealBatchItemSize ||
currentChunkItemCount >= this.config.batchSize ||
i === payloadEntries.length - 1
) {
result.push(currentChunk);
currentChunk = {};
currentChunkItemCount = 0;
}
}
return result;
}
/**
* Count words in a record or array
* @param payload - The payload to count words in
* @returns The total number of words
*/
private countWordsInRecord(
payload: any | Record<string, any> | Array<any>,
): number {
if (Array.isArray(payload)) {
return payload.reduce(
(acc, item) => acc + this.countWordsInRecord(item),
0,
);
} else if (typeof payload === "object" && payload !== null) {
return Object.values(payload).reduce(
(acc: number, item) => acc + this.countWordsInRecord(item),
0,
);
} else if (typeof payload === "string") {
return payload.trim().split(/\s+/).filter(Boolean).length;
} else {
return 0;
}
}
/**
* Localize a typical JavaScript object
* @param obj - The object to be localized (strings will be extracted and translated)
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocale: The target language code (e.g., 'es')
* - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
* @param progressCallback - Optional callback function to report progress (0-100)
* @param signal - Optional AbortSignal to cancel the operation
* @returns A new object with the same structure but localized string values
*/
async localizeObject(
obj: Record<string, any>,
params: Z.infer<typeof localizationParamsSchema>,
progressCallback?: (
progress: number,
sourceChunk: Record<string, string>,
processedChunk: Record<string, string>,
) => void,
signal?: AbortSignal,
): Promise<Record<string, any>> {
return this._localizeRaw(obj, params, progressCallback, signal);
}
/**
* Localize a single text string
* @param text - The text string to be localized
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocale: The target language code (e.g., 'es')
* - fast: Optional boolean to enable fast mode (faster for bigger batches)
* @param progressCallback - Optional callback function to report progress (0-100)
* @param signal - Optional AbortSignal to cancel the operation
* @returns The localized text string
*/
async localizeText(
text: string,
params: Z.infer<typeof localizationParamsSchema>,
progressCallback?: (progress: number) => void,
signal?: AbortSignal,
): Promise<string> {
const response = await this._localizeRaw(
{ text },
params,
progressCallback,
signal,
);
return response.text || "";
}
/**
* Localize a text string to multiple target locales
* @param text - The text string to be localized
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocales: An array of target language codes (e.g., ['es', 'fr'])
* - fast: Optional boolean to enable fast mode (for bigger batches)
* @param signal - Optional AbortSignal to cancel the operation
* @returns An array of localized text strings
*/
async batchLocalizeText(
text: string,
params: {
sourceLocale: LocaleCode;
targetLocales: LocaleCode[];
fast?: boolean;
},
signal?: AbortSignal,
) {
const responses = await Promise.all(
params.targetLocales.map((targetLocale) =>
this.localizeText(
text,
{
sourceLocale: params.sourceLocale,
targetLocale,
fast: params.fast,
},
undefined,
signal,
),
),
);
return responses;
}
/**
* Localize an array of strings
* @param strings - An array of strings to be localized
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocale: The target language code (e.g., 'es')
* - fast: Optional boolean to enable fast mode (faster for bigger batches)
* @returns An array of localized strings in the same order
*/
async localizeStringArray(
strings: string[],
params: Z.infer<typeof localizationParamsSchema>,
): Promise<string[]> {
const mapped = strings.reduce(
(acc, str, i) => {
acc[`item_${i}`] = str;
return acc;
},
{} as Record<string, string>,
);
const result = await this.localizeObject(mapped, params);
return Object.values(result);
}
/**
* Localize a chat sequence while preserving speaker names
* @param chat - Array of chat messages, each with 'name' and 'text' properties
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocale: The target language code (e.g., 'es')
* - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
* @param progressCallback - Optional callback function to report progress (0-100)
* @param signal - Optional AbortSignal to cancel the operation
* @returns Array of localized chat messages with preserved structure
*/
async localizeChat(
chat: Array<{ name: string; text: string }>,
params: Z.infer<typeof localizationParamsSchema>,
progressCallback?: (progress: number) => void,
signal?: AbortSignal,
): Promise<Array<{ name: string; text: string }>> {
const localized = await this._localizeRaw(
{ chat },
params,
progressCallback,
signal,
);
return Object.entries(localized).map(([key, value]) => ({
name: chat[parseInt(key.split("_")[1])].name,
text: value,
}));
}
/**
* Localize an HTML document while preserving structure and formatting
* Handles both text content and localizable attributes (alt, title, placeholder, meta content)
* @param html - The HTML document string to be localized
* @param params - Localization parameters:
* - sourceLocale: The source language code (e.g., 'en')
* - targetLocale: The target language code (e.g., 'es')
* - fast: Optional boolean to enable fast mode (faster but potentially lower quality)
* @param progressCallback - Optional callback function to report progress (0-100)
* @param signal - Optional AbortSignal to cancel the operation
* @returns The localized HTML document as a string, with updated lang attribute
*/
async localizeHtml(
html: string,
params: Z.infer<typeof localizationParamsSchema>,
progressCallback?: (progress: number) => void,
signal?: AbortSignal,
): Promise<string> {
const jsdomPackage = await import("jsdom");
const { JSDOM } = jsdomPackage;
const dom = new JSDOM(html);
const document = dom.window.document;
const LOCALIZABLE_ATTRIBUTES: Record<string, string[]> = {
meta: ["content"],
img: ["alt"],
input: ["placeholder"],
a: ["title"],
};
const UNLOCALIZABLE_TAGS = ["script", "style"];
const extractedContent: Record<string, string> = {};
const getPath = (node: Node, attribute?: string): string => {
const indices: number[] = [];
let current = node as ChildNode;
let rootParent = "";
while (current) {
const parent = current.parentElement as Element;
if (!parent) break;
if (parent === document.documentElement) {
rootParent = current.nodeName.toLowerCase();
break;
}
const siblings = Array.from(parent.childNodes).filter(
(n) =>
n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim()),
);
const index = siblings.indexOf(current);
if (index !== -1) {
indices.unshift(index);
}
current = parent;
}
const basePath = rootParent
? `${rootParent}/${indices.join("/")}`
: indices.join("/");
return attribute ? `${basePath}#${attribute}` : basePath;
};
const processNode = (node: Node) => {
let parent = node.parentElement;
while (parent) {
if (UNLOCALIZABLE_TAGS.includes(parent.tagName.toLowerCase())) {
return;
}
parent = parent.parentElement;
}
if (node.nodeType === 3) {
const text = node.textContent?.trim() || "";
if (text) {
extractedContent[getPath(node)] = text;
}
} else if (node.nodeType === 1) {
const element = node as Element;
const tagName = element.tagName.toLowerCase();
const attributes = LOCALIZABLE_ATTRIBUTES[tagName] || [];
attributes.forEach((attr) => {
const value = element.getAttribute(attr);
if (value) {
extractedContent[getPath(element, attr)] = value;
}
});
Array.from(element.childNodes)
.filter(
(n) =>
n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim()),
)
.forEach(processNode);
}
};
Array.from(document.head.childNodes)
.filter(
(n) => n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim()),
)
.forEach(processNode);
Array.from(document.body.childNodes)
.filter(
(n) => n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim()),
)
.forEach(processNode);
const localizedContent = await this._localizeRaw(
extractedContent,
params,
progressCallback,
signal,
);
// Update the DOM with localized content
document.documentElement.setAttribute("lang", params.targetLocale);
Object.entries(localizedContent).forEach(([path, value]) => {
const [nodePath, attribute] = path.split("#");
const [rootTag, ...indices] = nodePath.split("/");
let parent: Element = rootTag === "head" ? document.head : document.body;
let current: Node | null = parent;
for (const index of indices) {
const siblings = Array.from(parent.childNodes).filter(
(n) =>
n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim()),
);
current = siblings[parseInt(index)] || null;
if (current?.nodeType === 1) {
parent = current as Element;
}
}
if (current) {
if (attribute) {
(current as Element).setAttribute(attribute, value);
} else {
current.textContent = value;
}
}
});
return dom.serialize();
}
/**
* Detect the language of a given text
* @param text - The text to analyze
* @param signal - Optional AbortSignal to cancel the operation
* @returns Promise resolving to a locale code (e.g., 'en', 'es', 'fr')
*/
async recognizeLocale(
text: string,
signal?: AbortSignal,
): Promise<LocaleCode> {
const response = await fetch(`${this.config.apiUrl}/recognize`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify({ text }),
signal,
});
if (!response.ok) {
if (response.status >= 500 && response.status < 600) {
throw new Error(
`Server error (${response.status}): ${response.statusText}. This may be due to temporary service issues.`,
);
}
throw new Error(`Error recognizing locale: ${response.statusText}`);
}
const jsonResponse = await response.json();
return jsonResponse.locale;
}
async whoami(
signal?: AbortSignal,
): Promise<{ email: string; id: string } | null> {
try {
const res = await fetch(`${this.config.apiUrl}/whoami`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
ContentType: "application/json",
},
signal,
});
if (res.ok) {
const payload = await res.json();
if (!payload?.email) {
return null;
}
return {
email: payload.email,
id: payload.id,
};
}
if (res.status >= 500 && res.status < 600) {
throw new Error(
`Server error (${res.status}): ${res.statusText}. This may be due to temporary service issues.`,
);
}
return null;
} catch (error) {
if (error instanceof Error && error.message.includes("Server error")) {
throw error;
}
return null;
}
}
}
/**
* @deprecated Use LingoDotDevEngine instead. This class is maintained for backwards compatibility.
*/
export class ReplexicaEngine extends LingoDotDevEngine {
private static hasWarnedDeprecation = false;
constructor(config: Partial<Z.infer<typeof engineParamsSchema>>) {
super(config);
if (!ReplexicaEngine.hasWarnedDeprecation) {
console.warn(
"ReplexicaEngine is deprecated and will be removed in a future release. " +
"Please use LingoDotDevEngine instead. " +
"See https://lingo.dev/cli for more information.",
);
ReplexicaEngine.hasWarnedDeprecation = true;
}
}
}
/**
* @deprecated Use LingoDotDevEngine instead. This class is maintained for backwards compatibility.
*/
export class LingoEngine extends LingoDotDevEngine {
private static hasWarnedDeprecation = false;
constructor(config: Partial<Z.infer<typeof engineParamsSchema>>) {
super(config);
if (!LingoEngine.hasWarnedDeprecation) {
console.warn(
"LingoEngine is deprecated and will be removed in a future release. " +
"Please use LingoDotDevEngine instead. " +
"See https://lingo.dev/cli for more information.",
);
LingoEngine.hasWarnedDeprecation = true;
}
}
}
```
--------------------------------------------------------------------------------
/packages/sdk/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
# @lingo.dev/\_sdk
## 0.12.6
### Patch Changes
- [#1230](https://github.com/lingodotdev/lingo.dev/pull/1230) [`b45347c`](https://github.com/lingodotdev/lingo.dev/commit/b45347c38572ee371b2bc494261b7e3e90c4aed1) Thanks [@vrcprl](https://github.com/vrcprl)! - add an xcode-xcstrings-v2 bucket type that supports cldr pluralization rules
- Updated dependencies [[`b45347c`](https://github.com/lingodotdev/lingo.dev/commit/b45347c38572ee371b2bc494261b7e3e90c4aed1)]:
- @lingo.dev/[email protected]
## 0.12.5
### Patch Changes
- Updated dependencies [[`82f5e7c`](https://github.com/lingodotdev/lingo.dev/commit/82f5e7cdde9a2a15b4c2a7fcb8c67ed64eab596b), [`e858174`](https://github.com/lingodotdev/lingo.dev/commit/e858174fd5165e0ea3e3f25fa1fc3edb292bc58f)]:
- @lingo.dev/[email protected]
## 0.12.4
### Patch Changes
- Updated dependencies [[`1fa218c`](https://github.com/lingodotdev/lingo.dev/commit/1fa218c13bf90df6d175fb18264f59c1a10b967c)]:
- @lingo.dev/[email protected]
## 0.12.3
### Patch Changes
- Updated dependencies [[`bbc71b9`](https://github.com/lingodotdev/lingo.dev/commit/bbc71b9948ccc289c9669d8b0c276c9596f6a5e7)]:
- @lingo.dev/[email protected]
## 0.12.2
### Patch Changes
- Updated dependencies [[`6579d70`](https://github.com/lingodotdev/lingo.dev/commit/6579d70bc670c2fdc06c09842d931b07e134151c)]:
- @lingo.dev/[email protected]
## 0.12.1
### Patch Changes
- Updated dependencies [[`a35032e`](https://github.com/lingodotdev/lingo.dev/commit/a35032e7e7a188d1f5e774576352068124526e24)]:
- @lingo.dev/[email protected]
## 0.12.0
### Minor Changes
- [#1066](https://github.com/lingodotdev/lingo.dev/pull/1066) [`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add hints support
### Patch Changes
- Updated dependencies [[`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e)]:
- @lingo.dev/[email protected]
## 0.11.0
### Minor Changes
- [#1049](https://github.com/lingodotdev/lingo.dev/pull/1049) [`85dfc10`](https://github.com/lingodotdev/lingo.dev/commit/85dfc10961b116e31b2bb478f42013756ca49974) Thanks [@VAIBHAVSING](https://github.com/VAIBHAVSING)! - Added new methods to the SDK:
1. `localizeStringArray`: Localizes an array of strings while maintaining their order.
Also added comprehensive tests for these methods using Vitest.
## 0.10.2
### Patch Changes
- Updated dependencies [[`afbb978`](https://github.com/lingodotdev/lingo.dev/commit/afbb978fec83d574f2c43b7d68457e435fca9b57)]:
- @lingo.dev/[email protected]
## 0.10.1
### Patch Changes
- [#1023](https://github.com/lingodotdev/lingo.dev/pull/1023) [`9266fd0`](https://github.com/lingodotdev/lingo.dev/commit/9266fd0bcddf4b07ca51d2609af92a9473106f9d) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Update Zod dependency to version 3.25.76
- Updated dependencies [[`9266fd0`](https://github.com/lingodotdev/lingo.dev/commit/9266fd0bcddf4b07ca51d2609af92a9473106f9d)]:
- @lingo.dev/[email protected]
## 0.10.0
### Minor Changes
- [#998](https://github.com/lingodotdev/lingo.dev/pull/998) [`cb2aa0f`](https://github.com/lingodotdev/lingo.dev/commit/cb2aa0f505d6b7dbc435b526e8a6f62265d1f453) Thanks [@VAIBHAVSING](https://github.com/VAIBHAVSING)! - Added support for AbortController to all public SDK methods, enabling consumers to cancel long-running operations using the standard AbortController API. Refactored internal methods to propagate AbortSignal and check for abortion between batch chunks. Updated fetch calls to use AbortSignal for network request cancellation.
## 0.9.6
### Patch Changes
- Updated dependencies [[`acd5356`](https://github.com/lingodotdev/lingo.dev/commit/acd5356b68d2261576240c173fea790864c3c31d)]:
- @lingo.dev/[email protected]
## 0.9.5
### Patch Changes
- Updated dependencies [[`f644123`](https://github.com/lingodotdev/lingo.dev/commit/f644123ddf6a6254790d08af50141e4dd78c3677)]:
- @lingo.dev/[email protected]
## 0.9.4
### Patch Changes
- Updated dependencies [[`84fd214`](https://github.com/lingodotdev/lingo.dev/commit/84fd214a21766e7683c5d645fcb8c4c0162eb0b6)]:
- @lingo.dev/[email protected]
## 0.9.3
### Patch Changes
- Updated dependencies [[`ce8c75c`](https://github.com/lingodotdev/lingo.dev/commit/ce8c75c7fc1a2124d3e18444bc356c4dfce26434)]:
- @lingo.dev/[email protected]
## 0.9.2
### Patch Changes
- [#937](https://github.com/lingodotdev/lingo.dev/pull/937) [`4e5983d`](https://github.com/lingodotdev/lingo.dev/commit/4e5983d7e59ebf9eb529c4b7c1c87689432ac873) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Update documentation URLs from docs.lingo.dev to lingo.dev/cli and lingo.dev/compiler
## 0.9.1
### Patch Changes
- Updated dependencies [[`1b9b113`](https://github.com/lingodotdev/lingo.dev/commit/1b9b11301978e8caa2555832d027ff93216aa6e1), [`0329a9c`](https://github.com/lingodotdev/lingo.dev/commit/0329a9cdb5e5a63fcecab4efcd7cce22f155a0e9)]:
- @lingo.dev/[email protected]
## 0.9.0
### Minor Changes
- [#915](https://github.com/lingodotdev/lingo.dev/pull/915) [`6b4b9e6`](https://github.com/lingodotdev/lingo.dev/commit/6b4b9e6cc9a0cb5da8a4df9e9ebda474bf2a18ed) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - feat: enhance 5xx error handling with Cloudflare status integration
- [#915](https://github.com/lingodotdev/lingo.dev/pull/915) [`6b4b9e6`](https://github.com/lingodotdev/lingo.dev/commit/6b4b9e6cc9a0cb5da8a4df9e9ebda474bf2a18ed) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - feat: enhance 5xx error handling with Cloudflare status integration
## 0.8.1
### Patch Changes
- Updated dependencies [[`a5da697`](https://github.com/lingodotdev/lingo.dev/commit/a5da697f7efd46de31d17b202d06eb5f655ed9b9)]:
- @lingo.dev/[email protected]
## 0.8.0
### Minor Changes
- [`e980e84`](https://github.com/lingodotdev/lingo.dev/commit/e980e84178439ad70417d38b425acf9148cfc4b6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added the compiler
### Patch Changes
- Updated dependencies [[`e980e84`](https://github.com/lingodotdev/lingo.dev/commit/e980e84178439ad70417d38b425acf9148cfc4b6)]:
- @lingo.dev/[email protected]
## 0.7.43
### Patch Changes
- Updated dependencies [[`0272fbf`](https://github.com/lingodotdev/lingo.dev/commit/0272fbf8847240ed9453130237d5843b918f869f)]:
- @lingo.dev/[email protected]
## 0.7.42
### Patch Changes
- [#782](https://github.com/lingodotdev/lingo.dev/pull/782) [`d913c20`](https://github.com/lingodotdev/lingo.dev/commit/d913c20fdf0086741c8b50fd4ddfb38eae304a24) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - parallel processing
- Updated dependencies [[`d913c20`](https://github.com/lingodotdev/lingo.dev/commit/d913c20fdf0086741c8b50fd4ddfb38eae304a24)]:
- @lingo.dev/[email protected]
## 0.7.41
### Patch Changes
- Updated dependencies [[`3f2aba9`](https://github.com/lingodotdev/lingo.dev/commit/3f2aba9c1d5834faf89a26194f1f3d9f9b878d40)]:
- @lingo.dev/[email protected]
## 0.7.40
### Patch Changes
- Updated dependencies [[`9aa7004`](https://github.com/lingodotdev/lingo.dev/commit/9aa700491446865dc131b80419f681132b888652)]:
- @lingo.dev/[email protected]
## 0.7.39
### Patch Changes
- Updated dependencies [[`5170449`](https://github.com/lingodotdev/lingo.dev/commit/517044905dfc682d6a5fa95b0605b8715e2b72c7)]:
- @lingo.dev/[email protected]
## 0.7.38
### Patch Changes
- Updated dependencies [[`c5ccf81`](https://github.com/lingodotdev/lingo.dev/commit/c5ccf81e9c2bd27bae332306da2a41e41bbeb87d)]:
- @lingo.dev/[email protected]
## 0.7.37
### Patch Changes
- [#649](https://github.com/lingodotdev/lingo.dev/pull/649) [`409018d`](https://github.com/lingodotdev/lingo.dev/commit/409018de74614a1fd99363c6749b0e4be9e1a278) Thanks [@mathio](https://github.com/mathio)! - refactor dependencies
- Updated dependencies [[`409018d`](https://github.com/lingodotdev/lingo.dev/commit/409018de74614a1fd99363c6749b0e4be9e1a278)]:
- @lingo.dev/[email protected]
## 0.7.36
### Patch Changes
- [#647](https://github.com/lingodotdev/lingo.dev/pull/647) [`235b6d9`](https://github.com/lingodotdev/lingo.dev/commit/235b6d914c5f542ee5f1a8a88085cfd9dea5409e) Thanks [@mathio](https://github.com/mathio)! - update vitest
- Updated dependencies [[`235b6d9`](https://github.com/lingodotdev/lingo.dev/commit/235b6d914c5f542ee5f1a8a88085cfd9dea5409e)]:
- @lingo.dev/[email protected]
## 0.7.35
### Patch Changes
- [#645](https://github.com/lingodotdev/lingo.dev/pull/645) [`d824b10`](https://github.com/lingodotdev/lingo.dev/commit/d824b106631f45fc428cf01f733aab4842b4fa81) Thanks [@mathio](https://github.com/mathio)! - update dependencies
- Updated dependencies [[`d824b10`](https://github.com/lingodotdev/lingo.dev/commit/d824b106631f45fc428cf01f733aab4842b4fa81)]:
- @lingo.dev/[email protected]
## 0.7.34
### Patch Changes
- Updated dependencies [[`82efe61`](https://github.com/lingodotdev/lingo.dev/commit/82efe6176db12cc7c5bbeb84f38bc3261f9eec4f), [`82efe61`](https://github.com/lingodotdev/lingo.dev/commit/82efe6176db12cc7c5bbeb84f38bc3261f9eec4f)]:
- @lingo.dev/[email protected]
## 0.7.33
### Patch Changes
- Updated dependencies [[`58f3959`](https://github.com/lingodotdev/lingo.dev/commit/58f39599b3b765ad807e725b4089a5e9b11a01b2)]:
- @lingo.dev/[email protected]
## 0.7.32
### Patch Changes
- Updated dependencies [[`fe922a4`](https://github.com/lingodotdev/lingo.dev/commit/fe922a469c2d5dac23a909a4fb67a6efd56d80d6)]:
- @lingo.dev/[email protected]
## 0.7.31
### Patch Changes
- Updated dependencies [[`2495afd`](https://github.com/lingodotdev/lingo.dev/commit/2495afd69e23700f96e19e5bbf74e393b29c2033), [`516a79c`](https://github.com/lingodotdev/lingo.dev/commit/516a79c75501c5960ae944379f38591806ca43e2), [`2cc6114`](https://github.com/lingodotdev/lingo.dev/commit/2cc61140fccc69ab73d40c7802a2d0e018889475)]:
- @lingo.dev/[email protected]
## 0.7.30
### Patch Changes
- Updated dependencies [[`1dbbfd2`](https://github.com/lingodotdev/lingo.dev/commit/1dbbfd2ed9f5a7e0479dc83f700fb68ee5347a18)]:
- @lingo.dev/[email protected]
## 0.7.29
### Patch Changes
- [#596](https://github.com/lingodotdev/lingo.dev/pull/596) [`61b487e`](https://github.com/lingodotdev/lingo.dev/commit/61b487e1e059328a32c3cdf673255d9d2cd480d9) Thanks [@vrcprl](https://github.com/vrcprl)! - add new locale
- Updated dependencies [[`61b487e`](https://github.com/lingodotdev/lingo.dev/commit/61b487e1e059328a32c3cdf673255d9d2cd480d9)]:
- @lingo.dev/[email protected]
## 0.7.28
### Patch Changes
- Updated dependencies [[`743d93e`](https://github.com/lingodotdev/lingo.dev/commit/743d93e554841bbd96d23682d8aec63cb4eb3ec8)]:
- @lingo.dev/[email protected]
## 0.7.27
### Patch Changes
- [#574](https://github.com/lingodotdev/lingo.dev/pull/574) [`dde7fbe`](https://github.com/lingodotdev/lingo.dev/commit/dde7fbe57fc9b1d3ce28e192b778921099354dad) Thanks [@mathio](https://github.com/mathio)! - handle errors from i18n when streaming
## 0.7.26
### Patch Changes
- [#553](https://github.com/lingodotdev/lingo.dev/pull/553) [`95023f2`](https://github.com/lingodotdev/lingo.dev/commit/95023f2c8da3958e8582628a22bf40674f8d2317) Thanks [@vrcprl](https://github.com/vrcprl)! - Add new locales
- Updated dependencies [[`95023f2`](https://github.com/lingodotdev/lingo.dev/commit/95023f2c8da3958e8582628a22bf40674f8d2317)]:
- @lingo.dev/[email protected]
## 0.7.25
### Patch Changes
- Updated dependencies [[`9089b08`](https://github.com/lingodotdev/lingo.dev/commit/9089b085b968ff3195866e377ecf3016aa06f959)]:
- @lingo.dev/[email protected]
## 0.7.24
### Patch Changes
- Updated dependencies [[`0b48be1`](https://github.com/lingodotdev/lingo.dev/commit/0b48be197e88dac581cc4f257789a04b43acf932)]:
- @lingo.dev/[email protected]
## 0.7.23
### Patch Changes
- [#537](https://github.com/lingodotdev/lingo.dev/pull/537) [`7597b99`](https://github.com/lingodotdev/lingo.dev/commit/7597b99c4869f63a42e6de3c4ed25424498d15ae) Thanks [@mathio](https://github.com/mathio)! - automatic source locale detection
## 0.7.22
### Patch Changes
- Updated dependencies [[`bafa755`](https://github.com/lingodotdev/lingo.dev/commit/bafa755d9681e93741462eb7bcf9b85073d20fd7)]:
- @lingo.dev/[email protected]
## 0.7.21
### Patch Changes
- Updated dependencies [[`444a731`](https://github.com/lingodotdev/lingo.dev/commit/444a7319a1351e22e5666504169023b4c8a29d5f)]:
- @lingo.dev/[email protected]
## 0.7.20
### Patch Changes
- [#515](https://github.com/lingodotdev/lingo.dev/pull/515) [`fd99a6c`](https://github.com/lingodotdev/lingo.dev/commit/fd99a6ca18ee21774ba5c2b7ce72d1712e374675) Thanks [@mathio](https://github.com/mathio)! - add typesVersions for support of older `moduleResolution`
## 0.7.19
### Patch Changes
- Updated dependencies [[`ec2902e`](https://github.com/lingodotdev/lingo.dev/commit/ec2902e5dc31fd79cc3b6fbf478ed1f3c4df0345)]:
- @lingo.dev/[email protected]
## 0.7.18
### Patch Changes
- Updated dependencies [[`beb0541`](https://github.com/lingodotdev/lingo.dev/commit/beb05411ee459461e05801a763b1fa28d288e04e)]:
- @lingo.dev/[email protected]
## 0.7.17
### Patch Changes
- [#493](https://github.com/lingodotdev/lingo.dev/pull/493) [`81527a4`](https://github.com/lingodotdev/lingo.dev/commit/81527a457ad8ef7fe735232caacdf2cc575e5b20) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix payload references
## 0.7.16
### Patch Changes
- Updated dependencies [[`a096300`](https://github.com/lingodotdev/lingo.dev/commit/a0963008ea2a8bbc910b0eaeb20f4e3b3cd641a7)]:
- @lingo.dev/[email protected]
## 0.7.15
### Patch Changes
- [#473](https://github.com/lingodotdev/lingo.dev/pull/473) [`3a99763`](https://github.com/lingodotdev/lingo.dev/commit/3a99763087512ba82955303d6f0567e813f4fa05) Thanks [@vrcprl](https://github.com/vrcprl)! - add new locales
- Updated dependencies [[`3a99763`](https://github.com/lingodotdev/lingo.dev/commit/3a99763087512ba82955303d6f0567e813f4fa05)]:
- @lingo.dev/[email protected]
## 0.7.14
### Patch Changes
- [#463](https://github.com/lingodotdev/lingo.dev/pull/463) [`f249d8f`](https://github.com/lingodotdev/lingo.dev/commit/f249d8f69d04f0ce40fd94e500e7b829b7ba1ed4) Thanks [@vrcprl](https://github.com/vrcprl)! - set utf-8 encoding explicitly
## 0.7.13
### Patch Changes
- [`dc8bfc7`](https://github.com/lingodotdev/lingo.dev/commit/dc8bfc7ddc38ade768b8aa11c56669db7eb446e6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - publish deps
- Updated dependencies [[`dc8bfc7`](https://github.com/lingodotdev/lingo.dev/commit/dc8bfc7ddc38ade768b8aa11c56669db7eb446e6)]:
- @lingo.dev/[email protected]
## 0.7.12
### Patch Changes
- [`6281dbd`](https://github.com/lingodotdev/lingo.dev/commit/6281dbd96bd5cfe54f194a6a1d055c8255a250de) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix sdk/spec exported types
- Updated dependencies [[`6281dbd`](https://github.com/lingodotdev/lingo.dev/commit/6281dbd96bd5cfe54f194a6a1d055c8255a250de)]:
- @lingo.dev/[email protected]
## 0.7.11
### Patch Changes
- [#419](https://github.com/lingodotdev/lingo.dev/pull/419) [`a45feb1`](https://github.com/lingodotdev/lingo.dev/commit/a45feb1d747f8fa32c42c1726953a04c174e754a) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Replexica is now Lingo.dev! 🎉
- Updated dependencies [[`a45feb1`](https://github.com/lingodotdev/lingo.dev/commit/a45feb1d747f8fa32c42c1726953a04c174e754a)]:
- @lingo.dev/[email protected]
## 0.7.10
### Patch Changes
- Updated dependencies [[`003344f`](https://github.com/lingodotdev/lingo.dev/commit/003344ffcca98a391a298707f18476971c4d4c2b)]:
- @replexica/[email protected]
## 0.7.9
### Patch Changes
- Updated dependencies [[`a2ada16`](https://github.com/lingodotdev/lingo.dev/commit/a2ada16ecf4cd559d3486f0e4756d58808194f7e)]:
- @replexica/[email protected]
## 0.7.8
### Patch Changes
- Updated dependencies [[`e6521b8`](https://github.com/lingodotdev/lingo.dev/commit/e6521b86637c254c011aba89a3558802c04ab3ca)]:
- @replexica/[email protected]
## 0.7.7
### Patch Changes
- Updated dependencies [[`cff3c4e`](https://github.com/lingodotdev/lingo.dev/commit/cff3c4eb1a40f82a9c4c095e49cfd9fce053b048)]:
- @replexica/[email protected]
## 0.7.6
### Patch Changes
- Updated dependencies [[`58d7b35`](https://github.com/lingodotdev/lingo.dev/commit/58d7b3567e51cc3ef0fad0288c13451381b95a98)]:
- @replexica/[email protected]
## 0.7.5
### Patch Changes
- Updated dependencies [[`9cf5299`](https://github.com/lingodotdev/lingo.dev/commit/9cf5299f7efbef70fd83f95177eac49b4d8f8007), [`3ab5de6`](https://github.com/lingodotdev/lingo.dev/commit/3ab5de66d8a913297b46095c2e73823124cc8c5b)]:
- @replexica/[email protected]
## 0.7.4
### Patch Changes
- Updated dependencies [[`1556977`](https://github.com/lingodotdev/lingo.dev/commit/1556977332a6f949100283bfa8c9a9ff5e74b156)]:
- @replexica/[email protected]
## 0.7.3
### Patch Changes
- [`cbef8f3`](https://github.com/lingodotdev/lingo.dev/commit/cbef8f3cafdc955d61053ce885d98e425acb668d) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - moved jsdom import into the html handler function
## 0.7.2
### Patch Changes
- Updated dependencies [[`5cb3c93`](https://github.com/lingodotdev/lingo.dev/commit/5cb3c930fff6e30cff5cc2266b794f75a0db646d)]:
- @replexica/[email protected]
## 0.7.1
### Patch Changes
- [`db819a4`](https://github.com/lingodotdev/lingo.dev/commit/db819a42412ceb67fedbe729b7d018952686d60b) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - reduce default batch size to avoid hitting rate limits
- [`2c5cbcf`](https://github.com/lingodotdev/lingo.dev/commit/2c5cbcfbf6feb28440255cdea0818c8cefa61d91) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - filter out non extistent keys
## 0.7.0
### Minor Changes
- [`c42dc2d`](https://github.com/lingodotdev/lingo.dev/commit/c42dc2d5b4efe95e804b5a7e7f6d354cf8622dc7) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add `batchLocalizeText` to sdk
## 0.6.0
### Minor Changes
- [`a71a88e`](https://github.com/lingodotdev/lingo.dev/commit/a71a88e5c8bd6601b0838c381433a87763142801) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fast mode
### Patch Changes
- [`f0a77ad`](https://github.com/lingodotdev/lingo.dev/commit/f0a77ad774a01c30e7e9bc5a0253638176332fd2) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - updated default batch size limits in the SDK
## 0.5.0
### Minor Changes
- [`ebf44cb`](https://github.com/lingodotdev/lingo.dev/commit/ebf44cbb462516abfe660c295c04627796c5a3a7) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - implement recognize locale
- [`42d0a5a`](https://github.com/lingodotdev/lingo.dev/commit/42d0a5a7a53e296192a31e8f1d67c126793ea280) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added .localizeHtml implementation to SDK
### Patch Changes
- Updated dependencies [[`a6b22a3`](https://github.com/lingodotdev/lingo.dev/commit/a6b22a3237f574455d8119f914d82b0b247b4151)]:
- @replexica/[email protected]
## 0.4.3
### Patch Changes
- Updated dependencies [[`091ee35`](https://github.com/lingodotdev/lingo.dev/commit/091ee353081795bf8f61c7d41483bc309c7b62ef)]:
- @replexica/[email protected]
## 0.4.2
### Patch Changes
- Updated dependencies [[`5e282d7`](https://github.com/lingodotdev/lingo.dev/commit/5e282d7ffa5ca9494aa7046a090bb7c327085a86)]:
- @replexica/[email protected]
## 0.4.1
### Patch Changes
- Updated dependencies [[`0071cd6`](https://github.com/lingodotdev/lingo.dev/commit/0071cd66b1c868ad3898fc368390a628c5a67767)]:
- @replexica/[email protected]
## 0.4.0
### Minor Changes
- [#264](https://github.com/lingodotdev/lingo.dev/pull/264) [`cdef5b7`](https://github.com/lingodotdev/lingo.dev/commit/cdef5b7bfbee4670c6de62cf4b4f3e0315748e25) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added format specific methods to `@replexica/sdk`
## 0.3.4
### Patch Changes
- Updated dependencies [[`2859938`](https://github.com/lingodotdev/lingo.dev/commit/28599388a91bf80cea3813bb4b8999bb4df302c9)]:
- @replexica/[email protected]
## 0.3.3
### Patch Changes
- Updated dependencies [[`ca9e20e`](https://github.com/lingodotdev/lingo.dev/commit/ca9e20eef9047e20d39ccf9dff74d2f6069d4676), [`2aedf3b`](https://github.com/lingodotdev/lingo.dev/commit/2aedf3bec2d9dffc7b43fc10dea0cab5742d44af), [`626082a`](https://github.com/lingodotdev/lingo.dev/commit/626082a64b88fb3b589acd950afeafe417ce5ddc)]:
- @replexica/[email protected]
## 0.3.2
### Patch Changes
- Updated dependencies [[`1601f70`](https://github.com/lingodotdev/lingo.dev/commit/1601f708bdf0ff1786d3bf9b19265ac5b567f740)]:
- @replexica/[email protected]
## 0.3.1
### Patch Changes
- Updated dependencies [[`bc5a28c`](https://github.com/lingodotdev/lingo.dev/commit/bc5a28c3c98b619872924b5f913229ac01387524)]:
- @replexica/[email protected]
## 0.3.0
### Minor Changes
- [#165](https://github.com/lingodotdev/lingo.dev/pull/165) [`5c2ca37`](https://github.com/lingodotdev/lingo.dev/commit/5c2ca37114663eaeb529a027e33949ef3839549b) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Update locale code resolution logic
### Patch Changes
- Updated dependencies [[`5c2ca37`](https://github.com/lingodotdev/lingo.dev/commit/5c2ca37114663eaeb529a027e33949ef3839549b)]:
- @replexica/[email protected]
## 0.2.1
### Patch Changes
- Updated dependencies [[`6870fc7`](https://github.com/lingodotdev/lingo.dev/commit/6870fc758dae9d1adb641576befbd8cda61cd5ea)]:
- @replexica/[email protected]
## 0.2.0
### Minor Changes
- [`d6e6d5c`](https://github.com/lingodotdev/lingo.dev/commit/d6e6d5c24b266de3769e95545f74632e7d75c697) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add support for multisource localization to the CLI
### Patch Changes
- Updated dependencies [[`d6e6d5c`](https://github.com/lingodotdev/lingo.dev/commit/d6e6d5c24b266de3769e95545f74632e7d75c697)]:
- @replexica/[email protected]
## 0.1.1
### Patch Changes
- Updated dependencies [[`73c9250`](https://github.com/lingodotdev/lingo.dev/commit/73c925084989ccea120cae1617ec87776c88e83e)]:
- @replexica/[email protected]
## 0.1.0
### Minor Changes
- [#142](https://github.com/lingodotdev/lingo.dev/pull/142) [`d9b0e51`](https://github.com/lingodotdev/lingo.dev/commit/d9b0e512196329cc781a4d33346f8ca0f3a81e7e) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Extract API calling into SDK package
```
--------------------------------------------------------------------------------
/packages/spec/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
# @lingo.dev/\_spec
## 0.41.1
### Patch Changes
- [#1230](https://github.com/lingodotdev/lingo.dev/pull/1230) [`b45347c`](https://github.com/lingodotdev/lingo.dev/commit/b45347c38572ee371b2bc494261b7e3e90c4aed1) Thanks [@vrcprl](https://github.com/vrcprl)! - add an xcode-xcstrings-v2 bucket type that supports cldr pluralization rules
## 0.41.0
### Minor Changes
- [#1186](https://github.com/lingodotdev/lingo.dev/pull/1186) [`82f5e7c`](https://github.com/lingodotdev/lingo.dev/commit/82f5e7cdde9a2a15b4c2a7fcb8c67ed64eab596b) Thanks [@davidturnbull](https://github.com/davidturnbull)! - Add Markdoc support
### Patch Changes
- [#1215](https://github.com/lingodotdev/lingo.dev/pull/1215) [`e858174`](https://github.com/lingodotdev/lingo.dev/commit/e858174fd5165e0ea3e3f25fa1fc3edb292bc58f) Thanks [@vrcprl](https://github.com/vrcprl)! - add provider settings
## 0.40.4
### Patch Changes
- [#1201](https://github.com/lingodotdev/lingo.dev/pull/1201) [`1fa218c`](https://github.com/lingodotdev/lingo.dev/commit/1fa218c13bf90df6d175fb18264f59c1a10b967c) Thanks [@vrcprl](https://github.com/vrcprl)! - add new languages Malayalam (India), Armenian (Armenia), Macedonian (Macedonia)
## 0.40.3
### Patch Changes
- [#1192](https://github.com/lingodotdev/lingo.dev/pull/1192) [`bbc71b9`](https://github.com/lingodotdev/lingo.dev/commit/bbc71b9948ccc289c9669d8b0c276c9596f6a5e7) Thanks [@vrcprl](https://github.com/vrcprl)! - Add biome support
## 0.40.2
### Patch Changes
- [#1171](https://github.com/lingodotdev/lingo.dev/pull/1171) [`6579d70`](https://github.com/lingodotdev/lingo.dev/commit/6579d70bc670c2fdc06c09842d931b07e134151c) Thanks [@vrcprl](https://github.com/vrcprl)! - add el-CY en-IE fr-LU locales
## 0.40.1
### Patch Changes
- [#1016](https://github.com/lingodotdev/lingo.dev/pull/1016) [`a35032e`](https://github.com/lingodotdev/lingo.dev/commit/a35032e7e7a188d1f5e774576352068124526e24) Thanks [@davidturnbull](https://github.com/davidturnbull)! - feat: add automated config documentation generator for i18n.json schema
## 0.40.0
### Minor Changes
- [#1066](https://github.com/lingodotdev/lingo.dev/pull/1066) [`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add hints support for xcode and jsonc buckets
## 0.39.3
### Patch Changes
- [#1031](https://github.com/lingodotdev/lingo.dev/pull/1031) [`afbb978`](https://github.com/lingodotdev/lingo.dev/commit/afbb978fec83d574f2c43b7d68457e435fca9b57) Thanks [@mathio](https://github.com/mathio)! - add json-dictionary loader support
## 0.39.2
### Patch Changes
- [#1023](https://github.com/lingodotdev/lingo.dev/pull/1023) [`9266fd0`](https://github.com/lingodotdev/lingo.dev/commit/9266fd0bcddf4b07ca51d2609af92a9473106f9d) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Update Zod dependency to version 3.25.76
## 0.39.1
### Patch Changes
- [#995](https://github.com/lingodotdev/lingo.dev/pull/995) [`acd5356`](https://github.com/lingodotdev/lingo.dev/commit/acd5356b68d2261576240c173fea790864c3c31d) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Add Icelandic (is) locale support with is-IS regional variant
## 0.39.0
### Minor Changes
- [#981](https://github.com/lingodotdev/lingo.dev/pull/981) [`f644123`](https://github.com/lingodotdev/lingo.dev/commit/f644123ddf6a6254790d08af50141e4dd78c3677) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Add support for plain TXT files to enable translation of fastlane App Store metadata and other plain text content
## 0.38.0
### Minor Changes
- [#958](https://github.com/lingodotdev/lingo.dev/pull/958) [`84fd214`](https://github.com/lingodotdev/lingo.dev/commit/84fd214a21766e7683c5d645fcb8c4c0162eb0b6) Thanks [@chrissiwaffler](https://github.com/chrissiwaffler)! - feat: add Mistral AI as a supported LLM provider
- Added Mistral AI provider support across the entire lingo.dev ecosystem
- Users can now use Mistral models for localization by setting MISTRAL_API_KEY
- Supports all Mistral models available through the @ai-sdk/mistral package
- Configuration via environment variable or user-wide config: `npx lingo.dev@latest config set llm.mistralApiKey <key>`
## 0.37.0
### Minor Changes
- [#956](https://github.com/lingodotdev/lingo.dev/pull/956) [`ce8c75c`](https://github.com/lingodotdev/lingo.dev/commit/ce8c75c7fc1a2124d3e18444bc356c4dfce26434) Thanks [@VAIBHAVSING](https://github.com/VAIBHAVSING)! - feat: add EJS (Embedded JavaScript) templating engine support
- Added EJS loader to support parsing and translating EJS template files
- EJS loader extracts translatable text while preserving EJS tags and expressions
- Updated spec package to include "ejs" in supported bucket types
- Added comprehensive test suite covering various EJS scenarios including conditionals, loops, includes, and mixed content
- Automatically installed EJS dependency (@types/ejs) for TypeScript support
## 0.36.0
### Minor Changes
- [#913](https://github.com/lingodotdev/lingo.dev/pull/913) [`1b9b113`](https://github.com/lingodotdev/lingo.dev/commit/1b9b11301978e8caa2555832d027ff93216aa6e1) Thanks [@The-Best-Codes](https://github.com/The-Best-Codes)! - Add support for Ollama as a CLI and Compiler provider.
### Patch Changes
- [#922](https://github.com/lingodotdev/lingo.dev/pull/922) [`0329a9c`](https://github.com/lingodotdev/lingo.dev/commit/0329a9cdb5e5a63fcecab4efcd7cce22f155a0e9) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add openrouter ais support for compiler
## 0.35.0
### Minor Changes
- [#897](https://github.com/lingodotdev/lingo.dev/pull/897) [`a5da697`](https://github.com/lingodotdev/lingo.dev/commit/a5da697f7efd46de31d17b202d06eb5f655ed9b9) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add support for other providers in the compiler and implement Google AI as a provider.
## 0.34.0
### Minor Changes
- [`e980e84`](https://github.com/lingodotdev/lingo.dev/commit/e980e84178439ad70417d38b425acf9148cfc4b6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added the compiler
## 0.33.3
### Patch Changes
- [#805](https://github.com/lingodotdev/lingo.dev/pull/805) [`0272fbf`](https://github.com/lingodotdev/lingo.dev/commit/0272fbf8847240ed9453130237d5843b918f869f) Thanks [@Vicentesan](https://github.com/Vicentesan)! - Introduce the gregorian language (ka-GE)
## 0.33.2
### Patch Changes
- [#782](https://github.com/lingodotdev/lingo.dev/pull/782) [`d913c20`](https://github.com/lingodotdev/lingo.dev/commit/d913c20fdf0086741c8b50fd4ddfb38eae304a24) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - parallel processing
## 0.33.1
### Patch Changes
- [#778](https://github.com/lingodotdev/lingo.dev/pull/778) [`3f2aba9`](https://github.com/lingodotdev/lingo.dev/commit/3f2aba9c1d5834faf89a26194f1f3d9f9b878d40) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add ignoredKeys
## 0.33.0
### Minor Changes
- [#759](https://github.com/lingodotdev/lingo.dev/pull/759) [`9aa7004`](https://github.com/lingodotdev/lingo.dev/commit/9aa700491446865dc131b80419f681132b888652) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Enhance TypeScript loader to support nested fields and arrays
## 0.32.0
### Minor Changes
- [#757](https://github.com/lingodotdev/lingo.dev/pull/757) [`5170449`](https://github.com/lingodotdev/lingo.dev/commit/517044905dfc682d6a5fa95b0605b8715e2b72c7) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Add TypeScript loader for .ts files that extracts string literals from default exports
## 0.31.0
### Minor Changes
- [#700](https://github.com/lingodotdev/lingo.dev/pull/700) [`c5ccf81`](https://github.com/lingodotdev/lingo.dev/commit/c5ccf81e9c2bd27bae332306da2a41e41bbeb87d) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - Add support for locked patterns in MDX loader
This change adds support for preserving specific patterns in MDX files during translation, including:
- !params syntax for parameter documentation
- !! parameter_name headings
- !type declarations
- !required flags
- !values lists
The implementation adds a new config version 1.7 with a "lockedPatterns" field that accepts an array of regex patterns to be preserved during translation.
## 0.30.3
### Patch Changes
- [#649](https://github.com/lingodotdev/lingo.dev/pull/649) [`409018d`](https://github.com/lingodotdev/lingo.dev/commit/409018de74614a1fd99363c6749b0e4be9e1a278) Thanks [@mathio](https://github.com/mathio)! - refactor dependencies
## 0.30.2
### Patch Changes
- [#647](https://github.com/lingodotdev/lingo.dev/pull/647) [`235b6d9`](https://github.com/lingodotdev/lingo.dev/commit/235b6d914c5f542ee5f1a8a88085cfd9dea5409e) Thanks [@mathio](https://github.com/mathio)! - update vitest
## 0.30.1
### Patch Changes
- [#645](https://github.com/lingodotdev/lingo.dev/pull/645) [`d824b10`](https://github.com/lingodotdev/lingo.dev/commit/d824b106631f45fc428cf01f733aab4842b4fa81) Thanks [@mathio](https://github.com/mathio)! - update dependencies
## 0.30.0
### Minor Changes
- [#631](https://github.com/lingodotdev/lingo.dev/pull/631) [`82efe61`](https://github.com/lingodotdev/lingo.dev/commit/82efe6176db12cc7c5bbeb84f38bc3261f9eec4f) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - double formatting for mdx
- [#631](https://github.com/lingodotdev/lingo.dev/pull/631) [`82efe61`](https://github.com/lingodotdev/lingo.dev/commit/82efe6176db12cc7c5bbeb84f38bc3261f9eec4f) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - advanced mdx support (shout out to @ZYJLiu!)
## 0.29.0
### Minor Changes
- [#629](https://github.com/lingodotdev/lingo.dev/pull/629) [`58f3959`](https://github.com/lingodotdev/lingo.dev/commit/58f39599b3b765ad807e725b4089a5e9b11a01b2) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - advanced mdx support (shout out to @ZYJLiu!)
## 0.28.0
### Minor Changes
- [#627](https://github.com/lingodotdev/lingo.dev/pull/627) [`fe922a4`](https://github.com/lingodotdev/lingo.dev/commit/fe922a469c2d5dac23a909a4fb67a6efd56d80d6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add support for json/yaml key locking
## 0.27.0
### Minor Changes
- [#614](https://github.com/lingodotdev/lingo.dev/pull/614) [`2495afd`](https://github.com/lingodotdev/lingo.dev/commit/2495afd69e23700f96e19e5bbf74e393b29c2033) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add basic translators
### Patch Changes
- [#616](https://github.com/lingodotdev/lingo.dev/pull/616) [`516a79c`](https://github.com/lingodotdev/lingo.dev/commit/516a79c75501c5960ae944379f38591806ca43e2) Thanks [@mathio](https://github.com/mathio)! - po files --frozen flag
- [`2cc6114`](https://github.com/lingodotdev/lingo.dev/commit/2cc61140fccc69ab73d40c7802a2d0e018889475) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add Welsh language support
## 0.26.6
### Patch Changes
- [#605](https://github.com/lingodotdev/lingo.dev/pull/605) [`1dbbfd2`](https://github.com/lingodotdev/lingo.dev/commit/1dbbfd2ed9f5a7e0479dc83f700fb68ee5347a18) Thanks [@mathio](https://github.com/mathio)! - inject locale
## 0.26.5
### Patch Changes
- [#596](https://github.com/lingodotdev/lingo.dev/pull/596) [`61b487e`](https://github.com/lingodotdev/lingo.dev/commit/61b487e1e059328a32c3cdf673255d9d2cd480d9) Thanks [@vrcprl](https://github.com/vrcprl)! - add new locale
## 0.26.4
### Patch Changes
- [#584](https://github.com/lingodotdev/lingo.dev/pull/584) [`743d93e`](https://github.com/lingodotdev/lingo.dev/commit/743d93e554841bbd96d23682d8aec63cb4eb3ec8) Thanks [@khalatevarun](https://github.com/khalatevarun)! - Add unit test for utility function in locales.ts
## 0.26.3
### Patch Changes
- [#553](https://github.com/lingodotdev/lingo.dev/pull/553) [`95023f2`](https://github.com/lingodotdev/lingo.dev/commit/95023f2c8da3958e8582628a22bf40674f8d2317) Thanks [@vrcprl](https://github.com/vrcprl)! - Add new locales
## 0.26.2
### Patch Changes
- [#546](https://github.com/lingodotdev/lingo.dev/pull/546) [`9089b08`](https://github.com/lingodotdev/lingo.dev/commit/9089b085b968ff3195866e377ecf3016aa06f959) Thanks [@mathio](https://github.com/mathio)! - add helper method to spec
## 0.26.1
### Patch Changes
- [`0b48be1`](https://github.com/lingodotdev/lingo.dev/commit/0b48be197e88dac581cc4f257789a04b43acf932) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add Kinyarwanda and Kiswahili
## 0.26.0
### Minor Changes
- [#530](https://github.com/lingodotdev/lingo.dev/pull/530) [`bafa755`](https://github.com/lingodotdev/lingo.dev/commit/bafa755d9681e93741462eb7bcf9b85073d20fd7) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add Kazakh (Kazakhstan) locale (localization engine passed the benchmarks!)
## 0.25.3
### Patch Changes
- [#518](https://github.com/lingodotdev/lingo.dev/pull/518) [`444a731`](https://github.com/lingodotdev/lingo.dev/commit/444a7319a1351e22e5666504169023b4c8a29d5f) Thanks [@mathio](https://github.com/mathio)! - support JSON messages in <i18n> block of .vue files
## 0.25.2
### Patch Changes
- [#498](https://github.com/lingodotdev/lingo.dev/pull/498) [`ec2902e`](https://github.com/lingodotdev/lingo.dev/commit/ec2902e5dc31fd79cc3b6fbf478ed1f3c4df0345) Thanks [@mathio](https://github.com/mathio)! - build json schema for config
## 0.25.1
### Patch Changes
- [#496](https://github.com/lingodotdev/lingo.dev/pull/496) [`beb0541`](https://github.com/lingodotdev/lingo.dev/commit/beb05411ee459461e05801a763b1fa28d288e04e) Thanks [@mathio](https://github.com/mathio)! - po files
## 0.25.0
### Minor Changes
- [#485](https://github.com/lingodotdev/lingo.dev/pull/485) [`a096300`](https://github.com/lingodotdev/lingo.dev/commit/a0963008ea2a8bbc910b0eaeb20f4e3b3cd641a7) Thanks [@mathio](https://github.com/mathio)! - add support for php buckets
## 0.24.4
### Patch Changes
- [#473](https://github.com/lingodotdev/lingo.dev/pull/473) [`3a99763`](https://github.com/lingodotdev/lingo.dev/commit/3a99763087512ba82955303d6f0567e813f4fa05) Thanks [@vrcprl](https://github.com/vrcprl)! - add new locales
## 0.24.3
### Patch Changes
- [`dc8bfc7`](https://github.com/lingodotdev/lingo.dev/commit/dc8bfc7ddc38ade768b8aa11c56669db7eb446e6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - publish deps
## 0.24.2
### Patch Changes
- [`6281dbd`](https://github.com/lingodotdev/lingo.dev/commit/6281dbd96bd5cfe54f194a6a1d055c8255a250de) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix sdk/spec exported types
## 0.24.1
### Patch Changes
- [#419](https://github.com/lingodotdev/lingo.dev/pull/419) [`a45feb1`](https://github.com/lingodotdev/lingo.dev/commit/a45feb1d747f8fa32c42c1726953a04c174e754a) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Replexica is now Lingo.dev! 🎉
## 0.24.0
### Minor Changes
- [`003344f`](https://github.com/lingodotdev/lingo.dev/commit/003344ffcca98a391a298707f18476971c4d4c2b) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add locale delimiter override
## 0.23.0
### Minor Changes
- [#390](https://github.com/lingodotdev/lingo.dev/pull/390) [`a2ada16`](https://github.com/lingodotdev/lingo.dev/commit/a2ada16ecf4cd559d3486f0e4756d58808194f7e) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add explicit regional flag support
## 0.22.1
### Patch Changes
- [#371](https://github.com/lingodotdev/lingo.dev/pull/371) [`e6521b8`](https://github.com/lingodotdev/lingo.dev/commit/e6521b86637c254c011aba89a3558802c04ab3ca) Thanks [@mathio](https://github.com/mathio)! - support underscore in locale code
## 0.22.0
### Minor Changes
- [#356](https://github.com/lingodotdev/lingo.dev/pull/356) [`cff3c4e`](https://github.com/lingodotdev/lingo.dev/commit/cff3c4eb1a40f82a9c4c095e49cfd9fce053b048) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add dato support
## 0.21.1
### Patch Changes
- [`58d7b35`](https://github.com/lingodotdev/lingo.dev/commit/58d7b3567e51cc3ef0fad0288c13451381b95a98) Thanks [@vrcprl](https://github.com/vrcprl)! - Added Telugu (India) locale
## 0.21.0
### Minor Changes
- [#327](https://github.com/lingodotdev/lingo.dev/pull/327) [`3ab5de6`](https://github.com/lingodotdev/lingo.dev/commit/3ab5de66d8a913297b46095c2e73823124cc8c5b) Thanks [@partik03](https://github.com/partik03)! - added support for xliff loader
### Patch Changes
- [`9cf5299`](https://github.com/lingodotdev/lingo.dev/commit/9cf5299f7efbef70fd83f95177eac49b4d8f8007) Thanks [@vrcprl](https://github.com/vrcprl)! - Add Tagalog
## 0.20.0
### Minor Changes
- [`1556977`](https://github.com/lingodotdev/lingo.dev/commit/1556977332a6f949100283bfa8c9a9ff5e74b156) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add new locales
## 0.19.0
### Minor Changes
- [`5cb3c93`](https://github.com/lingodotdev/lingo.dev/commit/5cb3c930fff6e30cff5cc2266b794f75a0db646d) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added Latin / Cyrilic modifiers for Serbian
## 0.18.0
### Minor Changes
- [#300](https://github.com/lingodotdev/lingo.dev/pull/300) [`a6b22a3`](https://github.com/lingodotdev/lingo.dev/commit/a6b22a3237f574455d8119f914d82b0b247b4151) Thanks [@partik03](https://github.com/partik03)! - implemented srt file loader and added support for srt file format in spec
## 0.17.0
### Minor Changes
- [#275](https://github.com/lingodotdev/lingo.dev/pull/275) [`091ee35`](https://github.com/lingodotdev/lingo.dev/commit/091ee353081795bf8f61c7d41483bc309c7b62ef) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add support for `.po` format
## 0.16.0
### Minor Changes
- [#268](https://github.com/lingodotdev/lingo.dev/pull/268) [`5e282d7`](https://github.com/lingodotdev/lingo.dev/commit/5e282d7ffa5ca9494aa7046a090bb7c327085a86) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - composable loaders
## 0.15.0
### Minor Changes
- [`0071cd6`](https://github.com/lingodotdev/lingo.dev/commit/0071cd66b1c868ad3898fc368390a628c5a67767) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add csv format support
## 0.14.1
### Patch Changes
- [`2859938`](https://github.com/lingodotdev/lingo.dev/commit/28599388a91bf80cea3813bb4b8999bb4df302c9) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add missing locales
## 0.14.0
### Minor Changes
- [`ca9e20e`](https://github.com/lingodotdev/lingo.dev/commit/ca9e20eef9047e20d39ccf9dff74d2f6069d4676) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - .strings support
- [`2aedf3b`](https://github.com/lingodotdev/lingo.dev/commit/2aedf3bec2d9dffc7b43fc10dea0cab5742d44af) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added support for .stringsdict
- [`626082a`](https://github.com/lingodotdev/lingo.dev/commit/626082a64b88fb3b589acd950afeafe417ce5ddc) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added Flutter .arb support
## 0.13.0
### Minor Changes
- [#181](https://github.com/lingodotdev/lingo.dev/pull/181) [`1601f70`](https://github.com/lingodotdev/lingo.dev/commit/1601f708bdf0ff1786d3bf9b19265ac5b567f740) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Added support for .properties file
## 0.12.1
### Patch Changes
- [`bc5a28c`](https://github.com/lingodotdev/lingo.dev/commit/bc5a28c3c98b619872924b5f913229ac01387524) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Fix spec imports
## 0.12.0
### Minor Changes
- [#165](https://github.com/lingodotdev/lingo.dev/pull/165) [`5c2ca37`](https://github.com/lingodotdev/lingo.dev/commit/5c2ca37114663eaeb529a027e33949ef3839549b) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Update locale code resolution logic
## 0.11.0
### Minor Changes
- [`6870fc7`](https://github.com/lingodotdev/lingo.dev/commit/6870fc758dae9d1adb641576befbd8cda61cd5ea) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Fix version number bumping in 1.2 config autoupgrade
## 0.10.0
### Minor Changes
- [`d6e6d5c`](https://github.com/lingodotdev/lingo.dev/commit/d6e6d5c24b266de3769e95545f74632e7d75c697) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add support for multisource localization to the CLI
## 0.9.0
### Minor Changes
- [#158](https://github.com/lingodotdev/lingo.dev/pull/158) [`73c9250`](https://github.com/lingodotdev/lingo.dev/commit/73c925084989ccea120cae1617ec87776c88e83e) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Configuration spec v1.1: Improved bucket config structure, to support exclusion patterns
## 0.8.0
### Minor Changes
- [`8c8e7dd`](https://github.com/lingodotdev/lingo.dev/commit/8c8e7dd4d35669d484240d643427612ecdaf73eb) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Added new locales
## 0.7.0
### Minor Changes
- [`c0be1a2`](https://github.com/lingodotdev/lingo.dev/commit/c0be1a29e3069ef2c8bdc4e4f52d2fb17abdb1f5) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Replaced `replexica config` with `replexica show config`. Added `replexica show locale sources` and `replexica show locale targets`.
## 0.6.0
### Minor Changes
- [`10252ce`](https://github.com/lingodotdev/lingo.dev/commit/10252ceaa2685cc23f4dbeb6ac985cc2148853e2) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Add android support
## 0.5.1
### Patch Changes
- [`088de18`](https://github.com/lingodotdev/lingo.dev/commit/088de18a53f45fa8df5833fe81ed96a2ed231299) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Fix @replexica/config reference
## 0.5.0
### Minor Changes
- [#99](https://github.com/lingodotdev/lingo.dev/pull/99) [`4e94058`](https://github.com/lingodotdev/lingo.dev/commit/4e940582ea8ebe5a058b76fb33420729f7bfdcef) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - Added support for i18n lockfiles to improve AI localization performance.
## 0.4.1
### Patch Changes
- [#94](https://github.com/lingodotdev/lingo.dev/pull/94) [`abab45c`](https://github.com/lingodotdev/lingo.dev/commit/abab45cc91675f507499bf84350b080cd647c464) Thanks [@vrcprl](https://github.com/vrcprl)! - Locales mapping (ex. en -> en-US)
## 0.4.0
### Minor Changes
- [#87](https://github.com/lingodotdev/lingo.dev/pull/87) [`07657c6`](https://github.com/lingodotdev/lingo.dev/commit/07657c611306797d605718e13ce6b2c920a5a94e) Thanks [@vrcprl](https://github.com/vrcprl)! - added new core locales : ja de pt it ru uk hi zh ko tr ar and source locales yue pl sk th
## 0.3.0
### Minor Changes
- [`830d4a4`](https://github.com/lingodotdev/lingo.dev/commit/830d4a441c4d1177c9356756a9e9afc170a386d6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add support for shyriiwook language
## 0.2.0
### Minor Changes
- [#76](https://github.com/lingodotdev/lingo.dev/pull/76) [`69d487c`](https://github.com/lingodotdev/lingo.dev/commit/69d487c0b4c8e22f9c86867ebf6cc55ea2875dbf) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - enable french, catalan in source/target mode, and czech in source-only mode
## 0.1.0
### Minor Changes
- [#73](https://github.com/lingodotdev/lingo.dev/pull/73) [`94ab265`](https://github.com/lingodotdev/lingo.dev/commit/94ab26551577b5dfab629ffee3c82e59b56ce25d) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - intro a `@replexica/spec` package containing common definitions, constants, schemas, and types
- [#75](https://github.com/lingodotdev/lingo.dev/pull/75) [`b11b48e`](https://github.com/lingodotdev/lingo.dev/commit/b11b48e7c3ab05dd8de0ddcfe5cb4589786abbf9) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - framework-agnostic i18n support
```
--------------------------------------------------------------------------------
/packages/cli/src/cli/cmd/status.ts:
--------------------------------------------------------------------------------
```typescript
import {
bucketTypeSchema,
I18nConfig,
localeCodeSchema,
resolveOverriddenLocale,
} from "@lingo.dev/_spec";
import { Command } from "interactive-commander";
import Z from "zod";
import _ from "lodash";
import * as path from "path";
import { getConfig } from "../utils/config";
import { getSettings } from "../utils/settings";
import { CLIError } from "../utils/errors";
import Ora from "ora";
import createBucketLoader from "../loaders";
import { createAuthenticator } from "../utils/auth";
import { getBuckets } from "../utils/buckets";
import chalk from "chalk";
import Table from "cli-table3";
import { createDeltaProcessor } from "../utils/delta";
import trackEvent from "../utils/observability";
import { minimatch } from "minimatch";
import { exitGracefully } from "../utils/exit-gracefully";
// Define types for our language stats
interface LanguageStats {
complete: number;
missing: number;
updated: number;
words: number;
}
export default new Command()
.command("status")
.description("Show the status of the localization process")
.helpOption("-h, --help", "Show help")
.option(
"--locale <locale>",
"Limit the report to specific target locales from i18n.json. Repeat the flag to include multiple locales. Defaults to all configured target locales",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--bucket <bucket>",
"Limit the report to specific bucket types defined in i18n.json (e.g., json, yaml, android). Repeat the flag to include multiple bucket types. Defaults to all buckets",
(val: string, prev: string[]) => (prev ? [...prev, val] : [val]),
)
.option(
"--file [files...]",
"Filter the status report to only include files whose paths contain these substrings. Example: 'components' to match any file path containing 'components'",
)
.option(
"--force",
"Force all keys to be counted as needing translation, bypassing change detection. Shows word estimates for a complete retranslation regardless of current translation status",
)
.option(
"--verbose",
"Print detailed output showing missing and updated key counts with example key names for each file and locale",
)
.option(
"--api-key <api-key>",
"Override the API key from settings or environment variables for this run",
)
.action(async function (options) {
const ora = Ora();
const flags = parseFlags(options);
let authId: string | null = null;
try {
ora.start("Loading configuration...");
const i18nConfig = getConfig();
const settings = getSettings(flags.apiKey);
ora.succeed("Configuration loaded");
// Try to authenticate, but continue even if not authenticated
try {
ora.start("Checking authentication status...");
const auth = await tryAuthenticate(settings);
if (auth) {
authId = auth.id;
ora.succeed(`Authenticated as ${auth.email}`);
} else {
ora.info(
"Not authenticated. Continuing without authentication. (Run `lingo.dev login` to authenticate)",
);
}
} catch (error) {
ora.info("Authentication failed. Continuing without authentication.");
}
ora.start("Validating localization configuration...");
validateParams(i18nConfig, flags);
ora.succeed("Localization configuration is valid");
// Track event with or without authentication
trackEvent(authId || "status", "cmd.status.start", {
i18nConfig,
flags,
});
let buckets = getBuckets(i18nConfig!);
if (flags.bucket?.length) {
buckets = buckets.filter((bucket: any) =>
flags.bucket!.includes(bucket.type),
);
}
ora.succeed("Buckets retrieved");
if (flags.file?.length) {
buckets = buckets
.map((bucket: any) => {
const paths = bucket.paths.filter((path: any) =>
flags.file!.find(
(file) =>
path.pathPattern?.includes(file) ||
path.pathPattern?.match(file) ||
minimatch(path.pathPattern, file),
),
);
return { ...bucket, paths };
})
.filter((bucket: any) => bucket.paths.length > 0);
if (buckets.length === 0) {
ora.fail(
"No buckets found. All buckets were filtered out by --file option.",
);
process.exit(1);
} else {
ora.info(`\x1b[36mProcessing only filtered buckets:\x1b[0m`);
buckets.map((bucket: any) => {
ora.info(` ${bucket.type}:`);
bucket.paths.forEach((path: any) => {
ora.info(` - ${path.pathPattern}`);
});
});
}
}
const targetLocales = flags.locale?.length
? flags.locale
: i18nConfig!.locale.targets;
// Global stats
let totalSourceKeyCount = 0;
let uniqueKeysToTranslate = 0;
let totalExistingTranslations = 0;
const totalWordCount = new Map<string, number>(); // Words per language
const languageStats: Record<string, LanguageStats> = {};
// Initialize per-language stats
for (const locale of targetLocales) {
languageStats[locale] = {
complete: 0,
missing: 0,
updated: 0,
words: 0,
};
totalWordCount.set(locale, 0);
}
// Per-file stats
const fileStats: Record<
string,
{
path: string;
sourceKeys: number;
wordCount: number;
languageStats: Record<
string,
{
complete: number;
missing: number;
updated: number;
words: number;
}
>;
}
> = {};
// Process each bucket
for (const bucket of buckets) {
try {
console.log();
ora.info(`Analyzing bucket: ${bucket.type}`);
for (const bucketPath of bucket.paths) {
const bucketOra = Ora({ indent: 2 }).info(
`Analyzing path: ${bucketPath.pathPattern}`,
);
const sourceLocale = resolveOverriddenLocale(
i18nConfig!.locale.source,
bucketPath.delimiter,
);
const bucketLoader = createBucketLoader(
bucket.type,
bucketPath.pathPattern,
{
defaultLocale: sourceLocale,
injectLocale: bucket.injectLocale,
formatter: i18nConfig!.formatter,
},
bucket.lockedKeys,
bucket.lockedPatterns,
bucket.ignoredKeys,
);
bucketLoader.setDefaultLocale(sourceLocale);
await bucketLoader.init();
// Initialize file stats
const filePath = bucketPath.pathPattern;
if (!fileStats[filePath]) {
fileStats[filePath] = {
path: filePath,
sourceKeys: 0,
wordCount: 0,
languageStats: {},
};
for (const locale of targetLocales) {
fileStats[filePath].languageStats[locale] = {
complete: 0,
missing: 0,
updated: 0,
words: 0,
};
}
}
// Get source data and count source keys
const sourceData = await bucketLoader.pull(sourceLocale);
const sourceKeys = Object.keys(sourceData);
fileStats[filePath].sourceKeys = sourceKeys.length;
totalSourceKeyCount += sourceKeys.length;
// Calculate source word count
let sourceWordCount = 0;
for (const key of sourceKeys) {
const value = sourceData[key];
if (typeof value === "string") {
const words = value.trim().split(/\s+/).length;
sourceWordCount += words;
}
}
fileStats[filePath].wordCount = sourceWordCount;
// Process each target locale
for (const _targetLocale of targetLocales) {
const targetLocale = resolveOverriddenLocale(
_targetLocale,
bucketPath.delimiter,
);
bucketOra.start(
`[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`,
);
let targetData = {};
let fileExists = true;
try {
targetData = await bucketLoader.pull(targetLocale);
} catch (error) {
fileExists = false;
bucketOra.info(
`[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`,
);
}
if (!fileExists) {
// All keys are missing for this locale
fileStats[filePath].languageStats[_targetLocale].missing =
sourceKeys.length;
fileStats[filePath].languageStats[_targetLocale].words =
sourceWordCount;
languageStats[_targetLocale].missing += sourceKeys.length;
languageStats[_targetLocale].words += sourceWordCount;
totalWordCount.set(
_targetLocale,
(totalWordCount.get(_targetLocale) || 0) + sourceWordCount,
);
bucketOra.succeed(
`[${sourceLocale} -> ${targetLocale}] ${chalk.red(
`0% complete`,
)} (0/${sourceKeys.length} keys) - file not found`,
);
continue;
}
// Calculate delta for existing file
const deltaProcessor = createDeltaProcessor(
bucketPath.pathPattern,
);
const checksums = await deltaProcessor.loadChecksums();
const delta = await deltaProcessor.calculateDelta({
sourceData,
targetData,
checksums,
});
const missingKeys = delta.added;
const updatedKeys = delta.updated;
const completeKeys = sourceKeys.filter(
(key) =>
!missingKeys.includes(key) && !updatedKeys.includes(key),
);
// Count words that need translation
let wordsToTranslate = 0;
const keysToProcess = flags.force
? sourceKeys
: [...missingKeys, ...updatedKeys];
for (const key of keysToProcess) {
const value = sourceData[String(key)];
if (typeof value === "string") {
const words = value.trim().split(/\s+/).length;
wordsToTranslate += words;
}
}
// Update file stats
fileStats[filePath].languageStats[_targetLocale].missing =
missingKeys.length;
fileStats[filePath].languageStats[_targetLocale].updated =
updatedKeys.length;
fileStats[filePath].languageStats[_targetLocale].complete =
completeKeys.length;
fileStats[filePath].languageStats[_targetLocale].words =
wordsToTranslate;
// Update global stats
languageStats[_targetLocale].missing += missingKeys.length;
languageStats[_targetLocale].updated += updatedKeys.length;
languageStats[_targetLocale].complete += completeKeys.length;
languageStats[_targetLocale].words += wordsToTranslate;
totalWordCount.set(
_targetLocale,
(totalWordCount.get(_targetLocale) || 0) + wordsToTranslate,
);
// Display progress
const totalKeysInFile = sourceKeys.length;
const completionPercent = (
(completeKeys.length / totalKeysInFile) *
100
).toFixed(1);
if (missingKeys.length === 0 && updatedKeys.length === 0) {
bucketOra.succeed(
`[${sourceLocale} -> ${targetLocale}] ${chalk.green(
`100% complete`,
)} (${completeKeys.length}/${totalKeysInFile} keys)`,
);
} else {
const message = `[${sourceLocale} -> ${targetLocale}] ${
parseFloat(completionPercent) > 50
? chalk.yellow(`${completionPercent}% complete`)
: chalk.red(`${completionPercent}% complete`)
} (${completeKeys.length}/${totalKeysInFile} keys)`;
bucketOra.succeed(message);
if (flags.verbose) {
if (missingKeys.length > 0) {
console.log(
` ${chalk.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`,
);
console.log(
` ${chalk.red(`Missing:`)} ${
missingKeys.length
} keys, ~${wordsToTranslate} words`,
);
console.log(
` ${chalk.dim(
`Example missing: ${missingKeys
.slice(0, 2)
.join(", ")}${missingKeys.length > 2 ? "..." : ""}`,
)}`,
);
}
if (updatedKeys.length > 0) {
console.log(
` ${chalk.yellow(`Updated:`)} ${
updatedKeys.length
} keys that changed in source`,
);
}
}
}
}
}
} catch (error: any) {
ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);
}
}
// Calculate unique keys needing translation and keys fully translated
// Count unique keys that need translation
const totalKeysNeedingTranslation = Object.values(languageStats).reduce(
(sum, stats) => {
return sum + stats.missing + stats.updated;
},
0,
);
// Calculate keys that are completely translated
const totalCompletedKeys =
totalSourceKeyCount -
totalKeysNeedingTranslation / targetLocales.length;
// Summary output
console.log();
ora.succeed(chalk.green(`Localization status completed.`));
// Create a visually impactful main header
console.log(chalk.bold.cyan(`\n╔════════════════════════════════════╗`));
console.log(chalk.bold.cyan(`║ LOCALIZATION STATUS REPORT ║`));
console.log(chalk.bold.cyan(`╚════════════════════════════════════╝`));
// Source content overview
console.log(chalk.bold(`\n📝 SOURCE CONTENT:`));
console.log(
`• Source language: ${chalk.green(i18nConfig!.locale.source)}`,
);
console.log(
`• Source keys: ${chalk.yellow(
totalSourceKeyCount.toString(),
)} keys across all files`,
);
// Create a language-by-language breakdown table
console.log(chalk.bold(`\n🌐 LANGUAGE BY LANGUAGE BREAKDOWN:`));
// Create a new table instance with cli-table3
const table = new Table({
head: [
"Language",
"Status",
"Complete",
"Missing",
"Updated",
"Total Keys",
"Words to Translate",
],
style: {
head: ["white"], // White color for headers
border: [], // No color for borders
},
colWidths: [12, 20, 18, 12, 12, 12, 15], // Explicit column widths, making Status column wider
});
// Data rows
let totalWordsToTranslate = 0;
for (const locale of targetLocales) {
const stats = languageStats[locale];
const percentComplete = (
(stats.complete / totalSourceKeyCount) *
100
).toFixed(1);
const totalNeeded = stats.missing + stats.updated;
// Determine status text and color
let statusText;
let statusColor;
if (stats.missing === totalSourceKeyCount) {
statusText = "🔴 Not started";
statusColor = chalk.red;
} else if (stats.missing === 0 && stats.updated === 0) {
statusText = "✅ Complete";
statusColor = chalk.green;
} else if (parseFloat(percentComplete) > 80) {
statusText = "🟡 Almost done";
statusColor = chalk.yellow;
} else if (parseFloat(percentComplete) > 0) {
statusText = "🟠 In progress";
statusColor = chalk.yellow;
} else {
statusText = "🔴 Not started";
statusColor = chalk.red;
}
// Create row data
const words = totalWordCount.get(locale) || 0;
totalWordsToTranslate += words;
// Add row to the table
table.push([
locale,
statusColor(statusText),
`${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,
stats.missing > 0 ? chalk.red(stats.missing.toString()) : "0",
stats.updated > 0 ? chalk.yellow(stats.updated.toString()) : "0",
totalNeeded > 0 ? chalk.magenta(totalNeeded.toString()) : "0",
words > 0 ? `~${words.toLocaleString()}` : "0",
]);
}
// Display the table
console.log(table.toString());
// Total usage summary
console.log(chalk.bold(`\n📊 USAGE ESTIMATE:`));
console.log(
`• WORDS TO BE CONSUMED: ~${chalk.yellow.bold(
totalWordsToTranslate.toLocaleString(),
)} words across all languages`,
);
console.log(
` (Words are counted from source language for keys that need translation in target languages)`,
);
// Breakdown by language if we have multiple languages
if (targetLocales.length > 1) {
console.log(`• Per-language breakdown:`);
for (const locale of targetLocales) {
const words = totalWordCount.get(locale) || 0;
const percent = ((words / totalWordsToTranslate) * 100).toFixed(1);
console.log(
` - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`,
);
}
}
// Detailed stats if flags.confirm is specified
if (flags.confirm && Object.keys(fileStats).length > 0) {
console.log(chalk.bold(`\n📑 BREAKDOWN BY FILE:`));
Object.entries(fileStats)
.sort((a, b) => b[1].wordCount - a[1].wordCount) // Sort by word count
.forEach(([path, stats]) => {
// Skip files with no source keys
if (stats.sourceKeys === 0) return;
console.log(chalk.bold(`\n• ${path}:`));
console.log(
` ${
stats.sourceKeys
} source keys, ~${stats.wordCount.toLocaleString()} source words`,
);
// Create file detail table
const fileTable = new Table({
head: ["Language", "Status", "Details"],
style: {
head: ["white"],
border: [],
},
colWidths: [12, 20, 50], // Explicit column widths for file detail table
});
for (const locale of targetLocales) {
const langStats = stats.languageStats[locale];
const complete = langStats.complete;
const total = stats.sourceKeys;
const completion = ((complete / total) * 100).toFixed(1);
let status = "✅ Complete";
let statusColor = chalk.green;
if (langStats.missing === total) {
status = "❌ Not started";
statusColor = chalk.red;
} else if (langStats.missing > 0 || langStats.updated > 0) {
status = `⚠️ ${completion}% complete`;
statusColor = chalk.yellow;
}
// Show counts only if there's something missing or updated
let details = "";
if (langStats.missing > 0 || langStats.updated > 0) {
const parts = [];
if (langStats.missing > 0)
parts.push(`${langStats.missing} missing`);
if (langStats.updated > 0)
parts.push(`${langStats.updated} changed`);
details = `${parts.join(", ")}, ~${langStats.words} words`;
} else {
details = "All keys translated";
}
fileTable.push([locale, statusColor(status), details]);
}
console.log(fileTable.toString());
});
}
// Find fully translated and missing languages
const completeLanguages = targetLocales.filter(
(locale) =>
languageStats[locale].missing === 0 &&
languageStats[locale].updated === 0,
);
const missingLanguages = targetLocales.filter(
(locale) => languageStats[locale].complete === 0,
);
// Add optimization tips
console.log(chalk.bold.green(`\n💡 OPTIMIZATION TIPS:`));
if (missingLanguages.length > 0) {
console.log(
`• ${chalk.yellow(missingLanguages.join(", "))} ${
missingLanguages.length === 1 ? "has" : "have"
} no translations yet`,
);
}
if (completeLanguages.length > 0) {
console.log(
`• ${chalk.green(completeLanguages.join(", "))} ${
completeLanguages.length === 1 ? "is" : "are"
} completely translated`,
);
}
// Other tips
if (targetLocales.length > 1) {
console.log(`• Translating one language at a time reduces complexity`);
console.log(
`• Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`,
);
}
// Track successful completion
trackEvent(authId || "status", "cmd.status.success", {
i18nConfig,
flags,
totalSourceKeyCount,
languageStats,
totalWordsToTranslate,
authenticated: !!authId,
});
exitGracefully();
} catch (error: any) {
ora.fail(error.message);
trackEvent(authId || "status", "cmd.status.error", {
flags,
error: error.message,
authenticated: !!authId,
});
process.exit(1);
}
});
function parseFlags(options: any) {
return Z.object({
locale: Z.array(localeCodeSchema).optional(),
bucket: Z.array(bucketTypeSchema).optional(),
force: Z.boolean().optional(),
confirm: Z.boolean().optional(),
verbose: Z.boolean().optional(),
file: Z.array(Z.string()).optional(),
apiKey: Z.string().optional(),
}).parse(options);
}
async function tryAuthenticate(settings: ReturnType<typeof getSettings>) {
if (!settings.auth.apiKey) {
return null;
}
try {
const authenticator = createAuthenticator({
apiKey: settings.auth.apiKey,
apiUrl: settings.auth.apiUrl,
});
const user = await authenticator.whoami();
return user;
} catch (error) {
return null;
}
}
function validateParams(
i18nConfig: I18nConfig | null,
flags: ReturnType<typeof parseFlags>,
) {
if (!i18nConfig) {
throw new CLIError({
message:
"i18n.json not found. Please run `lingo.dev init` to initialize the project.",
docUrl: "i18nNotFound",
});
} else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {
throw new CLIError({
message:
"No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
docUrl: "bucketNotFound",
});
} else if (
flags.locale?.some((locale) => !i18nConfig.locale.targets.includes(locale))
) {
throw new CLIError({
message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
docUrl: "localeTargetNotFound",
});
} else if (
flags.bucket?.some(
(bucket) =>
!i18nConfig.buckets[bucket as keyof typeof i18nConfig.buckets],
)
) {
throw new CLIError({
message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
docUrl: "bucketNotFound",
});
}
}
```