#
tokens: 44335/50000 8/626 files (page 11/16)
lines: off (toggle) GitHub
raw markdown copy
This is page 11 of 16. Use http://codebase.md/lingodotdev/lingo.dev?lines=false&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

--------------------------------------------------------------------------------
/packages/compiler/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# @lingo.dev/\_compiler

## 0.7.15

### Patch Changes

- [#1231](https://github.com/lingodotdev/lingo.dev/pull/1231) [`44a928b`](https://github.com/lingodotdev/lingo.dev/commit/44a928b473802cd07bec64f94a273ee1b845a0d0) Thanks [@davidturnbull](https://github.com/davidturnbull)! - Compiler now throws errors instead of abruptly exiting the process, allowing parent applications to handle errors gracefully

## 0.7.14

### Patch Changes

- Updated dependencies [[`b45347c`](https://github.com/lingodotdev/lingo.dev/commit/b45347c38572ee371b2bc494261b7e3e90c4aed1)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.13

### Patch Changes

- [#1222](https://github.com/lingodotdev/lingo.dev/pull/1222) [`38139c8`](https://github.com/lingodotdev/lingo.dev/commit/38139c81a85001739cece60873c0c6ad711327a4) Thanks [@vrcprl](https://github.com/vrcprl)! - fix regex replacement

## 0.7.12

### 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]
  - @lingo.dev/[email protected]

## 0.7.11

### Patch Changes

- Updated dependencies [[`1fa218c`](https://github.com/lingodotdev/lingo.dev/commit/1fa218c13bf90df6d175fb18264f59c1a10b967c)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.10

### Patch Changes

- Updated dependencies [[`bbc71b9`](https://github.com/lingodotdev/lingo.dev/commit/bbc71b9948ccc289c9669d8b0c276c9596f6a5e7)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.9

### Patch Changes

- Updated dependencies [[`6579d70`](https://github.com/lingodotdev/lingo.dev/commit/6579d70bc670c2fdc06c09842d931b07e134151c)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.8

### Patch Changes

- Updated dependencies [[`a35032e`](https://github.com/lingodotdev/lingo.dev/commit/a35032e7e7a188d1f5e774576352068124526e24)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.7

### Patch Changes

- [#1130](https://github.com/lingodotdev/lingo.dev/pull/1130) [`bc7b08e`](https://github.com/lingodotdev/lingo.dev/commit/bc7b08ef1245d1af0c68813cb18193d4f14bc7e0) Thanks [@mathio](https://github.com/mathio)! - dictionary path calculation

## 0.7.6

### Patch Changes

- [#1121](https://github.com/lingodotdev/lingo.dev/pull/1121) [`b6071e4`](https://github.com/lingodotdev/lingo.dev/commit/b6071e4f19dd1823f4f2ce54ba5495538a94d4fd) Thanks [@mathio](https://github.com/mathio)! - compiler: prevent duplicate props

## 0.7.5

### Patch Changes

- [#1118](https://github.com/lingodotdev/lingo.dev/pull/1118) [`410825c`](https://github.com/lingodotdev/lingo.dev/commit/410825c8bf0029d8ee458514d6f203a7397c8f22) Thanks [@mathio](https://github.com/mathio)! - support Turbopack in Next.js v14 by Compiler

- [#1116](https://github.com/lingodotdev/lingo.dev/pull/1116) [`bc419ae`](https://github.com/lingodotdev/lingo.dev/commit/bc419aeeb4211d80d3c0ddd65deeab62ad68fea8) Thanks [@devin-ai-integration](https://github.com/apps/devin-ai-integration)! - fix: move vitest from dependencies to devDependencies

## 0.7.4

### Patch Changes

- [#1072](https://github.com/lingodotdev/lingo.dev/pull/1072) [`3cb1ebe`](https://github.com/lingodotdev/lingo.dev/commit/3cb1ebec5441882678ab30a7d1b532bc2fc397b6) Thanks [@The-Best-Codes](https://github.com/The-Best-Codes)! - Fixed compiler handling of namespace imports (import \* as React from "react") and default imports.

## 0.7.3

### Patch Changes

- Updated dependencies [[`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e), [`6af91a0`](https://github.com/lingodotdev/lingo.dev/commit/6af91a083d16f85051fb49a4034789abe784017e)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.7.2

### Patch Changes

- Updated dependencies [[`85dfc10`](https://github.com/lingodotdev/lingo.dev/commit/85dfc10961b116e31b2bb478f42013756ca49974)]:
  - @lingo.dev/[email protected]

## 0.7.1

### Patch Changes

- [#1040](https://github.com/lingodotdev/lingo.dev/pull/1040) [`f897a7d`](https://github.com/lingodotdev/lingo.dev/commit/f897a7d0a3f7a236fb64f19bce9a8d00626d09ca) Thanks [@The-Best-Codes](https://github.com/The-Best-Codes)! - Fixed the compiler to handle type-only react imports.

## 0.7.0

### Minor Changes

- [#997](https://github.com/lingodotdev/lingo.dev/pull/997) [`bd9538a`](https://github.com/lingodotdev/lingo.dev/commit/bd9538ac6eba0ffc91ffc1fef5db6366c13e9e06) Thanks [@VAIBHAVSING](https://github.com/VAIBHAVSING)! - ### Whitespace Normalization Fix

  - Improved `normalizeJsxWhitespace` logic to preserve leading spaces inside JSX elements while removing unnecessary formatting whitespace and extra lines.
  - Ensured explicit whitespace (e.g., `{" "}`) is handled correctly without introducing double spaces.
  - Added targeted tests (`jsx-content-whitespace.spec.ts`) to verify whitespace handling.
  - Cleaned up unnecessary debug/test files created during development.

## 0.6.3

### Patch Changes

- Updated dependencies [[`afbb978`](https://github.com/lingodotdev/lingo.dev/commit/afbb978fec83d574f2c43b7d68457e435fca9b57)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.6.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

- Updated dependencies [[`9266fd0`](https://github.com/lingodotdev/lingo.dev/commit/9266fd0bcddf4b07ca51d2609af92a9473106f9d)]:
  - @lingo.dev/[email protected]
  - @lingo.dev/[email protected]

## 0.6.1

### Patch Changes

- [#1021](https://github.com/lingodotdev/lingo.dev/pull/1021) [`6baa1a7`](https://github.com/lingodotdev/lingo.dev/commit/6baa1a7e88dbfac3783d1d49695595077fd8d209) Thanks [@mathio](https://github.com/mathio)! - add lingo.dev provider details

## 0.6.0

### Minor Changes

- [#1010](https://github.com/lingodotdev/lingo.dev/pull/1010) [`864c305`](https://github.com/lingodotdev/lingo.dev/commit/864c30586510e6b69739c20fa42efdf45d8881ed) Thanks [@davidturnbull](https://github.com/davidturnbull)! - improve type safety of compiler params

### Patch Changes

- Updated dependencies [[`cb2aa0f`](https://github.com/lingodotdev/lingo.dev/commit/cb2aa0f505d6b7dbc435b526e8a6f62265d1f453)]:
  - @lingo.dev/[email protected]

## 0.5.5

### Patch Changes

- [#1011](https://github.com/lingodotdev/lingo.dev/pull/1011) [`bfcb424`](https://github.com/lingodotdev/lingo.dev/commit/bfcb424eb4479d0d3b767e062d30f02c5bcaeb14) Thanks [@mathio](https://github.com/mathio)! - replace elements with dot in name

## 0.5.4

### Patch Changes

- [#1002](https://github.com/lingodotdev/lingo.dev/pull/1002) [`2b297ba`](https://github.com/lingodotdev/lingo.dev/commit/2b297babe76f9799c5154d9421fecd1ebbe1bb72) Thanks [@mathio](https://github.com/mathio)! - support custom prompts in compiler

## 0.5.3

### Patch Changes

- Updated dependencies []:
  - @lingo.dev/[email protected]

## 0.5.2

### Patch Changes

- Updated dependencies []:
  - @lingo.dev/[email protected]

## 0.5.1

### Patch Changes

- [#972](https://github.com/lingodotdev/lingo.dev/pull/972) [`b249484`](https://github.com/lingodotdev/lingo.dev/commit/b249484d6f0060e29cd5b50b3d8ce68b857ccad5) Thanks [@mathio](https://github.com/mathio)! - support components with dot in name

## 0.5.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>`

### Patch Changes

- Updated dependencies []:
  - @lingo.dev/[email protected]

## 0.4.1

### Patch Changes

- Updated dependencies []:
  - @lingo.dev/[email protected]

## 0.4.0

### Minor Changes

- [#932](https://github.com/lingodotdev/lingo.dev/pull/932) [`1bba8ee`](https://github.com/lingodotdev/lingo.dev/commit/1bba8eed6272ae166ceb9b92963404bfe90a4aaa) Thanks [@The-Best-Codes](https://github.com/The-Best-Codes)! - Add support for Next.js Turbopack with the Lingo.dev compiler.

## 0.3.5

### Patch Changes

- [#947](https://github.com/lingodotdev/lingo.dev/pull/947) [`d80285a`](https://github.com/lingodotdev/lingo.dev/commit/d80285a9b12bd85425564cb00e558812fd0aee40) Thanks [@mathio](https://github.com/mathio)! - remove local variable cache

## 0.3.4

### 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

- Updated dependencies [[`4e5983d`](https://github.com/lingodotdev/lingo.dev/commit/4e5983d7e59ebf9eb529c4b7c1c87689432ac873)]:
  - @lingo.dev/[email protected]

## 0.3.3

### Patch Changes

- [`76cbd9b`](https://github.com/lingodotdev/lingo.dev/commit/76cbd9b2f2e1217421ad1f671bed5b3d64b43333) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - dictionary merging

## 0.3.2

### Patch Changes

- [`01f253d`](https://github.com/lingodotdev/lingo.dev/commit/01f253dd9759b518f400dff03ab51b460b9b8997) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix dictionary merging

## 0.3.1

### Patch Changes

- [`8e97256`](https://github.com/lingodotdev/lingo.dev/commit/8e97256ca4e78dd09a967539ca9dec359bd558ef) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix dictionary merging

## 0.3.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.

- [#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

### 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 []:
  - @lingo.dev/[email protected]

## 0.2.4

### Patch Changes

- [#919](https://github.com/lingodotdev/lingo.dev/pull/919) [`3b6574f`](https://github.com/lingodotdev/lingo.dev/commit/3b6574f0499f3f4d3c48f66ba2b828d2c1c0ceb0) Thanks [@mathio](https://github.com/mathio)! - update package import names

## 0.2.3

### Patch Changes

- [#911](https://github.com/lingodotdev/lingo.dev/pull/911) [`d7e74c6`](https://github.com/lingodotdev/lingo.dev/commit/d7e74c6cc724da8ae759ba8d8fdb1a64867d505c) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - fix hyphens in locale names

## 0.2.2

### Patch Changes

- [#905](https://github.com/lingodotdev/lingo.dev/pull/905) [`1a235a1`](https://github.com/lingodotdev/lingo.dev/commit/1a235a17455fb2631f7426283aa8431209999758) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - remove @/ path mapping in compiler

## 0.2.1

### Patch Changes

- [#900](https://github.com/lingodotdev/lingo.dev/pull/900) [`fead8e0`](https://github.com/lingodotdev/lingo.dev/commit/fead8e08dc2b2869a093cb25a04f6e0aa78cf6b7) Thanks [@mathio](https://github.com/mathio)! - load API key from env var and env files

## 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.

## 0.1.13

### Patch Changes

- [#890](https://github.com/lingodotdev/lingo.dev/pull/890) [`145fb74`](https://github.com/lingodotdev/lingo.dev/commit/145fb74c09b42c8810f351be5a641b1366881ae1) Thanks [@mathio](https://github.com/mathio)! - do not parse LingoProvider component

- [#889](https://github.com/lingodotdev/lingo.dev/pull/889) [`0c45acc`](https://github.com/lingodotdev/lingo.dev/commit/0c45accfc45e63f597758c47033bc58d2f6059b5) Thanks [@mathio](https://github.com/mathio)! - update Groq API error handling

## 0.1.12

### Patch Changes

- [#887](https://github.com/lingodotdev/lingo.dev/pull/887) [`511a2ec`](https://github.com/lingodotdev/lingo.dev/commit/511a2ecd68a9c5e2800035d5c6a6b5b31b2dc80f) Thanks [@mathio](https://github.com/mathio)! - handle when lingo dir is deleted

## 0.1.11

### Patch Changes

- [#883](https://github.com/lingodotdev/lingo.dev/pull/883) [`7191444`](https://github.com/lingodotdev/lingo.dev/commit/7191444f67864ea5b5a91a9be759b2445bf186d3) Thanks [@mathio](https://github.com/mathio)! - client-side loading state

## 0.1.10

### Patch Changes

- [#876](https://github.com/lingodotdev/lingo.dev/pull/876) [`152e96a`](https://github.com/lingodotdev/lingo.dev/commit/152e96a46b98dd25d558ff0e7e20b18b954d375a) Thanks [@vrcprl](https://github.com/vrcprl)! - fix for triggering reload on Windows

## 0.1.9

### Patch Changes

- [#866](https://github.com/lingodotdev/lingo.dev/pull/866) [`77461a7`](https://github.com/lingodotdev/lingo.dev/commit/77461a7872eec3ea188b3ca6c6f7ce1fd13fdfbb) Thanks [@vrcprl](https://github.com/vrcprl)! - normalize paths in dictionaries

## 0.1.8

### Patch Changes

- [#861](https://github.com/lingodotdev/lingo.dev/pull/861) [`1bccb7e`](https://github.com/lingodotdev/lingo.dev/commit/1bccb7ed51ac1f13ea79e618bbee551d5529efdc) Thanks [@vrcprl](https://github.com/vrcprl)! - support filePath on Windows

## 0.1.7

### Patch Changes

- [`5b68641`](https://github.com/lingodotdev/lingo.dev/commit/5b686414f363f8ee4b79fd4e804a434db5cfcb36) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - feat: unshift the plugins

## 0.1.6

### Patch Changes

- [`7a5898b`](https://github.com/lingodotdev/lingo.dev/commit/7a5898b12dcd0015a5e57236bf65172cedb8a6ee) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - merge dictionaries

## 0.1.5

### Patch Changes

- [`7013b53`](https://github.com/lingodotdev/lingo.dev/commit/7013b5300d6c2c26f39da62b5ad2c7cf11158c74) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - value.trim() issue

## 0.1.4

### Patch Changes

- [#853](https://github.com/lingodotdev/lingo.dev/pull/853) [`cb7d5e2`](https://github.com/lingodotdev/lingo.dev/commit/cb7d5e213282c00af658159472183a763f84ca3d) Thanks [@vrcprl](https://github.com/vrcprl)! - Fix groq api key retrieval from .env

## 0.1.3

### Patch Changes

- [`f42cff8`](https://github.com/lingodotdev/lingo.dev/commit/f42cff8355b1ff7bba1445bd04d11ee4672903c2) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - flat reexports

## 0.1.2

### Patch Changes

- [`920e3f5`](https://github.com/lingodotdev/lingo.dev/commit/920e3f5c3ca1fd51b0919db13a4787cfd616de54) Thanks [@mathio](https://github.com/mathio)! - remove cloneDeep for optimization

## 0.1.1

### Patch Changes

- [`caef325`](https://github.com/lingodotdev/lingo.dev/commit/caef3253bc99fa7bf7a0b40e5604c3590dcb4958) Thanks [@mathio](https://github.com/mathio)! - release fix

## 0.1.0

### Minor Changes

- [`e980e84`](https://github.com/lingodotdev/lingo.dev/commit/e980e84178439ad70417d38b425acf9148cfc4b6) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - added the compiler

```

--------------------------------------------------------------------------------
/packages/react/src/core/component.spec.tsx:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from "vitest";
import { render } from "@testing-library/react";
import { LingoComponent } from "./component";

describe("LingoComponent", () => {
  const dictionary = {
    files: {
      messages: {
        entries: {
          greeting: "Hello {user.profile.name} you have {count} messages",
          welcome:
            "Welcome <element:a>incredible <element:span>fantastic <element:em>wonderful <element:strong>amazing</element:strong></element:em></element:span> user</element:a> <element:Icons.Rocket></element:Icons.Rocket>",
          complex:
            "<element:a>Hello {user.profile.name}, welcome to <element:span>wonderful <element:strong><element:em>{placeholder}</element:em> nested</element:strong></element:span> world</element:a> of the <element:u>universe number {count}</element:u>",
        },
      },
    },
  };

  it("replaces variables in text", () => {
    const { container } = render(
      <LingoComponent
        $dictionary={dictionary}
        $as="div"
        $fileKey="messages"
        $entryKey="greeting"
        $variables={{ "user.profile.name": "John", count: 69 }}
      />,
    );
    expect(container.textContent).toBe("Hello John you have 69 messages");
  });

  it("replaces variables with JSX", () => {
    const { container } = render(
      <LingoComponent
        $dictionary={dictionary}
        $as="div"
        $fileKey="messages"
        $entryKey="greeting"
        $variables={{
          "user.profile.name": <strong>John</strong>,
          count: <em>69</em>,
        }}
      />,
    );
    expect(container.innerHTML).toBe(
      "<div>Hello <strong>John</strong> you have <em>69</em> messages</div>",
    );
  });

  it("replaces element placeholders", () => {
    const Icons = {
      Rocket: () => <span>🚀</span>,
    };

    const { container } = render(
      <LingoComponent
        $dictionary={dictionary}
        $as="div"
        $fileKey="messages"
        $entryKey="welcome"
        $elements={[
          ({ children }: any) => <a href="#">{children}</a>,
          ({ children }: any) => <span>{children}</span>,
          ({ children }: any) => <em>{children}</em>,
          ({ children }: any) => <strong className="red">{children}</strong>,
          ({ children }: any) => <Icons.Rocket />,
        ]}
      />,
    );
    expect(container.innerHTML).toBe(
      '<div>Welcome <a href="#">incredible <span>fantastic <em>wonderful <strong class="red">amazing</strong></em></span> user</a> <span>🚀</span></div>',
    );
  });

  it("handles both variables and elements", () => {
    const { container } = render(
      <LingoComponent
        $dictionary={dictionary}
        $as="div"
        $fileKey="messages"
        $entryKey="complex"
        $variables={{
          "user.profile.name": "John",
          count: 42,
          placeholder: "very",
        }}
        $elements={[
          ({ children }: any) => <a>{children}</a>,
          ({ children }: any) => <span>{children}</span>,
          ({ children }: any) => <strong>{children}</strong>,
          ({ children }: any) => <em>{children}</em>,
          ({ children }: any) => <u>{children}</u>,
        ]}
      />,
    );
    expect(container.innerHTML).toBe(
      "<div><a>Hello John, welcome to <span>wonderful <strong><em>very</em> nested</strong></span> world</a> of the <u>universe number 42</u></div>",
    );
  });

  it("falls back to entryKey if value not found", () => {
    const { container } = render(
      <LingoComponent
        $dictionary={dictionary}
        $as="div"
        $fileKey="messages"
        $entryKey="nonexistent"
        $variables={{}}
        $elements={[]}
      />,
    );
    expect(container.textContent).toBe("nonexistent");
  });

  describe("function replacement", () => {
    const getName = () => "John";
    const getCount = () => 42;
    const formatName = () => "John Doe";
    const getUnread = () => 3;
    const fnDictionary = {
      files: {
        messages: {
          entries: {
            simple:
              "Hello <function:getName/>, you have <function:getCount/> items",
            chained: "Hello <function:user.details.profile.name/>",
            mixed:
              "Welcome <function:formatName/>, you have {count} items and <function:getUnread/> unread",
            nested:
              "<element:strong>User <function:getName/></element:strong> has <element:em><function:getCount/></element:em>",
          },
        },
      },
    };

    it("replaces function calls in text", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="simple"
          $functions={{
            getName: [getName()],
            getCount: [getCount()],
          }}
        />,
      );
      expect(container.textContent).toBe("Hello John, you have 42 items");
    });

    it("handles mixed variables and functions", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="mixed"
          $variables={{
            count: 5,
          }}
          $functions={{
            formatName: [formatName()],
            getUnread: [getUnread()],
          }}
        />,
      );
      expect(container.textContent).toBe(
        "Welcome John Doe, you have 5 items and 3 unread",
      );
    });

    it("handles functions with nested elements", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="nested"
          $functions={{
            getName: [getName()],
            getCount: [getCount()],
          }}
          $elements={[
            ({ children }: any) => <strong>{children}</strong>,
            ({ children }: any) => <em>{children}</em>,
          ]}
        />,
      );
      expect(container.innerHTML).toBe(
        "<div><strong>User John</strong> has <em>42</em></div>",
      );
    });

    it("handles function with chained names", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="chained"
          $functions={{
            "user.details.profile.name": [getName()],
          }}
        />,
      );
      expect(container.textContent).toBe("Hello John");
    });

    it("preserves function placeholder if function not provided", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="simple"
          $functions={{
            getName: [getName()],
            // fn1:getCount not provided
          }}
        />,
      );
      expect(container.textContent).toBe(
        "Hello John, you have <function:getCount/> items",
      );
    });

    it("replaces function calls with JSX", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={fnDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="simple"
          $functions={{
            getName: [<strong>John</strong>],
            getCount: [<em>42</em>],
          }}
        />,
      );
      expect(container.innerHTML).toBe(
        "<div>Hello <strong>John</strong>, you have <em>42</em> items</div>",
      );
    });
  });

  describe("expression replacement", () => {
    const exprDictionary = {
      files: {
        messages: {
          entries: {
            simple: "Result: <expression/>",
            multiple: "First: <expression/>, Second: <expression/>",
            mixed:
              "Count: <expression/>, User: {user.name}, Items: <expression/>",
            nested:
              "<element:strong>Value: <expression/></element:strong> and <element:em>Total: <expression/></element:em>",
          },
        },
      },
    };

    it("replaces simple expressions", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="simple"
          $expressions={[42]}
        />,
      );
      expect(container.textContent).toBe("Result: 42");
    });

    it("handles multiple expressions", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="multiple"
          $expressions={[42 * 2, "hello".toUpperCase()]}
        />,
      );
      expect(container.textContent).toBe("First: 84, Second: HELLO");
    });

    it("handles mixed variables and expressions", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="mixed"
          $variables={{
            "user.name": "John",
          }}
          $expressions={[42 + 1, [1, 2, 3].length]}
        />,
      );
      expect(container.textContent).toBe("Count: 43, User: John, Items: 3");
    });

    it("handles expressions with nested elements", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="nested"
          $expressions={[42 * 2, [1, 2, 3].reduce((a, b) => a + b, 0)]}
          $elements={[
            ({ children }: any) => <strong>{children}</strong>,
            ({ children }: any) => <em>{children}</em>,
          ]}
        />,
      );
      expect(container.innerHTML).toBe(
        "<div><strong>Value: 84</strong> and <em>Total: 6</em></div>",
      );
    });

    it("preserves expression placeholder if not provided", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="multiple"
          $expressions={[
            42,
            // second expression not provided
          ]}
        />,
      );
      expect(container.textContent).toBe("First: 42, Second: <expression/>");
    });

    it("replaces expressions with JSX", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={exprDictionary}
          $as="div"
          $fileKey="messages"
          $entryKey="multiple"
          $expressions={[<strong>foo</strong>, <code>bar</code>]}
        />,
      );
      expect(container.innerHTML).toBe(
        "<div>First: <strong>foo</strong>, Second: <code>bar</code></div>",
      );
    });
  });

  describe("array mutation prevention (shift() bug fix)", () => {
    const mutationDictionary = {
      files: {
        test: {
          entries: {
            elements:
              "First <element:0>text</element:0> and <element:1>more</element:1>",
            functions: "Call <function:fn1/> then <function:fn2/>",
            expressions: "Value <expression/> and <expression/>",
            mixed:
              "Element <element:0>content</element:0> with <function:fn1/> and <expression/>",
          },
        },
      },
    };

    it("does not mutate elements array during processing", () => {
      const elements = [
        ({ children }: any) => <span>{children}</span>,
        ({ children }: any) => <strong>{children}</strong>,
      ];
      const originalElements = [...elements];

      render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="elements"
          $elements={elements}
        />,
      );

      expect(elements).toEqual(originalElements);
      expect(elements.length).toBe(2);
    });

    it("does not mutate functions arrays during processing", () => {
      const functions = {
        fn1: ["result1", "result2"],
        fn2: ["result3", "result4"],
      };
      const originalFunctions = {
        fn1: [...functions.fn1],
        fn2: [...functions.fn2],
      };

      render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="functions"
          $functions={functions}
        />,
      );

      expect(functions.fn1).toEqual(originalFunctions.fn1);
      expect(functions.fn2).toEqual(originalFunctions.fn2);
      expect(functions.fn1.length).toBe(2);
      expect(functions.fn2.length).toBe(2);
    });

    it("does not mutate expressions array during processing", () => {
      const expressions = ["value1", "value2"];
      const originalExpressions = [...expressions];

      render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="expressions"
          $expressions={expressions}
        />,
      );

      expect(expressions).toEqual(originalExpressions);
      expect(expressions.length).toBe(2);
    });

    it("produces consistent output across multiple renders", () => {
      const elements = [
        ({ children }: any) => <span>{children}</span>,
        ({ children }: any) => <strong>{children}</strong>,
      ];
      const functions = { fn1: ["result1"] };
      const expressions = ["value1"];

      const { container: container1 } = render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="mixed"
          $elements={elements}
          $functions={functions}
          $expressions={expressions}
        />,
      );

      const { container: container2 } = render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="mixed"
          $elements={elements}
          $functions={functions}
          $expressions={expressions}
        />,
      );

      expect(container1.innerHTML).toBe(container2.innerHTML);
    });

    it("handles shared arrays across multiple component instances", () => {
      const sharedElements = [
        ({ children }: any) => <span>{children}</span>,
        ({ children }: any) => <strong>{children}</strong>,
      ];

      const { container: container1 } = render(
        <div>
          <LingoComponent
            $dictionary={mutationDictionary}
            $as="div"
            $fileKey="test"
            $entryKey="elements"
            $elements={sharedElements}
          />
        </div>,
      );

      const { container: container2 } = render(
        <div>
          <LingoComponent
            $dictionary={mutationDictionary}
            $as="div"
            $fileKey="test"
            $entryKey="elements"
            $elements={sharedElements}
          />
        </div>,
      );

      expect(container1.innerHTML).toBe(container2.innerHTML);
      expect(sharedElements.length).toBe(2);
    });

    it("extracts inner content when elements array is exhausted", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="elements"
          $elements={[({ children }: any) => <span>{children}</span>]}
        />,
      );

      expect(container.textContent).toBe("First text and more");
      expect(container.innerHTML).toBe(
        "<div>First <span>text</span> and more</div>",
      );
    });

    it("handles completely empty elements array gracefully", () => {
      const { container } = render(
        <LingoComponent
          $dictionary={mutationDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="elements"
          $elements={[]}
        />,
      );

      expect(container.textContent).toBe("First text and more");
      expect(container.innerHTML).toBe("<div>First text and more</div>");
    });

    it("maintains function index tracking per function name", () => {
      const multiCallDictionary = {
        files: {
          test: {
            entries: {
              multiCall:
                "First <function:fn1/>, second <function:fn1/>, third <function:fn2/>",
            },
          },
        },
      };

      const { container } = render(
        <LingoComponent
          $dictionary={multiCallDictionary}
          $as="div"
          $fileKey="test"
          $entryKey="multiCall"
          $functions={{
            fn1: ["A", "B"],
            fn2: ["C"],
          }}
        />,
      );

      expect(container.textContent).toBe("First A, second B, third C");
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/xliff.ts:
--------------------------------------------------------------------------------

```typescript
import { ILoader } from "./_types";
import { createLoader } from "./_utils";
import { JSDOM } from "jsdom";

/**
 * Creates a comprehensive XLIFF loader supporting versions 1.2 and 2.0
 * with deterministic key generation and structure preservation
 */
export default function createXliffLoader(): ILoader<
  string,
  Record<string, string>
> {
  return createLoader({
    async pull(locale, input, _ctx, originalLocale) {
      const trimmedInput = (input ?? "").trim();

      if (!trimmedInput) {
        return createEmptyResult(originalLocale, locale);
      }

      try {
        const dom = new JSDOM(trimmedInput, { contentType: "text/xml" });
        const document = dom.window.document;

        // Check for parsing errors
        const parserError = document.querySelector("parsererror");
        if (parserError) {
          throw new Error(`XML parsing failed: ${parserError.textContent}`);
        }

        const xliffElement = document.documentElement;
        if (!xliffElement || xliffElement.tagName !== "xliff") {
          throw new Error("Invalid XLIFF: missing root <xliff> element");
        }

        const version = xliffElement.getAttribute("version") || "1.2";
        const isV2 = version === "2.0";

        if (isV2) {
          return pullV2(xliffElement, locale, originalLocale);
        } else {
          return pullV1(xliffElement, locale, originalLocale);
        }
      } catch (error: any) {
        throw new Error(`Failed to parse XLIFF file: ${error.message}`);
      }
    },

    async push(locale, translations, originalInput, originalLocale, pullInput) {
      if (!originalInput) {
        // Create new file from scratch
        return pushNewFile(locale, translations, originalLocale);
      }

      try {
        const dom = new JSDOM(originalInput, { contentType: "text/xml" });
        const document = dom.window.document;
        const xliffElement = document.documentElement;
        const version = xliffElement.getAttribute("version") || "1.2";
        const isV2 = version === "2.0";

        if (isV2) {
          return pushV2(
            dom,
            xliffElement,
            locale,
            translations,
            originalLocale,
            originalInput,
          );
        } else {
          return pushV1(
            dom,
            xliffElement,
            locale,
            translations,
            originalLocale,
            originalInput,
          );
        }
      } catch (error: any) {
        throw new Error(`Failed to update XLIFF file: ${error.message}`);
      }
    },
  });
}

/* -------------------------------------------------------------------------- */
/*                            Version 1.2 Support                            */
/* -------------------------------------------------------------------------- */

function pullV1(
  xliffElement: Element,
  locale: string,
  originalLocale: string,
): Record<string, string> {
  const result: Record<string, string> = {};
  const fileElement = xliffElement.querySelector("file");

  if (!fileElement) {
    return result;
  }

  const sourceLanguage =
    fileElement.getAttribute("source-language") || originalLocale;
  const isSourceLocale = sourceLanguage === locale;
  const bodyElement = fileElement.querySelector("body");

  if (!bodyElement) {
    return result;
  }

  const transUnits = bodyElement.querySelectorAll("trans-unit");
  const seenKeys = new Set<string>();

  transUnits.forEach((unit) => {
    let key = getTransUnitKey(unit as Element);
    if (!key) return;

    // Handle duplicates deterministically
    if (seenKeys.has(key)) {
      const id = (unit as Element).getAttribute("id")?.trim();
      if (id) {
        key = `${key}#${id}`;
      } else {
        let counter = 1;
        let newKey = `${key}__${counter}`;
        while (seenKeys.has(newKey)) {
          counter++;
          newKey = `${key}__${counter}`;
        }
        key = newKey;
      }
    }
    seenKeys.add(key);

    const elementName = isSourceLocale ? "source" : "target";
    const textElement = (unit as Element).querySelector(elementName);

    if (textElement) {
      result[key] = extractTextContent(textElement);
    } else if (isSourceLocale) {
      result[key] = key; // fallback for source
    } else {
      result[key] = ""; // empty for missing target
    }
  });

  return result;
}

function pushV1(
  dom: JSDOM,
  xliffElement: Element,
  locale: string,
  translations: Record<string, string>,
  originalLocale: string,
  originalInput?: string,
): string {
  const document = dom.window.document;
  const fileElement = xliffElement.querySelector("file");

  if (!fileElement) {
    throw new Error("Invalid XLIFF 1.2: missing <file> element");
  }

  // Update language attributes
  const sourceLanguage =
    fileElement.getAttribute("source-language") || originalLocale;
  const isSourceLocale = sourceLanguage === locale;

  if (!isSourceLocale) {
    fileElement.setAttribute("target-language", locale);
  }

  let bodyElement = fileElement.querySelector("body");
  if (!bodyElement) {
    bodyElement = document.createElement("body");
    fileElement.appendChild(bodyElement);
  }

  // Build current index
  const existingUnits = new Map<string, Element>();
  const seenKeys = new Set<string>();

  bodyElement.querySelectorAll("trans-unit").forEach((unit) => {
    let key = getTransUnitKey(unit as Element);
    if (!key) return;

    if (seenKeys.has(key)) {
      const id = (unit as Element).getAttribute("id")?.trim();
      if (id) {
        key = `${key}#${id}`;
      } else {
        let counter = 1;
        let newKey = `${key}__${counter}`;
        while (seenKeys.has(newKey)) {
          counter++;
          newKey = `${key}__${counter}`;
        }
        key = newKey;
      }
    }
    seenKeys.add(key);
    existingUnits.set(key, unit as Element);
  });

  // Update/create translation units
  Object.entries(translations).forEach(([key, value]) => {
    let unit = existingUnits.get(key);

    if (!unit) {
      unit = document.createElement("trans-unit");
      unit.setAttribute("id", key);
      unit.setAttribute("resname", key);
      unit.setAttribute("restype", "string");
      unit.setAttribute("datatype", "plaintext");

      const sourceElement = document.createElement("source");
      setTextContent(sourceElement, isSourceLocale ? value : key);
      unit.appendChild(sourceElement);

      if (!isSourceLocale) {
        const targetElement = document.createElement("target");
        targetElement.setAttribute("state", value ? "translated" : "new");
        setTextContent(targetElement, value);
        unit.appendChild(targetElement);
      }

      bodyElement.appendChild(unit);
      existingUnits.set(key, unit);
    } else {
      updateTransUnitV1(unit, key, value, isSourceLocale);
    }
  });

  // Remove orphaned units
  const translationKeys = new Set(Object.keys(translations));
  existingUnits.forEach((unit, key) => {
    if (!translationKeys.has(key)) {
      unit.parentNode?.removeChild(unit);
    }
  });

  return serializeWithDeclaration(
    dom,
    extractXmlDeclaration(originalInput || ""),
  );
}

function updateTransUnitV1(
  unit: Element,
  key: string,
  value: string,
  isSourceLocale: boolean,
): void {
  const document = unit.ownerDocument!;

  if (isSourceLocale) {
    let sourceElement = unit.querySelector("source");
    if (!sourceElement) {
      sourceElement = document.createElement("source");
      unit.appendChild(sourceElement);
    }
    setTextContent(sourceElement, value);
  } else {
    let targetElement = unit.querySelector("target");
    if (!targetElement) {
      targetElement = document.createElement("target");
      unit.appendChild(targetElement);
    }

    setTextContent(targetElement, value);
    targetElement.setAttribute("state", value.trim() ? "translated" : "new");
  }
}

/* -------------------------------------------------------------------------- */
/*                            Version 2.0 Support                            */
/* -------------------------------------------------------------------------- */

function pullV2(
  xliffElement: Element,
  locale: string,
  originalLocale: string,
): Record<string, string> {
  const result: Record<string, string> = {};

  // Add source language metadata
  const srcLang = xliffElement.getAttribute("srcLang") || originalLocale;
  result.sourceLanguage = srcLang;

  const fileElements = xliffElement.querySelectorAll("file");

  fileElements.forEach((fileElement) => {
    const fileId = fileElement.getAttribute("id");
    if (!fileId) return;

    traverseUnitsV2(fileElement, fileId, "", result);
  });

  return result;
}

function traverseUnitsV2(
  container: Element,
  fileId: string,
  currentPath: string,
  result: Record<string, string>,
): void {
  Array.from(container.children).forEach((child) => {
    const tagName = child.tagName;

    if (tagName === "unit") {
      const unitId = child.getAttribute("id")?.trim();
      if (!unitId) return;

      const key = `resources/${fileId}/${currentPath}${unitId}/source`;
      const segment = child.querySelector("segment");
      const source = segment?.querySelector("source");

      if (source) {
        result[key] = extractTextContent(source);
      } else {
        result[key] = unitId; // fallback
      }
    } else if (tagName === "group") {
      const groupId = child.getAttribute("id")?.trim();
      const newPath = groupId
        ? `${currentPath}${groupId}/groupUnits/`
        : currentPath;
      traverseUnitsV2(child, fileId, newPath, result);
    }
  });
}

function pushV2(
  dom: JSDOM,
  xliffElement: Element,
  locale: string,
  translations: Record<string, string>,
  originalLocale: string,
  originalInput?: string,
): string {
  const document = dom.window.document;

  // Handle sourceLanguage metadata
  if (translations.sourceLanguage) {
    xliffElement.setAttribute("srcLang", translations.sourceLanguage);
    delete translations.sourceLanguage; // Don't process as regular translation
  }

  // Build index of existing units
  const existingUnits = new Map<string, Element>();
  const fileElements = xliffElement.querySelectorAll("file");

  fileElements.forEach((fileElement) => {
    const fileId = fileElement.getAttribute("id");
    if (!fileId) return;

    indexUnitsV2(fileElement, fileId, "", existingUnits);
  });

  // Update existing units
  Object.entries(translations).forEach(([key, value]) => {
    const unit = existingUnits.get(key);
    if (unit) {
      updateUnitV2(unit, value);
    } else {
      // For new units, we'd need to create the structure
      // This is complex in V2 due to the hierarchical nature
      console.warn(`Cannot create new unit for key: ${key} in XLIFF 2.0`);
    }
  });

  return serializeWithDeclaration(
    dom,
    extractXmlDeclaration(originalInput || ""),
  );
}

function indexUnitsV2(
  container: Element,
  fileId: string,
  currentPath: string,
  index: Map<string, Element>,
): void {
  Array.from(container.children).forEach((child) => {
    const tagName = child.tagName;

    if (tagName === "unit") {
      const unitId = child.getAttribute("id")?.trim();
      if (!unitId) return;

      const key = `resources/${fileId}/${currentPath}${unitId}/source`;
      index.set(key, child);
    } else if (tagName === "group") {
      const groupId = child.getAttribute("id")?.trim();
      const newPath = groupId
        ? `${currentPath}${groupId}/groupUnits/`
        : currentPath;
      indexUnitsV2(child, fileId, newPath, index);
    }
  });
}

function updateUnitV2(unit: Element, value: string): void {
  const document = unit.ownerDocument!;

  let segment = unit.querySelector("segment");
  if (!segment) {
    segment = document.createElement("segment");
    unit.appendChild(segment);
  }

  let source = segment.querySelector("source");
  if (!source) {
    source = document.createElement("source");
    segment.appendChild(source);
  }

  setTextContent(source, value);
}

/* -------------------------------------------------------------------------- */
/*                              Utilities                                     */
/* -------------------------------------------------------------------------- */

function getTransUnitKey(transUnit: Element): string {
  const resname = transUnit.getAttribute("resname")?.trim();
  if (resname) return resname;

  const id = transUnit.getAttribute("id")?.trim();
  if (id) return id;

  const sourceElement = transUnit.querySelector("source");
  if (sourceElement) {
    const sourceText = extractTextContent(sourceElement).trim();
    if (sourceText) return sourceText;
  }

  return "";
}

function extractTextContent(element: Element): string {
  // Handle CDATA sections
  const cdataNode = Array.from(element.childNodes).find(
    (node) => node.nodeType === element.CDATA_SECTION_NODE,
  );

  if (cdataNode) {
    return cdataNode.nodeValue || "";
  }

  return element.textContent || "";
}

function setTextContent(element: Element, content: string): void {
  const document = element.ownerDocument!;

  // Clear existing content
  while (element.firstChild) {
    element.removeChild(element.firstChild);
  }

  // Use CDATA if content contains XML-sensitive characters
  if (/[<>&"']/.test(content)) {
    const cdataSection = document.createCDATASection(content);
    element.appendChild(cdataSection);
  } else {
    element.textContent = content;
  }
}

function extractXmlDeclaration(xmlContent: string): string {
  const match = xmlContent.match(/^<\?xml[^>]*\?>/);
  return match ? match[0] : "";
}

function serializeWithDeclaration(dom: JSDOM, declaration: string): string {
  let serialized = dom.serialize();

  // Add proper indentation for readability
  serialized = formatXml(serialized);

  if (declaration) {
    serialized = `${declaration}\n${serialized}`;
  }

  return serialized;
}

function formatXml(xml: string): string {
  // Parse and reformat XML with proper indentation using JSDOM
  const dom = new JSDOM(xml, { contentType: "text/xml" });
  const doc = dom.window.document;

  function formatElement(element: Element, depth: number = 0): string {
    const indent = "  ".repeat(depth);
    const tagName = element.tagName;
    const attributes = Array.from(element.attributes)
      .map((attr) => `${attr.name}="${attr.value}"`)
      .join(" ");

    const openTag = attributes ? `<${tagName} ${attributes}>` : `<${tagName}>`;

    // Check for CDATA sections first
    const cdataNode = Array.from(element.childNodes).find(
      (node) => node.nodeType === element.CDATA_SECTION_NODE,
    );

    if (cdataNode) {
      return `${indent}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;
    }

    // Check if element has only text content
    const textContent = element.textContent?.trim() || "";
    const hasOnlyText =
      element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;

    if (hasOnlyText && textContent) {
      return `${indent}${openTag}${textContent}</${tagName}>`;
    }

    // Element has child elements
    const children = Array.from(element.children);
    if (children.length === 0) {
      return `${indent}${openTag}</${tagName}>`;
    }

    let result = `${indent}${openTag}\n`;
    for (const child of children) {
      result += formatElement(child, depth + 1) + "\n";
    }
    result += `${indent}</${tagName}>`;

    return result;
  }

  return formatElement(doc.documentElement);
}

function createEmptyResult(
  originalLocale: string,
  locale: string,
): Record<string, string> {
  return {};
}

function pushNewFile(
  locale: string,
  translations: Record<string, string>,
  originalLocale: string,
): string {
  const skeleton = `<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
  <file original="" source-language="${originalLocale}" target-language="${locale}" datatype="plaintext">
    <header></header>
    <body></body>
  </file>
</xliff>`;

  const dom = new JSDOM(skeleton, { contentType: "text/xml" });
  const document = dom.window.document;
  const bodyElement = document.querySelector("body")!;

  Object.entries(translations).forEach(([key, value]) => {
    const unit = document.createElement("trans-unit");
    unit.setAttribute("id", key);
    unit.setAttribute("resname", key);
    unit.setAttribute("restype", "string");
    unit.setAttribute("datatype", "plaintext");

    const sourceElement = document.createElement("source");
    setTextContent(sourceElement, key);
    unit.appendChild(sourceElement);

    const targetElement = document.createElement("target");
    targetElement.setAttribute("state", value ? "translated" : "new");
    setTextContent(targetElement, value);
    unit.appendChild(targetElement);

    bodyElement.appendChild(unit);
  });

  return serializeWithDeclaration(
    dom,
    '<?xml version="1.0" encoding="utf-8"?>',
  );
}

```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/index.ts:
--------------------------------------------------------------------------------

```typescript
import Z from "zod";
import jsdom from "jsdom";
import { bucketTypeSchema } from "@lingo.dev/_spec";
import { composeLoaders } from "./_utils";
import createJsonLoader from "./json";
import createJson5Loader from "./json5";
import createJsoncLoader from "./jsonc";
import createFlatLoader from "./flat";
import createTextFileLoader from "./text-file";
import createYamlLoader from "./yaml";
import createRootKeyLoader from "./root-key";
import createFlutterLoader from "./flutter";
import { ILoader } from "./_types";
import createAndroidLoader from "./android";
import createCsvLoader from "./csv";
import createHtmlLoader from "./html";
import createMarkdownLoader from "./markdown";
import createMarkdocLoader from "./markdoc";
import createPropertiesLoader from "./properties";
import createXcodeStringsLoader from "./xcode-strings";
import createXcodeStringsdictLoader from "./xcode-stringsdict";
import createXcodeXcstringsLoader from "./xcode-xcstrings";
import createXcodeXcstringsV2Loader from "./xcode-xcstrings-v2-loader";
import { isICUPluralObject } from "./xcode-xcstrings-icu";
import createUnlocalizableLoader from "./unlocalizable";
import { createFormatterLoader, FormatterType } from "./formatters";
import createPoLoader from "./po";
import createXliffLoader from "./xliff";
import createXmlLoader from "./xml";
import createSrtLoader from "./srt";
import createDatoLoader from "./dato";
import createVttLoader from "./vtt";
import createVariableLoader from "./variable";
import createSyncLoader from "./sync";
import createPlutilJsonTextLoader from "./plutil-json-loader";
import createPhpLoader from "./php";
import createVueJsonLoader from "./vue-json";
import createTypescriptLoader from "./typescript";
import createInjectLocaleLoader from "./inject-locale";
import createLockedKeysLoader from "./locked-keys";
import createMdxFrontmatterSplitLoader from "./mdx2/frontmatter-split";
import createMdxCodePlaceholderLoader from "./mdx2/code-placeholder";
import createLocalizableMdxDocumentLoader from "./mdx2/localizable-document";
import createMdxSectionsSplit2Loader from "./mdx2/sections-split-2";
import createLockedPatternsLoader from "./locked-patterns";
import createIgnoredKeysLoader from "./ignored-keys";
import createEjsLoader from "./ejs";
import createEnsureKeyOrderLoader from "./ensure-key-order";
import createTxtLoader from "./txt";
import createJsonKeysLoader from "./json-dictionary";

type BucketLoaderOptions = {
  returnUnlocalizedKeys?: boolean;
  defaultLocale: string;
  injectLocale?: string[];
  targetLocale?: string;
  formatter?: FormatterType;
};

export default function createBucketLoader(
  bucketType: Z.infer<typeof bucketTypeSchema>,
  bucketPathPattern: string,
  options: BucketLoaderOptions,
  lockedKeys?: string[],
  lockedPatterns?: string[],
  ignoredKeys?: string[],
): ILoader<void, Record<string, any>> {
  switch (bucketType) {
    default:
      throw new Error(`Unsupported bucket type: ${bucketType}`);
    case "android":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createAndroidLoader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "csv":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createCsvLoader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "html":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "html", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createHtmlLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "ejs":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createEjsLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "json":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "json", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createJsonLoader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createInjectLocaleLoader(options.injectLocale),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "json5":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createJson5Loader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createInjectLocaleLoader(options.injectLocale),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "jsonc":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createJsoncLoader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createInjectLocaleLoader(options.injectLocale),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "markdown":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "markdown", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createMarkdownLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "markdoc":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createMarkdocLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "mdx":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "mdx", bucketPathPattern),
        createMdxCodePlaceholderLoader(),
        createLockedPatternsLoader(lockedPatterns),
        createMdxFrontmatterSplitLoader(),
        createMdxSectionsSplit2Loader(),
        createLocalizableMdxDocumentLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "po":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createPoLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createVariableLoader({ type: "python" }),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "properties":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createPropertiesLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xcode-strings":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createXcodeStringsLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xcode-stringsdict":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createXcodeStringsdictLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xcode-xcstrings":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createPlutilJsonTextLoader(),
        createLockedPatternsLoader(lockedPatterns),
        createJsonLoader(),
        createXcodeXcstringsLoader(options.defaultLocale),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createVariableLoader({ type: "ieee" }),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xcode-xcstrings-v2":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createPlutilJsonTextLoader(),
        createLockedPatternsLoader(lockedPatterns),
        createJsonLoader(),
        createXcodeXcstringsLoader(options.defaultLocale),
        createXcodeXcstringsV2Loader(options.defaultLocale),
        createFlatLoader({ shouldPreserveObject: isICUPluralObject }),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createVariableLoader({ type: "ieee" }),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "yaml":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "yaml", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createYamlLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "yaml-root-key":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "yaml", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createYamlLoader(),
        createRootKeyLoader(true),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "flutter":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "json", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createJsonLoader(),
        createEnsureKeyOrderLoader(),
        createFlutterLoader(),
        createFlatLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xliff":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createXliffLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "xml":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createXmlLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "srt":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createSrtLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "dato":
      return composeLoaders(
        createDatoLoader(bucketPathPattern),
        createSyncLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "vtt":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createVttLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "php":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createPhpLoader(),
        createSyncLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "vue-json":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createVueJsonLoader(),
        createSyncLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "typescript":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(
          options.formatter,
          "typescript",
          bucketPathPattern,
        ),
        createLockedPatternsLoader(lockedPatterns),
        createTypescriptLoader(),
        createFlatLoader(),
        createEnsureKeyOrderLoader(),
        createSyncLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "txt":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createTxtLoader(),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
    case "json-dictionary":
      return composeLoaders(
        createTextFileLoader(bucketPathPattern),
        createFormatterLoader(options.formatter, "json", bucketPathPattern),
        createLockedPatternsLoader(lockedPatterns),
        createJsonLoader(),
        createJsonKeysLoader(),
        createEnsureKeyOrderLoader(),
        createFlatLoader(),
        createInjectLocaleLoader(options.injectLocale),
        createLockedKeysLoader(lockedKeys || []),
        createIgnoredKeysLoader(ignoredKeys || []),
        createSyncLoader(),
        createUnlocalizableLoader(options.returnUnlocalizedKeys),
      );
  }
}

```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/markdoc.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from "vitest";
import createMarkdocLoader from "./markdoc";

describe("markdoc loader", () => {
  describe("block-level tag", () => {
    it("should extract text content from block-level tag", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% foo %}
This is content inside of a block-level tag
{% /foo %}`;

      const output = await loader.pull("en", input);

      // Should extract the text content with semantic keys
      const contents = Object.values(output);

      expect(contents).toContain("This is content inside of a block-level tag");
    });

    it("should preserve tag structure on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% foo %}
This is content inside of a block-level tag
{% /foo %}`;

      const pulled = await loader.pull("en", input);
      const pushed = await loader.push("en", pulled);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should apply translations on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% example %}
This paragraph is nested within a Markdoc tag.
{% /example %}`;

      const pulled = await loader.pull("en", input);

      // Modify the content using semantic keys
      const translated = { ...pulled };
      const contentKey = Object.keys(translated).find(
        (k) =>
          translated[k] === "This paragraph is nested within a Markdoc tag.",
      );
      if (contentKey) {
        translated[contentKey] =
          "Este párrafo está anidado dentro de una etiqueta Markdoc.";
      }

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain(
        "Este párrafo está anidado dentro de una etiqueta Markdoc.",
      );
      expect(pushed).toContain("{% example %}");
      expect(pushed).toContain("{% /example %}");
    });
  });

  describe("self-closing tag", () => {
    it("should handle self-closing tag with no content", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% example /%}`;

      const output = await loader.pull("en", input);

      // Should have the tag but no text content
      expect(output).toBeDefined();
    });

    it("should preserve self-closing tag on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% example /%}`;

      const pulled = await loader.pull("en", input);
      const pushed = await loader.push("en", pulled);

      expect(pushed.trim()).toBe(input.trim());
    });
  });

  describe("inline tag", () => {
    it("should extract text from inline tag", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `This is a paragraph {% foo %}that contains a tag{% /foo %}`;

      const output = await loader.pull("en", input);

      // Should extract both text segments
      const contents = Object.values(output);

      expect(contents).toContain("This is a paragraph ");
      expect(contents).toContain("that contains a tag");
    });

    it("should preserve inline tag structure on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `This is a paragraph {% foo %}that contains a tag{% /foo %}`;

      const pulled = await loader.pull("en", input);
      const pushed = await loader.push("en", pulled);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should apply translations to inline tag content", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `This is a paragraph {% foo %}that contains a tag{% /foo %}`;

      const pulled = await loader.pull("en", input);

      // Translate both text segments
      const translated = { ...pulled };
      Object.keys(translated).forEach((key) => {
        if (translated[key] === "This is a paragraph ") {
          translated[key] = "Este es un párrafo ";
        } else if (translated[key] === "that contains a tag") {
          translated[key] = "que contiene una etiqueta";
        }
      });

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("Este es un párrafo");
      expect(pushed).toContain("que contiene una etiqueta");
      expect(pushed).toContain("{% foo %}");
      expect(pushed).toContain("{% /foo %}");
    });
  });

  describe("inline tag only content", () => {
    it("should handle inline tag as sole paragraph content", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% foo %}This is content inside of an inline tag{% /foo %}`;

      const output = await loader.pull("en", input);

      const contents = Object.values(output);

      expect(contents).toContain("This is content inside of an inline tag");
    });

    it("should preserve inline-only tag structure on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% foo %}This is content inside of an inline tag{% /foo %}`;

      const pulled = await loader.pull("en", input);
      const pushed = await loader.push("en", pulled);

      expect(pushed.trim()).toBe(input.trim());
    });
  });

  describe("mixed content", () => {
    it("should handle document with multiple tags and text", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Heading

This is a paragraph.

{% note %}
Important information here.
{% /note %}

Another paragraph with {% inline %}inline content{% /inline %}.

{% self-closing /%}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      // Verify structure is preserved
      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("{% note %}");
      expect(pushed).toContain("{% /note %}");
      expect(pushed).toContain("{% inline %}");
      expect(pushed).toContain("{% /inline %}");
      expect(pushed).toContain("{% self-closing /%}");
    });
  });

  describe("nested tags", () => {
    it("should handle nested tags", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% outer %}
Outer content
{% inner %}
Inner content
{% /inner %}
More outer content
{% /outer %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("{% outer %}");
      expect(pushed).toContain("{% inner %}");
      expect(pushed).toContain("{% /inner %}");
      expect(pushed).toContain("{% /outer %}");
    });
  });

  describe("interpolation", () => {
    it("should preserve variable interpolation", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `Hello {% $username %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should preserve function interpolation", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `Result: {% calculateValue() %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should preserve interpolation in middle of text", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `This is {% $var %} some text.`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should translate text around interpolation", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `Hello {% $username %}, welcome!`;

      const output = await loader.pull("en", input);

      // Should extract text segments but not interpolation
      const textContents = Object.values(output).filter(
        (v) => typeof v === "string",
      );

      expect(textContents).toContain("Hello ");
      expect(textContents).toContain(", welcome!");

      // Translate the text segments
      const translated = { ...output };
      Object.keys(translated).forEach((key) => {
        if (translated[key] === "Hello ") {
          translated[key] = "Hola ";
        } else if (translated[key] === ", welcome!") {
          translated[key] = ", ¡bienvenido!";
        }
      });

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("Hola");
      expect(pushed).toContain("¡bienvenido!");
      expect(pushed).toContain("{% $username %}");
    });

    it("should handle interpolation in tags", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% callout %}
The value is {% $value %} today.
{% /callout %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("{% callout %}");
      expect(pushed).toContain("{% $value %}");
      expect(pushed).toContain("{% /callout %}");
    });
  });

  describe("annotations", () => {
    it("should preserve annotations with shorthand class attribute", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Heading {% .example %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("{% .example %}");
    });

    it("should preserve annotations with shorthand id attribute", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Heading {% #main-title %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("{% #main-title %}");
    });

    it("should preserve annotations with multiple shorthand attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Heading {% #foo .bar .baz %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("{% #foo .bar .baz %}");
    });

    it("should translate heading text with annotations", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Welcome {% .hero-title %}`;

      const output = await loader.pull("en", input);

      // Find and translate the heading text (note: has trailing space)
      const translated = { ...output };
      Object.keys(translated).forEach((key) => {
        if (translated[key] === "Welcome ") {
          translated[key] = "Bienvenido ";
        }
      });

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("Bienvenido");
      expect(pushed).toContain("{% .hero-title %}");
    });
  });

  describe("tag attributes", () => {
    it("should preserve tags with full attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% callout type="note" %}
This is important information.
{% /callout %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain('{% callout type="note" %}');
      expect(pushed).toContain("{% /callout %}");
    });

    it("should preserve tags with multiple attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% image src="logo.png" alt="Company Logo" width="200" /%}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should preserve tags with array attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% chart data=[1, 2, 3] /%}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed.trim()).toBe(input.trim());
    });

    it("should translate content in tags with attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% callout type="warning" %}
Please read carefully.
{% /callout %}`;

      const output = await loader.pull("en", input);

      // Translate the content
      const translated = { ...output };
      Object.keys(translated).forEach((key) => {
        if (translated[key] === "Please read carefully.") {
          translated[key] = "Por favor lea con atención.";
        }
      });

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("Por favor lea con atención.");
      expect(pushed).toContain('{% callout type="warning" %}');
      expect(pushed).toContain("{% /callout %}");
    });
  });

  describe("primary attributes", () => {
    it("should preserve tags with primary attribute", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% if $showContent %}
Content is visible.
{% /if %}`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).toContain("{% if $showContent %}");
      expect(pushed).toContain("{% /if %}");
    });

    it("should translate content in tags with primary attribute", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `{% if $showContent %}
Content is visible.
{% /if %}`;

      const output = await loader.pull("en", input);

      // Translate the content
      const translated = { ...output };
      Object.keys(translated).forEach((key) => {
        if (translated[key] === "Content is visible.") {
          translated[key] = "El contenido es visible.";
        }
      });

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("El contenido es visible.");
      expect(pushed).toContain("{% if $showContent %}");
    });
  });

  describe("frontmatter", () => {
    it("should extract frontmatter attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `---
title: My Document
description: A sample document
author: John Doe
---

# Heading

Content here.`;

      const output = await loader.pull("en", input);

      expect(output["fm-attr-title"]).toBe("My Document");
      expect(output["fm-attr-description"]).toBe("A sample document");
      expect(output["fm-attr-author"]).toBe("John Doe");
    });

    it("should preserve frontmatter on push", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `---
title: My Document
description: A sample document
---

# Heading

Content here.`;

      const pulled = await loader.pull("en", input);
      const pushed = await loader.push("en", pulled);

      expect(pushed).toContain("title: My Document");
      expect(pushed).toContain("description: A sample document");
      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("Content here.");
    });

    it("should translate frontmatter attributes", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `---
title: Welcome
description: This is a guide
---

# Content

Some text.`;

      const pulled = await loader.pull("en", input);

      // Translate frontmatter
      const translated = { ...pulled };
      translated["fm-attr-title"] = "Bienvenido";
      translated["fm-attr-description"] = "Esta es una guía";

      const pushed = await loader.push("es", translated);

      expect(pushed).toContain("title: Bienvenido");
      expect(pushed).toContain("description: Esta es una guía");
    });

    it("should handle documents without frontmatter", async () => {
      const loader = createMarkdocLoader();
      loader.setDefaultLocale("en");

      const input = `# Heading

Content without frontmatter.`;

      const output = await loader.pull("en", input);
      const pushed = await loader.push("en", output);

      expect(pushed).not.toContain("---");
      expect(pushed).toContain("# Heading");
      expect(pushed).toContain("Content without frontmatter.");
    });
  });
});

```

--------------------------------------------------------------------------------
/packages/compiler/src/lib/lcp/api/index.ts:
--------------------------------------------------------------------------------

```typescript
import { createGroq } from "@ai-sdk/groq";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { createOllama } from "ollama-ai-provider";
import { createMistral } from "@ai-sdk/mistral";
import { generateText } from "ai";
import { LingoDotDevEngine } from "@lingo.dev/_sdk";
import { DictionarySchema } from "../schema";
import _ from "lodash";
import { getLocaleModel } from "../../../utils/locales";
import getSystemPrompt from "./prompt";
import { obj2xml, xml2obj } from "./xml2obj";
import shots from "./shots";
import {
  getGroqKey,
  getGroqKeyFromEnv,
  getGoogleKey,
  getGoogleKeyFromEnv,
  getOpenRouterKey,
  getOpenRouterKeyFromEnv,
  getMistralKey,
  getMistralKeyFromEnv,
  getLingoDotDevKeyFromEnv,
  getLingoDotDevKey,
} from "../../../utils/llm-api-key";
import dedent from "dedent";
import { isRunningInCIOrDocker } from "../../../utils/env";
import { LanguageModel } from "ai";
import { providerDetails } from "./provider-details";

export class LCPAPI {
  static async translate(
    models: "lingo.dev" | Record<string, string>,
    sourceDictionary: DictionarySchema,
    sourceLocale: string,
    targetLocale: string,
    prompt?: string | null,
  ): Promise<DictionarySchema> {
    const timeLabel = `LCPAPI.translate: ${targetLocale}`;
    console.time(timeLabel);
    const chunks = this._chunkDictionary(sourceDictionary);
    const translatedChunks = [];
    for (const chunk of chunks) {
      const translatedChunk = await this._translateChunk(
        models,
        chunk,
        sourceLocale,
        targetLocale,
        prompt,
      );
      translatedChunks.push(translatedChunk);
    }
    const result = this._mergeDictionaries(translatedChunks);
    console.timeEnd(timeLabel);
    return result;
  }

  private static _chunkDictionary(
    dictionary: DictionarySchema,
  ): DictionarySchema[] {
    const MAX_ENTRIES_PER_CHUNK = 100;
    const { files, ...rest } = dictionary;
    const chunks: DictionarySchema[] = [];

    let currentChunk: DictionarySchema = {
      ...rest,
      files: {},
    };
    let currentEntryCount = 0;

    Object.entries(files).forEach(([fileName, file]) => {
      const entries = file.entries;
      const entryPairs = Object.entries(entries);

      let currentIndex = 0;
      while (currentIndex < entryPairs.length) {
        const remainingSpace = MAX_ENTRIES_PER_CHUNK - currentEntryCount;
        const entriesToAdd = entryPairs.slice(
          currentIndex,
          currentIndex + remainingSpace,
        );

        if (entriesToAdd.length > 0) {
          currentChunk.files[fileName] = currentChunk.files[fileName] || {
            entries: {},
          };
          currentChunk.files[fileName].entries = {
            ...currentChunk.files[fileName].entries,
            ...Object.fromEntries(entriesToAdd),
          };
          currentEntryCount += entriesToAdd.length;
        }

        currentIndex += entriesToAdd.length;

        if (
          currentEntryCount >= MAX_ENTRIES_PER_CHUNK ||
          (currentIndex < entryPairs.length &&
            currentEntryCount + (entryPairs.length - currentIndex) >
              MAX_ENTRIES_PER_CHUNK)
        ) {
          chunks.push(currentChunk);
          currentChunk = { ...rest, files: {} };
          currentEntryCount = 0;
        }
      }
    });

    if (currentEntryCount > 0) {
      chunks.push(currentChunk);
    }

    return chunks;
  }

  private static _mergeDictionaries(dictionaries: DictionarySchema[]) {
    const fileNames = _.uniq(
      _.flatMap(dictionaries, (dict) => Object.keys(dict.files)),
    );
    const files = _(fileNames)
      .map((fileName) => {
        const entries = dictionaries.reduce((entries, dict) => {
          const file = dict.files[fileName];
          if (file) {
            entries = _.merge(entries, file.entries);
          }
          return entries;
        }, {});
        return [fileName, { entries }];
      })
      .fromPairs()
      .value();
    const dictionary = {
      version: dictionaries[0].version,
      locale: dictionaries[0].locale,
      files,
    };
    return dictionary;
  }

  private static _createLingoDotDevEngine() {
    // Specific check for CI/CD or Docker missing GROQ key
    if (isRunningInCIOrDocker()) {
      const apiKeyFromEnv = getLingoDotDevKeyFromEnv();
      if (!apiKeyFromEnv) {
        this._failMissingLLMKeyCi("lingo.dev");
      }
    }
    const apiKey = getLingoDotDevKey();
    if (!apiKey) {
      throw new Error(
        "⚠️  Lingo.dev API key not found. Please set LINGODOTDEV_API_KEY environment variable or configure it user-wide.",
      );
    }
    console.log(`Creating Lingo.dev client`);
    return new LingoDotDevEngine({
      apiKey,
    });
  }

  private static async _translateChunk(
    models: "lingo.dev" | Record<string, string>,
    sourceDictionary: DictionarySchema,
    sourceLocale: string,
    targetLocale: string,
    prompt?: string | null,
  ): Promise<DictionarySchema> {
    if (models === "lingo.dev") {
      try {
        const lingoDotDevEngine = this._createLingoDotDevEngine();

        console.log(
          `✨ Using Lingo.dev Engine to localize from "${sourceLocale}" to "${targetLocale}"`,
        );

        const result = await lingoDotDevEngine.localizeObject(
          sourceDictionary,
          {
            sourceLocale: sourceLocale,
            targetLocale: targetLocale,
          },
        );

        return result as DictionarySchema;
      } catch (error) {
        this._failLLMFailureLocal(
          "lingo.dev",
          targetLocale,
          error instanceof Error ? error.message : "Unknown error",
        );
        // This throw is unreachable because the failure method exits,
        // but it helps satisfy the TypeScript compiler.
        throw error;
      }
    } else {
      const { provider, model } = getLocaleModel(
        models,
        sourceLocale,
        targetLocale,
      );

      if (!provider || !model) {
        throw new Error(
          dedent`
            🚫  Lingo.dev Localization Engine Not Configured!

            The "models" parameter is missing or incomplete in your Lingo.dev configuration.

            👉 To fix this, set the "models" parameter to either:
               • "lingo.dev" (for the default engine)
               • a map of locale-to-model, e.g. { "models": { "en:es": "openai:gpt-3.5-turbo" } }

            Example:
              {
                // ...
                "models": "lingo.dev"
              }

            For more details, see: https://lingo.dev/compiler
            To get help, join our Discord: https://lingo.dev/go/discord
            `,
        );
      }

      try {
        const aiModel = this._createAiModel(provider, model, targetLocale);

        console.log(
          `ℹ️ Using raw LLM API ("${provider}":"${model}") to translate from "${sourceLocale}" to "${targetLocale}"`,
        );

        const response = await generateText({
          model: aiModel,
          messages: [
            {
              role: "system",
              content: getSystemPrompt({
                sourceLocale,
                targetLocale,
                prompt: prompt ?? undefined,
              }),
            },
            ...shots.flatMap((shotsTuple) => [
              {
                role: "user" as const,
                content: obj2xml(shotsTuple[0]),
              },
              {
                role: "assistant" as const,
                content: obj2xml(shotsTuple[1]),
              },
            ]),
            {
              role: "user",
              content: obj2xml(sourceDictionary),
            },
          ],
        });

        console.log("Response text received for", targetLocale);
        let responseText = response.text;
        // Extract XML content
        responseText = responseText.substring(
          responseText.indexOf("<"),
          responseText.lastIndexOf(">") + 1,
        );

        return xml2obj(responseText);
      } catch (error) {
        this._failLLMFailureLocal(
          provider,
          targetLocale,
          error instanceof Error ? error.message : "Unknown error",
        );
        // This throw is unreachable because the failure method exits,
        // but it helps satisfy the TypeScript compiler.
        throw error;
      }
    }
  }

  /**
   * Instantiates an AI model based on provider and model ID.
   * Includes CI/CD API key checks.
   * @param providerId The ID of the AI provider (e.g., "groq", "google").
   * @param modelId The ID of the specific model (e.g., "llama3-8b-8192", "gemini-2.0-flash").
   * @param targetLocale The target locale being translated to (for logging/error messages).
   * @returns An instantiated AI LanguageModel.
   * @throws Error if the provider is not supported or API key is missing in CI/CD.
   */
  private static _createAiModel(
    providerId: string,
    modelId: string,
    targetLocale: string,
  ): LanguageModel {
    switch (providerId) {
      case "groq": {
        // Specific check for CI/CD or Docker missing GROQ key
        if (isRunningInCIOrDocker()) {
          const groqFromEnv = getGroqKeyFromEnv();
          if (!groqFromEnv) {
            this._failMissingLLMKeyCi(providerId);
          }
        }
        const groqKey = getGroqKey();
        if (!groqKey) {
          throw new Error(
            "⚠️  GROQ API key not found. Please set GROQ_API_KEY environment variable or configure it user-wide.",
          );
        }
        console.log(
          `Creating Groq client for ${targetLocale} using model ${modelId}`,
        );
        return createGroq({ apiKey: groqKey })(modelId);
      }

      case "google": {
        // Specific check for CI/CD or Docker missing Google key
        if (isRunningInCIOrDocker()) {
          const googleFromEnv = getGoogleKeyFromEnv();
          if (!googleFromEnv) {
            this._failMissingLLMKeyCi(providerId);
          }
        }
        const googleKey = getGoogleKey();
        if (!googleKey) {
          throw new Error(
            "⚠️  Google API key not found. Please set GOOGLE_API_KEY environment variable or configure it user-wide.",
          );
        }
        console.log(
          `Creating Google Generative AI client for ${targetLocale} using model ${modelId}`,
        );
        return createGoogleGenerativeAI({ apiKey: googleKey })(modelId);
      }
      case "openrouter": {
        // Specific check for CI/CD or Docker missing OpenRouter key
        if (isRunningInCIOrDocker()) {
          const openRouterFromEnv = getOpenRouterKeyFromEnv();
          if (!openRouterFromEnv) {
            this._failMissingLLMKeyCi(providerId);
          }
        }
        const openRouterKey = getOpenRouterKey();
        if (!openRouterKey) {
          throw new Error(
            "⚠️  OpenRouter API key not found. Please set OPENROUTER_API_KEY environment variable or configure it user-wide.",
          );
        }
        console.log(
          `Creating OpenRouter client for ${targetLocale} using model ${modelId}`,
        );
        return createOpenRouter({
          apiKey: openRouterKey,
        })(modelId);
      }

      case "ollama": {
        // No API key check needed for Ollama
        console.log(
          `Creating Ollama client for ${targetLocale} using model ${modelId} at default Ollama address`,
        );
        return createOllama()(modelId);
      }

      case "mistral": {
        // Specific check for CI/CD or Docker missing Mistral key
        if (isRunningInCIOrDocker()) {
          const mistralFromEnv = getMistralKeyFromEnv();
          if (!mistralFromEnv) {
            this._failMissingLLMKeyCi(providerId);
          }
        }
        const mistralKey = getMistralKey();
        if (!mistralKey) {
          throw new Error(
            "⚠️  Mistral API key not found. Please set MISTRAL_API_KEY environment variable or configure it user-wide.",
          );
        }
        console.log(
          `Creating Mistral client for ${targetLocale} using model ${modelId}`,
        );
        return createMistral({ apiKey: mistralKey })(modelId);
      }

      default: {
        throw new Error(
          `⚠️  Provider "${providerId}" for locale "${targetLocale}" is not supported. Only "groq", "google", "openrouter", "ollama", and "mistral" providers are supported at the moment.`,
        );
      }
    }
  }

  /**
   * Show an actionable error message and exit the process when the compiler
   * is running in CI/CD without a required LLM API key.
   * The message explains why this situation is unusual and how to fix it.
   * @param providerId The ID of the LLM provider whose key is missing.
   */
  private static _failMissingLLMKeyCi(providerId: string): never {
    let details = providerDetails[providerId];
    if (!details) {
      // Fallback for unsupported provider in failure message logic
      throw new Error(
        `Internal Error: Missing details for provider "${providerId}" when reporting missing key in CI/CD. You might be using an unsupported provider.`,
      );
    }

    const errorMessage = dedent`
      💡 You're using Lingo.dev Localization Compiler, and it detected unlocalized components in your app.

      The compiler needs a ${details.name} API key to translate missing strings, but ${details.apiKeyEnvVar} is not set in the environment.

      This is unexpected: typically you run a full build locally, commit the generated translation files, and push them to CI/CD.

      However, If you want CI/CD to translate the new strings, provide the key with:
      • Session-wide: export ${details.apiKeyEnvVar}=<your-api-key>
      • Project-wide / CI: add ${details.apiKeyEnvVar}=<your-api-key> to your pipeline environment variables

      ⭐️ Also:
      1. If you don't yet have a ${details.name} API key, get one for free at ${details.getKeyLink}
      2. If you want to use a different LLM, update your configuration. Refer to documentation for help: https://lingo.dev/compiler
      3. If the model you want to use isn't supported yet, raise an issue in our open-source repo: https://lingo.dev/go/gh
    `;
    console.log(errorMessage);
    throw new Error(`Missing ${details.name} API key in CI/CD environment.`);
  }

  /**
   * Show an actionable error message and exit the process when an LLM API call
   * fails during local compilation.
   * @param providerId The ID of the LLM provider that failed.
   * @param targetLocale The target locale being translated to.
   * @param errorMessage The error message received from the API.
   */
  private static _failLLMFailureLocal(
    providerId: string,
    targetLocale: string,
    errorMessage: string,
  ): never {
    const details = providerDetails[providerId];
    if (!details) {
      // Fallback
      throw new Error(
        `Internal Error: Missing details for provider "${providerId}" when reporting local failure. Original Error: ${errorMessage}`,
      );
    }

    const isInvalidApiKey = errorMessage.match("Invalid API Key"); // TODO: This may change per-provider, so might update this later

    if (isInvalidApiKey) {
      const message = dedent`
        ⚠️  Lingo.dev Compiler requires a valid ${details.name} API key to translate your application.

        It looks like you set ${details.name} API key but it is not valid. Please check your API key and try again.

        Error details from ${details.name} API: ${errorMessage}

        👉 You can set the API key in one of the following ways:
        1. User-wide: Run npx lingo.dev@latest config set ${details.apiKeyConfigKey} <your-api-key>
        2. Project-wide: Add ${details.apiKeyEnvVar}=<your-api-key> to .env file in every project that uses Lingo.dev Localization Compiler
        3 Session-wide: Run export ${details.apiKeyEnvVar}=<your-api-key> in your terminal before running the compiler to set the API key for the current session

        ⭐️ Also:
        1. If you don't yet have a ${details.name} API key, get one for free at ${details.getKeyLink}
        2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
        3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
      `;
      console.log(message);
      throw new Error(`Invalid ${details.name} API key.`);
    } else {
      const message = dedent`
        ⚠️  Lingo.dev Compiler tried to translate your application to "${targetLocale}" locale via ${
          details.name
        } but it failed.

        Error details from ${details.name} API: ${errorMessage}

        This error comes from the ${
          details.name
        } API, please check their documentation for more details: ${
          details.docsLink
        }

        ⭐️ Also:
        1. Did you set ${
          details.apiKeyEnvVar
            ? `${details.apiKeyEnvVar}`
            : "the provider API key"
        } environment variable correctly ${
          !details.apiKeyEnvVar ? "(if required)" : ""
        }?
        2. Did you reach any limits of your ${details.name} account?
        3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
      `;
      console.log(message);
      throw new Error(
        `Translation failed for locale "${targetLocale}" using ${details.name}: ${errorMessage}`,
      );
    }
  }
}

```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/ignored-keys-buckets.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach, vi } from "vitest";
import fs from "fs/promises";
import dedent from "dedent";
import createBucketLoader from "./index";

describe("ignored keys support across buckets", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.resetModules();
    setupFileMocks();
  });

  it("android: should omit ignored keys on pull", async () => {
    const input = `
      <resources>
        <string name="button.title">Submit</string>
        <string name="button.description">Description</string>
      </resources>
    `.trim();
    mockFileOperations(input);

    const loader = createBucketLoader(
      "android",
      "values-[locale]/strings.xml",
      { defaultLocale: "en" },
      [],
      [],
      ["button.description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ "button.title": "Submit" });
  });

  it("csv: should omit ignored keys on pull", async () => {
    const input = `id,en\nbutton.title,Submit\nbutton.description,Description`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "csv",
      "i18n.csv",
      { defaultLocale: "en" },
      [],
      [],
      ["button.description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ "button.title": "Submit" });
  });

  it("html: should omit ignored keys (by prefix) on pull", async () => {
    const input = dedent`
      <html>
        <head>
          <title>My Page</title>
          <meta name="description" content="Page description" />
        </head>
        <body>
          <h1>Hello</h1>
          <p>Paragraph</p>
        </body>
      </html>
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "html",
      "i18n/[locale].html",
      { defaultLocale: "en" },
      [],
      [],
      ["head"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data).some((k) => k.startsWith("head"))).toBe(false);
  });

  it("ejs: should omit ignored keys on pull", async () => {
    const input = `<h1>Welcome</h1><p>Hello <%= name %></p>`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "ejs",
      "templates/[locale].ejs",
      { defaultLocale: "en" },
      [],
      [],
      ["text_*"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({});
  });

  it("json: should omit ignored keys on pull", async () => {
    const input = JSON.stringify({ title: "Submit", description: "Desc" });
    mockFileOperations(input);

    const loader = createBucketLoader(
      "json",
      "i18n/[locale].json",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("json5: should omit ignored keys on pull", async () => {
    const input = `{
      // comment
      title: "Submit",
      description: "Desc"
    }`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "json5",
      "i18n/[locale].json5",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("jsonc: should omit ignored keys on pull", async () => {
    const input = `{
      // comment
      "title": "Submit",
      "description": "Desc"
    }`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "jsonc",
      "i18n/[locale].jsonc",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("markdown: should omit ignored keys (frontmatter) on pull", async () => {
    const input = dedent`
      ---
      title: Test Markdown
      date: 2023-05-25
      ---

      # Heading 1

      Content.
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "markdown",
      "i18n/[locale].md",
      { defaultLocale: "en" },
      [],
      [],
      ["fm-attr-title"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).not.toContain("fm-attr-title");
  });

  it("markdoc: should omit ignored keys by semantic prefix on pull", async () => {
    const input = dedent`
      ---
      title: My Page
      ---

      # Heading 1

      Hello world
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "markdoc",
      "docs/[locale].md",
      { defaultLocale: "en" },
      [],
      [],
      ["heading"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data).some((k) => k.startsWith("heading"))).toBe(false);
  });

  it("mdx: should omit ignored section keys on pull", async () => {
    const input = dedent`
      ---
      title: Hello
      ---

      # Title

      Paragraph
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "mdx",
      "i18n/[locale].mdx",
      { defaultLocale: "en", formatter: undefined },
      [],
      [],
      ["md-section-0"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).not.toContain("md-section-0");
  });

  it("po: should omit ignored keys on pull", async () => {
    const input = dedent`
      #: hello.py:1
      msgid "Hello"
      msgstr ""
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "po",
      "i18n/[locale].po",
      { defaultLocale: "en" },
      [],
      [],
      ["Hello"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({});
  });

  it("properties: should omit ignored keys on pull", async () => {
    const input = dedent`
      welcome.message=Welcome
      error.message=Error
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "properties",
      "i18n/[locale].properties",
      { defaultLocale: "en" },
      [],
      [],
      ["error.message"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ "welcome.message": "Welcome" });
  });

  it("xcode-strings: should omit ignored keys on pull", async () => {
    const input = `"hello" = "Hello!";\n"bye" = "Bye!";`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xcode-strings",
      "i18n/[locale].strings",
      { defaultLocale: "en" },
      [],
      [],
      ["bye"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ hello: "Hello!" });
  });

  it("xcode-stringsdict: should omit ignored keys on pull", async () => {
    const input = dedent`
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
        <key>greeting</key>
        <string>Hello!</string>
        <key>items_count</key>
        <dict>
          <key>NSStringLocalizedFormatKey</key>
          <string>%#@items@</string>
          <key>items</key>
          <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>one</key>
            <string>%d item</string>
            <key>other</key>
            <string>%d items</string>
          </dict>
        </dict>
      </dict>
      </plist>
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xcode-stringsdict",
      "i18n/[locale].stringsdict",
      { defaultLocale: "en" },
      [],
      [],
      ["items_count"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toContain("greeting");
    expect(Object.keys(data).some((k) => k.startsWith("items_count"))).toBe(
      false,
    );
  });

  it("xcode-xcstrings: should omit ignored keys on pull", async () => {
    const input = dedent`
      {
        "sourceLanguage": "en",
        "strings": {
          "greeting": {
            "extractionState": "manual",
            "localizations": {
              "en": { "stringUnit": { "state": "translated", "value": "Hello!" } }
            }
          },
          "message": {
            "extractionState": "manual",
            "localizations": {
              "en": { "stringUnit": { "state": "translated", "value": "Welcome" } }
            }
          }
        }
      }
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xcode-xcstrings",
      "i18n/[locale].xcstrings",
      { defaultLocale: "en" },
      [],
      [],
      ["message"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ greeting: "Hello!" });
  });

  it("xcode-xcstrings-v2: should omit ignored string keys on pull", async () => {
    const input = dedent`
      {
        "sourceLanguage": "en",
        "strings": {
          "hello": {
            "extractionState": "manual",
            "localizations": {
              "en": { "stringUnit": { "state": "translated", "value": "Hello" } }
            }
          },
          "world": {
            "extractionState": "manual",
            "localizations": {
              "en": { "stringUnit": { "state": "translated", "value": "World" } }
            }
          }
        }
      }
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xcode-xcstrings-v2",
      "i18n/[locale].xcstrings",
      { defaultLocale: "en" },
      [],
      [],
      ["world"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toContain("hello");
    expect(Object.keys(data)).not.toContain("world");
  });

  it("yaml: should omit ignored keys on pull", async () => {
    const input = dedent`
      title: Submit
      description: Desc
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "yaml",
      "i18n/[locale].yml",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("yaml-root-key: should omit ignored keys on pull", async () => {
    const input = dedent`
      en:
        title: Submit
        description: Desc
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "yaml-root-key",
      "i18n/[locale].yml",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("flutter: should omit ignored keys on pull", async () => {
    const input = JSON.stringify(
      {
        "@@locale": "en",
        greeting: "Hello, {name}!",
        "@greeting": { description: "d" },
        farewell: "Goodbye!",
      },
      null,
      2,
    );
    mockFileOperations(input);

    const loader = createBucketLoader(
      "flutter",
      "lib/l10n/app_[locale].arb",
      { defaultLocale: "en" },
      [],
      [],
      ["farewell"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toContain("greeting");
    expect(Object.keys(data)).not.toContain("farewell");
  });

  it("xliff: should omit ignored keys on pull", async () => {
    const input = dedent`
      <?xml version="1.0" encoding="utf-8"?>
      <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
        <file original="" source-language="en" datatype="plaintext">
          <body>
            <trans-unit id="greeting" resname="greeting"><source>Hello</source></trans-unit>
            <trans-unit id="farewell" resname="farewell"><source>Goodbye</source></trans-unit>
          </body>
        </file>
      </xliff>
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xliff",
      "i18n/[locale].xliff",
      { defaultLocale: "en" },
      [],
      [],
      ["farewell"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toContain("greeting");
    expect(Object.keys(data)).not.toContain("farewell");
  });

  it("xml: should omit ignored keys on pull", async () => {
    const input = `<root><title>Hello</title><description>Desc</description></root>`;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "xml",
      "i18n/[locale].xml",
      { defaultLocale: "en" },
      [],
      [],
      ["root/description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toContain("root/title");
    expect(Object.keys(data)).not.toContain("root/description");
  });

  it("srt: should omit ignored keys on pull", async () => {
    const input = dedent`
      1
      00:00:01,000 --> 00:00:04,000
      Hello

      2
      00:00:05,000 --> 00:00:06,000
      World
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "srt",
      "i18n/[locale].srt",
      { defaultLocale: "en" },
      [],
      [],
      ["1#*"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    // Expect only entry 2 remains
    const keys = Object.keys(data);
    expect(keys.length).toBe(1);
    expect(keys[0].startsWith("2#")).toBe(true);
  });

  it("vtt: should omit ignored keys on pull", async () => {
    const input = dedent`
      WEBVTT

      00:00:00.000 --> 00:00:02.000
      First

      00:00:02.000 --> 00:00:04.000
      Second
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "vtt",
      "i18n/[locale].vtt",
      { defaultLocale: "en" },
      [],
      [],
      ["0#*"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    // One cue should be filtered
    expect(Object.keys(data).length).toBe(1);
  });

  it("php: should omit ignored keys on pull", async () => {
    const input = dedent`
      <?php
      return [
        'title' => 'Submit',
        'description' => 'Desc',
      ];
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "php",
      "i18n/[locale].php",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("vue-json: should omit ignored keys on pull", async () => {
    const input = dedent`
      <template></template>
      <i18n>
      {"en": {"title": "Hello", "description": "Desc"}}
      </i18n>
      <script setup></script>
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "vue-json",
      "i18n/App.vue",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Hello" });
  });

  it("typescript: should omit ignored keys on pull", async () => {
    const input = dedent`
      export default {
        title: "Submit",
        description: "Desc"
      };
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "typescript",
      "i18n/[locale].ts",
      { defaultLocale: "en" },
      [],
      [],
      ["description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(data).toEqual({ title: "Submit" });
  });

  it("txt: should omit ignored keys on pull", async () => {
    const input = dedent`
      First line
      Second line
    `;
    mockFileOperations(input);

    const loader = createBucketLoader(
      "txt",
      "fastlane/metadata/[locale]/description.txt",
      { defaultLocale: "en" },
      [],
      [],
      ["1"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    expect(Object.keys(data)).toEqual(["2"]);
  });

  it("json-dictionary: should omit ignored keys on pull (wildcard)", async () => {
    const input = JSON.stringify(
      {
        title: { en: "Title" },
        pages: [
          {
            elements: [
              { title: { en: "E1" }, description: { en: "D1" } },
              { title: { en: "E2" }, description: { en: "D2" } },
            ],
          },
        ],
      },
      null,
      2,
    );
    mockFileOperations(input);

    const loader = createBucketLoader(
      "json-dictionary",
      "i18n/[locale].json",
      { defaultLocale: "en" },
      [],
      [],
      ["pages/*/elements/*/description"],
    );
    loader.setDefaultLocale("en");
    const data = await loader.pull("en");
    const keys = Object.keys(data);
    expect(keys).toContain("title");
    expect(keys).toContain("pages/0/elements/0/title");
    expect(keys.find((k) => k.includes("/description"))).toBeUndefined();
  });
});

function setupFileMocks() {
  vi.mock("fs/promises", () => ({
    default: {
      readFile: vi.fn(),
      writeFile: vi.fn(),
      mkdir: vi.fn(),
      access: vi.fn(),
    },
  }));

  vi.mock("path", () => ({
    default: {
      resolve: vi.fn((path) => path),
      dirname: vi.fn((path) => path.split("/").slice(0, -1).join("/")),
    },
  }));
}

function mockFileOperations(input: string) {
  (fs.access as any).mockImplementation(() => Promise.resolve());
  (fs.readFile as any).mockImplementation(() => Promise.resolve(input));
  (fs.writeFile as any).mockImplementation(() => Promise.resolve());
}

```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/xcode-xcstrings.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from "vitest";
import createXcodeXcstringsLoader, { _removeLocale } from "./xcode-xcstrings";

describe("loaders/xcode-xcstrings", () => {
  const defaultLocale = "en";
  const mockInput = {
    sourceLanguage: "en",
    strings: {
      "app.title": {
        localizations: {
          en: {
            stringUnit: {
              state: "translated",
              value: "My App",
            },
          },
          es: {
            stringUnit: {
              state: "translated",
              value: "Mi App",
            },
          },
        },
      },
      "items.count": {
        localizations: {
          en: {
            variations: {
              plural: {
                one: {
                  stringUnit: {
                    state: "translated",
                    value: "1 item",
                  },
                },
                other: {
                  stringUnit: {
                    state: "translated",
                    value: "%d items",
                  },
                },
              },
            },
          },
          es: {
            variations: {
              plural: {
                one: {
                  stringUnit: {
                    state: "translated",
                    value: "1 artículo",
                  },
                },
                other: {
                  stringUnit: {
                    state: "translated",
                    value: "%d artículos",
                  },
                },
              },
            },
          },
        },
      },
      "key.no-translate": {
        shouldTranslate: false,
        localizations: {
          en: {
            stringUnit: {
              state: "translated",
              value: "Do not translate",
            },
          },
        },
      },
      "key.source-only": {
        localizations: {},
      },
      "key.missing-localization": {
        localizations: {
          es: {
            stringUnit: {
              state: "translated",
              value: "solo español",
            },
          },
        },
      },
    },
    version: "1.0",
  };

  describe("pull", () => {
    it("should pull simple string translations for a given locale", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const result = await loader.pull("es", mockInput);
      expect(result).toEqual({
        "app.title": "Mi App",
        "items.count": {
          one: "1 artículo",
          other: "%d artículos",
        },
        "key.missing-localization": "solo español",
      });
    });

    it("should pull plural translations for a given locale", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      const result = await loader.pull("en", mockInput);
      expect(result["items.count"]).toEqual({
        one: "1 item",
        other: "%d items",
      });
    });

    it("should use the key as value for the source language if no translation is available", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      const result = await loader.pull("en", mockInput);
      expect(result["key.source-only"]).toBe("key.source-only");
      expect(result["key.missing-localization"]).toBe(
        "key.missing-localization",
      );
    });

    it("should not use key as value if not source language", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const result = await loader.pull("es", mockInput);
      expect(result["key.source-only"]).toBeUndefined();
    });

    it("should skip keys marked with shouldTranslate: false", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      const result = await loader.pull("en", mockInput);
      expect(result["key.no-translate"]).toBeUndefined();
    });

    it("should return an empty object for a locale with no translations", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const result = await loader.pull("fr", mockInput);
      expect(result).toEqual({});
    });
  });

  describe("push", () => {
    it("should push simple string translations", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "app.title": "Mon App",
      };
      const result = await loader.push("fr", payload);
      expect(result).not.toBeNull();
      expect(result!.version).toBe("1.0");
      expect(result!.strings["app.title"].localizations.fr).toEqual({
        stringUnit: {
          state: "translated",
          value: "Mon App",
        },
      });
    });

    it("should push plural translations in plain object format", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "items.count": {
          one: "1 article",
          other: "%d articles",
        },
      };
      const result = await loader.push("fr", payload);
      expect(result).not.toBeNull();
      expect(result!.strings["items.count"].localizations.fr).toEqual({
        variations: {
          plural: {
            one: {
              stringUnit: {
                state: "translated",
                value: "1 article",
              },
            },
            other: {
              stringUnit: {
                state: "translated",
                value: "%d articles",
              },
            },
          },
        },
      });
    });

    it("should merge translations into existing input", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "app.title": "Mi App (actualizado)",
      };
      const result = await loader.push("es", payload);
      expect(result).not.toBeNull();
      // check new value
      expect(
        result!.strings["app.title"].localizations.es.stringUnit.value,
      ).toBe("Mi App (actualizado)");
      // check existing value is untouched
      expect(
        result!.strings["app.title"].localizations.en.stringUnit.value,
      ).toBe("My App");
    });

    it("should preserve the shouldTranslate: false flag", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "key.no-translate": "Ne pas traduire",
      };
      const result = await loader.push("fr", payload);
      expect(result).not.toBeNull();
      expect(result!.strings["key.no-translate"].shouldTranslate).toBe(false);
      expect(
        result!.strings["key.no-translate"].localizations.fr.stringUnit.value,
      ).toBe("Ne pas traduire");
    });

    it("should handle pushing to a null or undefined originalInput", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, { strings: {} });
      const payload = {
        greeting: "Hello",
      };
      const result = await loader.push("en", payload);
      expect(result).toEqual({
        strings: {
          greeting: {
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "Hello",
                },
              },
            },
          },
        },
      });
    });

    it("should skip null and undefined values in payload", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "app.title": "new title",
        "key.null": null,
        "key.undefined": undefined,
      };
      const result = await loader.push("en", payload);
      expect(result).not.toBeNull();
      expect(Object.keys(result!.strings)).not.toContain("key.null");
      expect(Object.keys(result!.strings)).not.toContain("key.undefined");
      expect(
        result!.strings["app.title"].localizations.en.stringUnit.value,
      ).toBe("new title");
    });

    it("should remove the pushed locale from original input", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);
      const payload = {
        "app.title": "new title",
      };
      const result = await loader.push("en", payload);
      expect(result).not.toBeNull();
      expect(result!.strings["app.title"].localizations.en.stringUnit).toEqual({
        state: "translated",
        value: "new title",
      });
      expect(result!.strings["items.count"].localizations.en).toBeUndefined();
      expect(
        result!.strings["key.no-translate"].localizations.en,
      ).toBeUndefined();
      expect(
        result!.strings["key.source-only"].localizations.en,
      ).toBeUndefined();
      expect(
        result!.strings["key.missing-localization"].localizations.en,
      ).toBeUndefined();
    });
  });

  describe("_removeLocale", () => {
    it("should remove the locale from the input", () => {
      const input = {
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {
              en: { stringUnit: { state: "translated", value: "Hello" } },
              es: { stringUnit: { state: "translated", value: "Hola" } },
            },
          },
          key2: {
            localizations: {
              en: { stringUnit: { state: "translated", value: "World" } },
              fr: { stringUnit: { state: "translated", value: "Monde" } },
            },
          },
          key3: {
            localizations: {
              en: {
                variations: {
                  plural: {
                    one: {
                      stringUnit: { state: "translated", value: "1 item" },
                    },
                  },
                },
              },
              fr: {
                variations: {
                  plural: {
                    one: {
                      stringUnit: { state: "translated", value: "1 article" },
                    },
                  },
                },
              },
            },
          },
        },
      };
      const result = _removeLocale(input, "en");
      expect(result).toEqual({
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {
              es: { stringUnit: { state: "translated", value: "Hola" } },
            },
          },
          key2: {
            localizations: {
              fr: { stringUnit: { state: "translated", value: "Monde" } },
            },
          },
          key3: {
            localizations: {
              fr: {
                variations: {
                  plural: {
                    one: {
                      stringUnit: { state: "translated", value: "1 article" },
                    },
                  },
                },
              },
            },
          },
        },
      });
    });

    it("should do nothing if the locale does not exist", () => {
      const input = {
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {
              en: { stringUnit: { state: "translated", value: "Hello" } },
              es: { stringUnit: { state: "translated", value: "Hola" } },
            },
          },
        },
      };
      const result = _removeLocale(input, "fr");
      expect(result).toEqual({
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {
              en: { stringUnit: { state: "translated", value: "Hello" } },
              es: { stringUnit: { state: "translated", value: "Hola" } },
            },
          },
        },
      });
    });

    it("should handle empty strings object", () => {
      const input = {
        sourceLanguage: "en",
        strings: {},
      };
      const result = _removeLocale(input, "en");
      expect(result).toEqual({
        sourceLanguage: "en",
        strings: {},
      });
    });

    it("should handle keys with no localizations", () => {
      const input = {
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {},
          },
        },
      };
      const result = _removeLocale(input, "en");
      expect(result).toEqual({
        sourceLanguage: "en",
        strings: {
          key1: {
            localizations: {},
          },
        },
      });
    });
  });

  describe("pullHints", () => {
    it("should extract comments from xcstrings format", async () => {
      const inputWithComments = {
        sourceLanguage: "en",
        strings: {
          welcome_message: {
            comment: "Greeting shown on the main screen",
            extractionState: "manual",
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "Welcome!",
                },
              },
            },
          },
          user_count: {
            comment: "Number of active users",
            extractionState: "manual",
            localizations: {
              en: {
                variations: {
                  plural: {
                    one: {
                      stringUnit: {
                        state: "translated",
                        value: "1 user",
                      },
                    },
                    other: {
                      stringUnit: {
                        state: "translated",
                        value: "%d users",
                      },
                    },
                  },
                },
              },
            },
          },
          no_comment_key: {
            extractionState: "manual",
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "No comment",
                },
              },
            },
          },
        },
      };

      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, inputWithComments);

      const hints = await loader.pullHints(inputWithComments);

      expect(hints).toEqual({
        welcome_message: { hint: "Greeting shown on the main screen" },
        user_count: { hint: "Number of active users" },
        "user_count/one": { hint: "Number of active users" },
        "user_count/other": { hint: "Number of active users" },
      });
    });

    it("should handle empty input", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);

      const hints1 = await loader.pullHints({});
      expect(hints1).toEqual({});

      const hints2 = await loader.pullHints(null as any);
      expect(hints2).toEqual({});

      const hints3 = await loader.pullHints(undefined as any);
      expect(hints3).toEqual({});
    });

    it("should handle xcstrings without comments", async () => {
      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, mockInput);

      const hints = await loader.pullHints(mockInput);
      expect(hints).toEqual({});
    });

    it("should handle strings with only some having comments", async () => {
      const inputWithMixedComments = {
        sourceLanguage: "en",
        strings: {
          with_comment: {
            comment: "This has a comment",
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "Value with comment",
                },
              },
            },
          },
          without_comment: {
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "Value without comment",
                },
              },
            },
          },
        },
      };

      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, inputWithMixedComments);

      const hints = await loader.pullHints(inputWithMixedComments);

      expect(hints).toEqual({
        with_comment: { hint: "This has a comment" },
      });
    });

    it("should handle multiple locales with same comment", async () => {
      const inputWithMultipleLocales = {
        sourceLanguage: "en",
        strings: {
          multi_locale: {
            comment: "Available in multiple languages",
            localizations: {
              en: {
                stringUnit: {
                  state: "translated",
                  value: "English",
                },
              },
              es: {
                stringUnit: {
                  state: "translated",
                  value: "Español",
                },
              },
              fr: {
                variations: {
                  plural: {
                    one: {
                      stringUnit: {
                        state: "translated",
                        value: "1 français",
                      },
                    },
                    other: {
                      stringUnit: {
                        state: "translated",
                        value: "%d français",
                      },
                    },
                  },
                },
              },
            },
          },
        },
      };

      const loader = createXcodeXcstringsLoader(defaultLocale);
      loader.setDefaultLocale(defaultLocale);
      await loader.pull(defaultLocale, inputWithMultipleLocales);

      const hints = await loader.pullHints(inputWithMultipleLocales);

      expect(hints).toEqual({
        multi_locale: { hint: "Available in multiple languages" },
        "multi_locale/one": { hint: "Available in multiple languages" },
        "multi_locale/other": { hint: "Available in multiple languages" },
      });
    });
  });
});

```
Page 11/16FirstPrevNextLast