#
tokens: 49113/50000 34/626 files (page 5/20)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 5 of 20. Use http://codebase.md/lingodotdev/lingo.dev?lines=true&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/cli/src/cli/loaders/unlocalizable.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, expect, it } from "vitest";
 2 | import createUnlocalizableLoader from "./unlocalizable";
 3 | 
 4 | describe("unlocalizable loader", () => {
 5 |   const data = {
 6 |     foo: "bar",
 7 |     num: 1,
 8 |     numStr: "1.0",
 9 |     empty: "",
10 |     boolTrue: true,
11 |     boolFalse: false,
12 |     boolStr: "false",
13 |     isoDate: "2025-02-21",
14 |     isoDateTime: "2025-02-21T00:00:00.000Z",
15 |     bar: "foo",
16 |     url: "https://example.com",
17 |     systemId: "Ab1cdefghijklmnopqrst2",
18 |   };
19 | 
20 |   it("should remove unlocalizable keys on pull", async () => {
21 |     const loader = createUnlocalizableLoader();
22 |     loader.setDefaultLocale("en");
23 |     const result = await loader.pull("en", data);
24 | 
25 |     expect(result).toEqual({
26 |       foo: "bar",
27 |       numStr: "1.0",
28 |       boolStr: "false",
29 |       bar: "foo",
30 |     });
31 |   });
32 | 
33 |   it("should handle unlocalizable keys on push", async () => {
34 |     const pushData = {
35 |       foo: "bar-es",
36 |       bar: "foo-es",
37 |       numStr: "2.0",
38 |       boolStr: "true",
39 |     };
40 | 
41 |     const loader = createUnlocalizableLoader();
42 |     loader.setDefaultLocale("en");
43 |     await loader.pull("en", data);
44 |     const result = await loader.push("es", pushData);
45 | 
46 |     expect(result).toEqual({ ...data, ...pushData });
47 |   });
48 | 
49 |   describe("return unlocalizable keys", () => {
50 |     describe.each([true, false])("%s", (returnUnlocalizedKeys) => {
51 |       it("should return unlocalizable keys on pull", async () => {
52 |         const loader = createUnlocalizableLoader(returnUnlocalizedKeys);
53 |         loader.setDefaultLocale("en");
54 |         const result = await loader.pull("en", data);
55 | 
56 |         const extraUnlocalizableData = returnUnlocalizedKeys
57 |           ? {
58 |               unlocalizable: {
59 |                 num: 1,
60 |                 empty: "",
61 |                 boolTrue: true,
62 |                 boolFalse: false,
63 |                 isoDate: "2025-02-21",
64 |                 isoDateTime: "2025-02-21T00:00:00.000Z",
65 |                 url: "https://example.com",
66 |                 systemId: "Ab1cdefghijklmnopqrst2",
67 |               },
68 |             }
69 |           : {};
70 | 
71 |         expect(result).toEqual({
72 |           foo: "bar",
73 |           numStr: "1.0",
74 |           boolStr: "false",
75 |           bar: "foo",
76 |           ...extraUnlocalizableData,
77 |         });
78 |       });
79 | 
80 |       it("should not affect push", async () => {
81 |         const pushData = {
82 |           foo: "bar-es",
83 |           bar: "foo-es",
84 |           numStr: "2.0",
85 |           boolStr: "true",
86 |         };
87 | 
88 |         const loader = createUnlocalizableLoader(returnUnlocalizedKeys);
89 |         loader.setDefaultLocale("en");
90 |         await loader.pull("en", data);
91 |         const result = await loader.push("es", pushData);
92 | 
93 |         const expectedData = { ...data, ...pushData };
94 |         expect(result).toEqual(expectedData);
95 |       });
96 |     });
97 |   });
98 | });
99 | 
```

--------------------------------------------------------------------------------
/integrations/directus/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
 1 | # @replexica/integration-directus
 2 | 
 3 | ## 0.1.10
 4 | 
 5 | ### Patch Changes
 6 | 
 7 | - [#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
 8 | 
 9 | ## 0.1.9
10 | 
11 | ### Patch Changes
12 | 
13 | - [`fc3cb88`](https://github.com/lingodotdev/lingo.dev/commit/fc3cb8839cbbb574b69087625dd5f97cf37d5d35) Thanks [@vrcprl](https://github.com/vrcprl)! - Updated README file with target languages changes
14 | 
15 | ## 0.1.8
16 | 
17 | ### Patch Changes
18 | 
19 | - [`2571fcd`](https://github.com/lingodotdev/lingo.dev/commit/2571fcdce6e969d9df96526188c9f3f89dd80677) Thanks [@vrcprl](https://github.com/vrcprl)! - Added multimple target languages option
20 | 
21 | ## 0.1.7
22 | 
23 | ### Patch Changes
24 | 
25 | - [`bd7c0a6`](https://github.com/lingodotdev/lingo.dev/commit/bd7c0a62ddfc5144690f6f572667a27d572e521a) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - update `@replexica/sdk` version
26 | 
27 | ## 0.1.6
28 | 
29 | ### Patch Changes
30 | 
31 | - [`e808190`](https://github.com/lingodotdev/lingo.dev/commit/e80819059b89f4a3f69980bab0979432cb7823bf) Thanks [@vrcprl](https://github.com/vrcprl)! - Fixed screenshot
32 | 
33 | - Updated dependencies []:
34 |   - @replexica/[email protected]
35 | 
36 | ## 0.1.5
37 | 
38 | ### Patch Changes
39 | 
40 | - [`ca7a085`](https://github.com/lingodotdev/lingo.dev/commit/ca7a085033ff31780001db1e6d58d818b60beded) Thanks [@vrcprl](https://github.com/vrcprl)! - Add README
41 | 
42 | ## 0.1.4
43 | 
44 | ### Patch Changes
45 | 
46 | - [`998a4a6`](https://github.com/lingodotdev/lingo.dev/commit/998a4a6267ff8542279a8f6d812d5579e3a78a42) Thanks [@vrcprl](https://github.com/vrcprl)! - Update primary key selection
47 | 
48 | ## 0.1.3
49 | 
50 | ### Patch Changes
51 | 
52 | - [`75253b6`](https://github.com/lingodotdev/lingo.dev/commit/75253b66833b000bf80d6880e92e3c60f5bcd068) Thanks [@vrcprl](https://github.com/vrcprl)! - Update replexica sdk version
53 | 
54 | ## 0.1.2
55 | 
56 | ### Patch Changes
57 | 
58 | - Updated dependencies []:
59 |   - @replexica/[email protected]
60 | 
61 | ## 0.1.1
62 | 
63 | ### Patch Changes
64 | 
65 | - [`22490ab`](https://github.com/lingodotdev/lingo.dev/commit/22490ab94f22d8e5769b23dc58d811fc8483f714) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - add Directus integration
66 | 
67 | ## 0.1.0
68 | 
69 | ### Minor Changes
70 | 
71 | - [`03b4506`](https://github.com/lingodotdev/lingo.dev/commit/03b45063f435715967825f70daf3f5bbdb05b9a0) Thanks [@vrcprl](https://github.com/vrcprl)! - Lingo.dev integration for Directus
72 | 
73 | ## 0.0.1
74 | 
75 | ### Patch Changes
76 | 
77 | - [#341](https://github.com/lingodotdev/lingo.dev/pull/341) [`1df47d6`](https://github.com/lingodotdev/lingo.dev/commit/1df47d6095f907e1d756524f5e2cc2e043fdb093) Thanks [@maxprilutskiy](https://github.com/maxprilutskiy)! - empty directus integration package
78 | 
```

--------------------------------------------------------------------------------
/scripts/packagist-publish.php:
--------------------------------------------------------------------------------

```php
 1 | <?php
 2 | /**
 3 |  * Packagist Publishing Script
 4 |  * 
 5 |  * This script handles publishing a package to Packagist using the Packagist API.
 6 |  * It requires the following environment variables:
 7 |  * - PACKAGIST_USERNAME: The Packagist username
 8 |  * - PACKAGIST_API_TOKEN: The Packagist API token
 9 |  * - PACKAGE_NAME: The name of the package to publish (e.g., vendor/package)
10 |  * 
11 |  * @php      7.4
12 |  */
13 | 
14 | $username = getenv('PACKAGIST_USERNAME');
15 | $apiToken = getenv('PACKAGIST_API_TOKEN');
16 | $packageName = getenv('PACKAGE_NAME');
17 | 
18 | if (!$username || !$apiToken || !$packageName) {
19 |     echo "Error: Missing required environment variables.\n";
20 |     echo "Please ensure PACKAGIST_USERNAME, PACKAGIST_API_TOKEN, and PACKAGE_NAME are set.\n";
21 |     exit(1);
22 | }
23 | 
24 | echo "Starting Packagist publishing process for package: $packageName\n";
25 | 
26 | $checkUrl = "https://packagist.org/packages/$packageName.json";
27 | $ch = curl_init($checkUrl);
28 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
29 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
30 | curl_setopt($ch, CURLOPT_HTTPHEADER, [
31 |     'Accept: application/json'
32 | ]);
33 | 
34 | echo "Checking if package exists on Packagist...\n";
35 | $response = curl_exec($ch);
36 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
37 | curl_close($ch);
38 | 
39 | $packageExists = ($httpCode === 200);
40 | 
41 | if ($packageExists) {
42 |     echo "Package $packageName already exists on Packagist. Updating...\n";
43 |     $apiUrl = "https://packagist.org/api/update-package?username=$username&apiToken=$apiToken";
44 | } else {
45 |     echo "Package $packageName does not exist on Packagist. Creating new package...\n";
46 |     $apiUrl = "https://packagist.org/api/create-package?username=$username&apiToken=$apiToken";
47 | }
48 | 
49 | $repoUrl = "https://github.com/lingodotdev/lingo.dev";
50 | 
51 | $data = [
52 |     'repository' => [
53 |         'url' => $repoUrl
54 |     ]
55 | ];
56 | 
57 | $ch = curl_init($apiUrl);
58 | 
59 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
60 | curl_setopt($ch, CURLOPT_POST, true);
61 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
62 | curl_setopt($ch, CURLOPT_HTTPHEADER, [
63 |     'Content-Type: application/json',
64 |     'Accept: application/json'
65 | ]);
66 | 
67 | echo "Sending request to Packagist API ($apiUrl)...\n";
68 | $response = curl_exec($ch);
69 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
70 | 
71 | if (curl_errno($ch)) {
72 |     echo "Error: " . curl_error($ch) . "\n";
73 |     curl_close($ch);
74 |     exit(1);
75 | }
76 | 
77 | curl_close($ch);
78 | 
79 | $responseData = json_decode($response, true);
80 | 
81 | echo "HTTP Response Code: $httpCode\n";
82 | echo "Response: " . print_r($responseData, true) . "\n";
83 | 
84 | if ($httpCode >= 200 && $httpCode < 300) {
85 |     echo "Package $packageName successfully " . ($packageExists ? "updated" : "submitted") . " to Packagist!\n";
86 |     exit(0);
87 | } else {
88 |     echo "Failed to " . ($packageExists ? "update" : "submit") . " package $packageName to Packagist.\n";
89 |     exit(1);
90 | }
91 | 
```

--------------------------------------------------------------------------------
/packages/cli/i18n.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "version": "1.10",
  3 |   "locale": {
  4 |     "source": "en",
  5 |     "targets": ["es"]
  6 |   },
  7 |   "buckets": {
  8 |     "xliff": {
  9 |       "include": ["demo/xliff/[locale]/*.xliff"]
 10 |     },
 11 |     "android": {
 12 |       "include": ["demo/android/[locale]/*.xml"]
 13 |     },
 14 |     "csv": {
 15 |       "include": ["demo/csv/example.csv"]
 16 |     },
 17 |     "ejs": {
 18 |       "include": ["demo/ejs/[locale]/*.ejs"]
 19 |     },
 20 |     "flutter": {
 21 |       "include": ["demo/flutter/[locale]/*.arb"]
 22 |     },
 23 |     "html": {
 24 |       "include": ["demo/html/[locale]/*.html"]
 25 |     },
 26 |     "json": {
 27 |       "include": ["demo/json/[locale]/example.json"],
 28 |       "lockedKeys": ["locked_key_1"],
 29 |       "ignoredKeys": ["ignored_key_1"]
 30 |     },
 31 |     "jsonc": {
 32 |       "include": ["demo/jsonc/[locale]/example.jsonc"],
 33 |       "lockedKeys": ["locked_key_1"],
 34 |       "ignoredKeys": ["ignored_key_1"]
 35 |     },
 36 |     "json5": {
 37 |       "include": ["demo/json5/[locale]/example.json5"]
 38 |     },
 39 |     "json-dictionary": {
 40 |       "include": ["demo/json-dictionary/example.json"]
 41 |     },
 42 |     "markdoc": {
 43 |       "include": ["demo/markdoc/[locale]/*.markdoc"]
 44 |     },
 45 |     "markdown": {
 46 |       "include": ["demo/markdown/[locale]/*.md"],
 47 |       "exclude": ["demo/markdown/[locale]/ignored.md"]
 48 |     },
 49 |     "mdx": {
 50 |       "include": ["demo/mdx/[locale]/*.mdx"],
 51 |       "lockedKeys": ["meta/locked_key_1"],
 52 |       "ignoredKeys": ["meta/ignored_key_1"]
 53 |     },
 54 |     "po": {
 55 |       "include": ["demo/po/[locale]/*.po"]
 56 |     },
 57 |     "properties": {
 58 |       "include": ["demo/properties/[locale]/*.properties"]
 59 |     },
 60 |     "srt": {
 61 |       "include": ["demo/srt/[locale]/*.srt"]
 62 |     },
 63 |     "txt": {
 64 |       "include": ["demo/txt/[locale]/*.txt"]
 65 |     },
 66 |     "typescript": {
 67 |       "include": ["demo/typescript/[locale]/*.ts"],
 68 |       "lockedKeys": ["forms/locked_key_1"],
 69 |       "ignoredKeys": ["forms/ignored_key_1"]
 70 |     },
 71 |     "vtt": {
 72 |       "include": ["demo/vtt/[locale]/*.vtt"]
 73 |     },
 74 |     "xcode-strings": {
 75 |       "include": ["demo/xcode-strings/[locale]/*.strings"]
 76 |     },
 77 |     "xcode-stringsdict": {
 78 |       "include": ["demo/xcode-stringsdict/[locale]/*.stringsdict"]
 79 |     },
 80 |     "xcode-xcstrings": {
 81 |       "include": ["demo/xcode-xcstrings/*.xcstrings"],
 82 |       "lockedKeys": ["api_key"],
 83 |       "ignoredKeys": ["item_count"]
 84 |     },
 85 |     "xcode-xcstrings-v2": {
 86 |       "include": ["demo/xcode-xcstrings-v2/*.xcstrings"]
 87 |     },
 88 |     "xml": {
 89 |       "include": ["demo/xml/[locale]/*.xml"]
 90 |     },
 91 |     "yaml": {
 92 |       "include": ["demo/yaml/[locale]/*.yml"],
 93 |       "lockedKeys": ["locked_key_1"],
 94 |       "ignoredKeys": ["ignored_key_1"]
 95 |     },
 96 |     "yaml-root-key": {
 97 |       "include": ["demo/yaml-root-key/[locale]/*.yml"]
 98 |     },
 99 |     "php": {
100 |       "include": ["demo/php/[locale]/*.php"]
101 |     },
102 |     "vue-json": {
103 |       "include": ["demo/vue-json/*.vue"]
104 |     }
105 |   },
106 |   "$schema": "https://lingo.dev/schema/i18n.json"
107 | }
108 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/utils/observability.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import pkg from "node-machine-id";
 2 | const { machineIdSync } = pkg;
 3 | import https from "https";
 4 | 
 5 | const POSTHOG_API_KEY = "phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk";
 6 | const POSTHOG_HOST = "eu.i.posthog.com";
 7 | const POSTHOG_PATH = "/i/v0/e/"; // Correct PostHog capture endpoint
 8 | const REQUEST_TIMEOUT_MS = 1000;
 9 | 
10 | /**
11 |  * Sends an analytics event to PostHog using direct HTTPS API.
12 |  * This is a fire-and-forget implementation that won't block the process.
13 |  *
14 |  * @param distinctId - Unique identifier for the user/device
15 |  * @param event - Name of the event to track
16 |  * @param properties - Additional properties to attach to the event
17 |  */
18 | export default function trackEvent(
19 |   distinctId: string | null | undefined,
20 |   event: string,
21 |   properties?: Record<string, any>,
22 | ): void {
23 |   // Skip tracking if explicitly disabled or in CI environment
24 |   if (process.env.DO_NOT_TRACK === "1") {
25 |     return;
26 |   }
27 | 
28 |   // Defer execution to next tick to avoid blocking
29 |   setImmediate(() => {
30 |     try {
31 |       const actualId = distinctId || `device-${machineIdSync()}`;
32 | 
33 |       // PostHog expects distinct_id at the root level, not nested in properties
34 |       const eventData = {
35 |         api_key: POSTHOG_API_KEY,
36 |         event,
37 |         distinct_id: actualId,
38 |         properties: {
39 |           ...properties,
40 |           $lib: "lingo.dev-cli",
41 |           $lib_version: process.env.npm_package_version || "unknown",
42 |           // Essential debugging context only
43 |           node_version: process.version,
44 |           is_ci: !!process.env.CI,
45 |           debug_enabled: process.env.DEBUG === "true",
46 |         },
47 |         timestamp: new Date().toISOString(),
48 |       };
49 | 
50 |       const payload = JSON.stringify(eventData);
51 | 
52 |       const options: https.RequestOptions = {
53 |         hostname: POSTHOG_HOST,
54 |         path: POSTHOG_PATH,
55 |         method: "POST",
56 |         headers: {
57 |           "Content-Type": "application/json",
58 |           "Content-Length": Buffer.byteLength(payload).toString(),
59 |         },
60 |         timeout: REQUEST_TIMEOUT_MS,
61 |       };
62 | 
63 |       const req = https.request(options);
64 | 
65 |       // Handle timeout by destroying the request
66 |       req.on("timeout", () => {
67 |         req.destroy();
68 |       });
69 | 
70 |       // Silently ignore errors to prevent crashes
71 |       req.on("error", (error) => {
72 |         if (process.env.DEBUG === "true") {
73 |           console.error("[Tracking] Error ignored:", error.message);
74 |         }
75 |       });
76 | 
77 |       // Send payload and close the request
78 |       req.write(payload);
79 |       req.end();
80 | 
81 |       // Ensure cleanup after timeout
82 |       setTimeout(() => {
83 |         if (!req.destroyed) {
84 |           req.destroy();
85 |         }
86 |       }, REQUEST_TIMEOUT_MS);
87 |     } catch (error) {
88 |       // Catch-all for any synchronous errors
89 |       if (process.env.DEBUG === "true") {
90 |         console.error("[Tracking] Failed to send event:", error);
91 |       }
92 |     }
93 |   });
94 | }
95 | 
```

--------------------------------------------------------------------------------
/packages/react/src/rsc/utils.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { cookies, headers } from "next/headers";
 2 | import { LOCALE_HEADER_NAME, LOCALE_COOKIE_NAME } from "../core";
 3 | 
 4 | /**
 5 |  * Gets the current locale code from the `"x-lingo-locale"` header.
 6 |  *
 7 |  * @returns Promise that resolves to the current locale code, or `"en"` if no header is found.
 8 |  *
 9 |  * @example Get locale from headers in a server component
10 |  * ```typescript
11 |  * import { loadLocaleFromHeaders } from "lingo.dev/react/rsc";
12 |  *
13 |  * export default async function ServerComponent() {
14 |  *   const locale = await loadLocaleFromHeaders();
15 |  *   return <div>Current locale: {locale}</div>;
16 |  * }
17 |  * ```
18 |  */
19 | export async function loadLocaleFromHeaders() {
20 |   const requestHeaders = await headers();
21 |   const result = requestHeaders.get(LOCALE_HEADER_NAME);
22 |   return result;
23 | }
24 | 
25 | /**
26 |  * Gets the current locale code from the `"lingo-locale"` cookie.
27 |  *
28 |  * @returns Promise that resolves to the current locale code, or `"en"` if no cookie is found.
29 |  *
30 |  * @example Get locale from cookies in a server component
31 |  * ```typescript
32 |  * import { loadLocaleFromCookies } from "lingo.dev/react/rsc";
33 |  *
34 |  * export default async function ServerComponent() {
35 |  *   const locale = await loadLocaleFromCookies();
36 |  *   return <div>User's saved locale: {locale}</div>;
37 |  * }
38 |  * ```
39 |  */
40 | export async function loadLocaleFromCookies() {
41 |   const requestCookies = await cookies();
42 |   const result = requestCookies.get(LOCALE_COOKIE_NAME)?.value || "en";
43 |   return result;
44 | }
45 | 
46 | /**
47 |  * Sets the current locale in the `"lingo-locale"` cookie.
48 |  *
49 |  * @param locale - The locale code to store in the cookie.
50 |  *
51 |  * @example Set locale in a server action
52 |  * ```typescript
53 |  * import { setLocaleInCookies } from "lingo.dev/react/rsc";
54 |  *
55 |  * export async function changeLocale(locale: string) {
56 |  *   "use server";
57 |  *   await setLocaleInCookies(locale);
58 |  *   redirect("/");
59 |  * }
60 |  * ```
61 |  */
62 | export async function setLocaleInCookies(locale: string) {
63 |   const requestCookies = await cookies();
64 |   requestCookies.set(LOCALE_COOKIE_NAME, locale);
65 | }
66 | 
67 | /**
68 |  * Loads the dictionary for the current locale.
69 |  *
70 |  * The current locale is determined by the `"lingo-locale"` cookie.
71 |  *
72 |  * @param loader - A callback function that loads the dictionary for the current locale.
73 |  *
74 |  * @returns Promise that resolves to the dictionary object containing localized content.
75 |  *
76 |  * @example Load dictionary from request in a server component
77 |  * ```typescript
78 |  * import { loadDictionaryFromRequest, loadDictionary } from "lingo.dev/react/rsc";
79 |  *
80 |  * export default async function ServerComponent() {
81 |  *   const dictionary = await loadDictionaryFromRequest(loadDictionary);
82 |  *   return <div>{dictionary.welcome}</div>;
83 |  * }
84 |  * ```
85 |  */
86 | export async function loadDictionaryFromRequest(
87 |   loader: (locale: string) => Promise<any>,
88 | ) {
89 |   const locale = await loadLocaleFromCookies();
90 |   return loader(locale);
91 | }
92 | 
```

--------------------------------------------------------------------------------
/packages/react/src/react-router/loader.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { LOCALE_COOKIE_NAME, getDictionary } from "../core";
  2 | 
  3 | /**
  4 |  * A placeholder function for loading dictionaries that contain localized content.
  5 |  *
  6 |  * This function:
  7 |  *
  8 |  * - Should be used in React Router and Remix applications
  9 |  * - Should be passed into the `LingoProvider` component
 10 |  * - Is transformed into functional code by Lingo.dev Compiler
 11 |  *
 12 |  * @param requestOrExplicitLocale - Either a `Request` object (from loader functions) or an explicit locale string.
 13 |  *
 14 |  * @returns Promise that resolves to the dictionary object containing localized content.
 15 |  *
 16 |  * @example Use in a React Router application
 17 |  * ```tsx
 18 |  * import { LingoProvider } from "lingo.dev/react/client";
 19 |  * import { loadDictionary } from "lingo.dev/react/react-router";
 20 |  * import {
 21 |  *   Links,
 22 |  *   Meta,
 23 |  *   Outlet,
 24 |  *   Scripts,
 25 |  *   ScrollRestoration,
 26 |  *   useLoaderData,
 27 |  *   type LoaderFunctionArgs,
 28 |  * } from "react-router";
 29 |  * import "./app.css";
 30 |  *
 31 |  * export const loader = async ({ request }: LoaderFunctionArgs) => {
 32 |  *   return {
 33 |  *     lingoDictionary: await loadDictionary(request),
 34 |  *   };
 35 |  * };
 36 |  *
 37 |  * export function Layout({ children }: { children: React.ReactNode }) {
 38 |  *   const { lingoDictionary } = useLoaderData<typeof loader>();
 39 |  *
 40 |  *   return (
 41 |  *     <LingoProvider dictionary={lingoDictionary}>
 42 |  *       <html lang="en">
 43 |  *         <head>
 44 |  *           <meta charSet="utf-8" />
 45 |  *           <meta name="viewport" content="width=device-width, initial-scale=1" />
 46 |  *           <Meta />
 47 |  *           <Links />
 48 |  *         </head>
 49 |  *         <body>
 50 |  *           {children}
 51 |  *           <ScrollRestoration />
 52 |  *           <Scripts />
 53 |  *         </body>
 54 |  *       </html>
 55 |  *     </LingoProvider>
 56 |  *   );
 57 |  * }
 58 |  *
 59 |  * export default function App() {
 60 |  *   return <Outlet />;
 61 |  * }
 62 |  * ```
 63 |  */
 64 | export const loadDictionary = async (
 65 |   requestOrExplicitLocale: Request | string,
 66 | ): Promise<any> => {
 67 |   return null;
 68 | };
 69 | 
 70 | function loadLocaleFromCookies(request: Request) {
 71 |   // it's a Request, so get the Cookie header
 72 |   const cookieHeaderValue = request.headers.get("Cookie");
 73 | 
 74 |   // there's no Cookie header, so return null
 75 |   if (!cookieHeaderValue) {
 76 |     return null;
 77 |   }
 78 | 
 79 |   // get the lingo-locale cookie
 80 |   const cookiePrefix = `${LOCALE_COOKIE_NAME}=`;
 81 |   const cookie = cookieHeaderValue
 82 |     .split(";")
 83 |     .find((cookie) => cookie.trim().startsWith(cookiePrefix));
 84 | 
 85 |   // there's no lingo-locale cookie, so return null
 86 |   if (!cookie) {
 87 |     return null;
 88 |   }
 89 | 
 90 |   // extract the locale value from the cookie
 91 |   return cookie.trim().substring(cookiePrefix.length);
 92 | }
 93 | 
 94 | export async function loadDictionary_internal(
 95 |   requestOrExplicitLocale: Request | string,
 96 |   dictionaryLoaders: Record<string, () => Promise<any>>,
 97 | ) {
 98 |   // gets the locale (falls back to "en")
 99 |   const locale =
100 |     typeof requestOrExplicitLocale === "string"
101 |       ? requestOrExplicitLocale
102 |       : loadLocaleFromCookies(requestOrExplicitLocale);
103 | 
104 |   return getDictionary(locale, dictionaryLoaders);
105 | }
106 | 
```

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

```typescript
  1 | import { parse } from "csv-parse/sync";
  2 | import { stringify } from "csv-stringify/sync";
  3 | import _ from "lodash";
  4 | import { ILoader } from "./_types";
  5 | import { composeLoaders, createLoader } from "./_utils";
  6 | 
  7 | /**
  8 |  * Tries to detect the key column name from a csvString.
  9 |  *
 10 |  * Current logic: get first cell > 'KEY' fallback if empty
 11 |  */
 12 | export function detectKeyColumnName(csvString: string) {
 13 |   const row: string[] | undefined = parse(csvString)[0];
 14 |   const firstColumn = row?.[0]?.trim();
 15 |   return firstColumn || "KEY";
 16 | }
 17 | 
 18 | export default function createCsvLoader() {
 19 |   return composeLoaders(_createCsvLoader(), createPullOutputCleaner());
 20 | }
 21 | 
 22 | type InternalTransferState = {
 23 |   keyColumnName: string;
 24 |   inputParsed: Record<string, any>[];
 25 |   items: Record<string, string>;
 26 | };
 27 | 
 28 | function _createCsvLoader(): ILoader<string, InternalTransferState> {
 29 |   return createLoader({
 30 |     async pull(locale, input) {
 31 |       const keyColumnName = detectKeyColumnName(
 32 |         input.split("\n").find((l) => l.length)!,
 33 |       );
 34 |       const inputParsed = parse(input, {
 35 |         columns: true,
 36 |         skip_empty_lines: true,
 37 |         relax_column_count_less: true,
 38 |       }) as Record<string, any>[];
 39 | 
 40 |       const items: Record<string, string> = {};
 41 | 
 42 |       // Assign keys that already have translation so AI doesn't re-generate it.
 43 |       _.forEach(inputParsed, (row) => {
 44 |         const key = row[keyColumnName];
 45 |         if (key && row[locale] && row[locale].trim() !== "") {
 46 |           items[key] = row[locale];
 47 |         }
 48 |       });
 49 | 
 50 |       return {
 51 |         inputParsed,
 52 |         keyColumnName,
 53 |         items,
 54 |       };
 55 |     },
 56 |     async push(locale, { inputParsed, keyColumnName, items }) {
 57 |       const columns =
 58 |         inputParsed.length > 0
 59 |           ? Object.keys(inputParsed[0])
 60 |           : [keyColumnName, locale];
 61 |       if (!columns.includes(locale)) {
 62 |         columns.push(locale);
 63 |       }
 64 | 
 65 |       const updatedRows = inputParsed.map((row) => ({
 66 |         ...row,
 67 |         [locale]: items[row[keyColumnName]] || row[locale] || "",
 68 |       }));
 69 |       const existingKeys = new Set(
 70 |         inputParsed.map((row) => row[keyColumnName]),
 71 |       );
 72 | 
 73 |       Object.entries(items).forEach(([key, value]) => {
 74 |         if (!existingKeys.has(key)) {
 75 |           const newRow: Record<string, string> = {
 76 |             [keyColumnName]: key,
 77 |             ...Object.fromEntries(columns.map((column) => [column, ""])),
 78 |           };
 79 |           newRow[locale] = value;
 80 |           updatedRows.push(newRow);
 81 |         }
 82 |       });
 83 | 
 84 |       return stringify(updatedRows, {
 85 |         header: true,
 86 |         columns,
 87 |       });
 88 |     },
 89 |   });
 90 | }
 91 | 
 92 | /**
 93 |  * This is a simple extra loader that is used to clean the data written to lockfile
 94 |  */
 95 | function createPullOutputCleaner(): ILoader<
 96 |   InternalTransferState,
 97 |   Record<string, string>
 98 | > {
 99 |   return createLoader({
100 |     async pull(_locale, input) {
101 |       return input.items;
102 |     },
103 |     async push(_locale, data, _oI, _oL, pullInput) {
104 |       return { ...pullInput!, items: data };
105 |     },
106 |   });
107 | }
108 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/locked-patterns.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ILoader } from "./_types";
  2 | import { createLoader } from "./_utils";
  3 | import { md5 } from "../utils/md5";
  4 | 
  5 | /**
  6 |  * Extracts content matching regex patterns and replaces it with placeholders.
  7 |  * Returns the transformed content and a mapping of placeholders to original content.
  8 |  */
  9 | function extractLockedPatterns(
 10 |   content: string,
 11 |   patterns: string[] = [],
 12 | ): {
 13 |   content: string;
 14 |   lockedPlaceholders: Record<string, string>;
 15 | } {
 16 |   let finalContent = content;
 17 |   const lockedPlaceholders: Record<string, string> = {};
 18 | 
 19 |   if (!patterns || patterns.length === 0) {
 20 |     return { content: finalContent, lockedPlaceholders };
 21 |   }
 22 | 
 23 |   for (const patternStr of patterns) {
 24 |     try {
 25 |       const pattern = new RegExp(patternStr, "gm");
 26 |       const matches = Array.from(finalContent.matchAll(pattern));
 27 | 
 28 |       for (const match of matches) {
 29 |         const matchedText = match[0];
 30 |         const matchHash = md5(matchedText);
 31 |         const placeholder = `---LOCKED-PATTERN-${matchHash}---`;
 32 | 
 33 |         lockedPlaceholders[placeholder] = matchedText;
 34 |         finalContent = finalContent.replace(matchedText, placeholder);
 35 |       }
 36 |     } catch (error) {
 37 |       console.warn(`Invalid regex pattern: ${patternStr}`);
 38 |     }
 39 |   }
 40 | 
 41 |   return {
 42 |     content: finalContent,
 43 |     lockedPlaceholders,
 44 |   };
 45 | }
 46 | 
 47 | /**
 48 |  * Creates a loader that preserves content matching regex patterns during translation.
 49 |  *
 50 |  * This loader extracts content matching the provided regex patterns and replaces it
 51 |  * with placeholders before translation. After translation, the placeholders are
 52 |  * restored with the original content.
 53 |  *
 54 |  * This is useful for preserving technical terms, code snippets, URLs, template
 55 |  * variables, and other non-translatable content within translatable files.
 56 |  *
 57 |  * Works with any string-based format (JSON, YAML, XML, Markdown, HTML, etc.).
 58 |  * Note: For structured formats (JSON, XML, YAML), ensure patterns only match
 59 |  * content within values, not structural syntax, to avoid breaking parsing.
 60 |  *
 61 |  * @param defaultPatterns - Array of regex pattern strings to match and preserve
 62 |  * @returns A loader that handles pattern locking/unlocking
 63 |  */
 64 | export default function createLockedPatternsLoader(
 65 |   defaultPatterns?: string[],
 66 | ): ILoader<string, string> {
 67 |   return createLoader({
 68 |     async pull(locale, input, initCtx, originalLocale) {
 69 |       const patterns = defaultPatterns || [];
 70 | 
 71 |       const { content } = extractLockedPatterns(input || "", patterns);
 72 | 
 73 |       return content;
 74 |     },
 75 | 
 76 |     async push(
 77 |       locale,
 78 |       data,
 79 |       originalInput,
 80 |       originalLocale,
 81 |       pullInput,
 82 |       pullOutput,
 83 |     ) {
 84 |       const patterns = defaultPatterns || [];
 85 | 
 86 |       if (!pullInput) {
 87 |         return data;
 88 |       }
 89 | 
 90 |       const { lockedPlaceholders } = extractLockedPatterns(
 91 |         pullInput as string,
 92 |         patterns,
 93 |       );
 94 | 
 95 |       let result = data;
 96 |       for (const [placeholder, original] of Object.entries(
 97 |         lockedPlaceholders,
 98 |       )) {
 99 |         result = result.replaceAll(placeholder, original);
100 |       }
101 | 
102 |       return result;
103 |     },
104 |   });
105 | }
106 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/lib/lcp/api/xml2obj.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import { xml2obj, obj2xml } from "./xml2obj";
  3 | 
  4 | function normalize(xml: string) {
  5 |   return xml.replace(/\s+/g, " ").trim();
  6 | }
  7 | 
  8 | describe("xml2obj / obj2xml", () => {
  9 |   it("should convert simple XML to object with key attributes", () => {
 10 |     const xml = `
 11 |       <object>
 12 |         <object key="user">
 13 |           <value key="id">123</value>
 14 |           <value key="dataValue">abc</value>
 15 |           <value key="firstName">John</value>
 16 |           <value key="lastName">Doe</value>
 17 |         </object>
 18 |       </object>
 19 |     `;
 20 |     const obj = xml2obj(xml);
 21 |     expect(obj).toEqual({
 22 |       user: {
 23 |         id: 123,
 24 |         dataValue: "abc",
 25 |         firstName: "John",
 26 |         lastName: "Doe",
 27 |       },
 28 |     });
 29 |   });
 30 | 
 31 |   it("should preserve complex structures through round-trip conversion", () => {
 32 |     const original = {
 33 |       root: {
 34 |         id: 123,
 35 |         name: "John & Jane <> \" '",
 36 |         notes: "Line1\nLine2",
 37 |         isActive: true,
 38 |         tags: {
 39 |           tag: ["a & b", "c < d"],
 40 |         },
 41 |         nestedObj: {
 42 |           childId: 456,
 43 |           weirdSymbols: "@#$%^&*()_+",
 44 |         },
 45 |         items: {
 46 |           item: [
 47 |             { keyOne: "value1", keyTwo: "value2" },
 48 |             { keyOne: "value3", keyTwo: "value4" },
 49 |           ],
 50 |         },
 51 |       },
 52 |     } as const;
 53 | 
 54 |     const result = xml2obj(obj2xml(original));
 55 |     expect(result).toEqual(original);
 56 |   });
 57 | 
 58 |   it("should handle empty elements, arrays and self-closing tags", () => {
 59 |     const original = `
 60 |       <object>
 61 |         <value key="products" />
 62 |         <array key="prices">
 63 |           <value>1.99</value>
 64 |           <value>9.99</value>
 65 |         </array>
 66 |       </object>
 67 |     `;
 68 |     const expected = {
 69 |       products: "",
 70 |       prices: [1.99, 9.99],
 71 |     };
 72 |     expect(xml2obj(original)).toEqual(expected);
 73 |   });
 74 | 
 75 |   it("should correctly escape special characters when building XML", () => {
 76 |     const original = { message: "5 < 6 & 7 > 4" } as const;
 77 |     const result = xml2obj(obj2xml(original));
 78 |     expect(result).toEqual(original);
 79 |   });
 80 | 
 81 |   it("check 1", () => {
 82 |     const original = `<?xml version="1.0" encoding="UTF-8"?>
 83 | <object>
 84 |  <value key="version">0.1.1</value>
 85 |  <value key="locale">ja</value>
 86 |  <object key="files">
 87 |  <object key="routes/($locale).z.tsx">
 88 |  <object key="entries">
 89 |  <value key="1/declaration/body/3/argument">&lt;element:select&gt;&lt;element:option&gt;使用済み&lt;/element:option&gt;&lt;element:option&gt;合計&lt;/element:option&gt;&lt;/element:select&gt; 🚀 あなたの使用状況: {wordType} {subscription.words[wordType]}</value>
 90 |  </object>
 91 |  </object>
 92 |  </object>
 93 | </object>`;
 94 | 
 95 |     const result = xml2obj(original);
 96 |     expect(result).toEqual({
 97 |       version: "0.1.1",
 98 |       locale: "ja",
 99 |       files: {
100 |         "routes/($locale).z.tsx": {
101 |           entries: {
102 |             "1/declaration/body/3/argument":
103 |               "<element:select><element:option>使用済み</element:option><element:option>合計</element:option></element:select> 🚀 あなたの使用状況: {wordType} {subscription.words[wordType]}",
104 |           },
105 |         },
106 |       },
107 |     });
108 |   });
109 | });
110 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/cmd/show/_shared-key-command.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { resolveOverriddenLocale, I18nConfig } from "@lingo.dev/_spec";
  2 | import createBucketLoader from "../../loaders";
  3 | import {
  4 |   matchesKeyPattern,
  5 |   formatDisplayValue,
  6 | } from "../../utils/key-matching";
  7 | 
  8 | export type KeyFilterType = "lockedKeys" | "ignoredKeys";
  9 | 
 10 | export interface KeyCommandOptions {
 11 |   bucket?: string;
 12 | }
 13 | 
 14 | export interface KeyCommandConfig {
 15 |   filterType: KeyFilterType;
 16 |   displayName: string; // e.g., "locked", "ignored"
 17 | }
 18 | 
 19 | export async function executeKeyCommand(
 20 |   i18nConfig: I18nConfig,
 21 |   buckets: any[],
 22 |   options: KeyCommandOptions,
 23 |   config: KeyCommandConfig,
 24 | ): Promise<void> {
 25 |   let hasAnyKeys = false;
 26 | 
 27 |   for (const bucket of buckets) {
 28 |     // Filter by bucket name if specified
 29 |     if (options.bucket && bucket.type !== options.bucket) {
 30 |       continue;
 31 |     }
 32 | 
 33 |     // Skip buckets without the specified key patterns
 34 |     const keyPatterns = bucket[config.filterType];
 35 |     if (!keyPatterns || keyPatterns.length === 0) {
 36 |       continue;
 37 |     }
 38 | 
 39 |     hasAnyKeys = true;
 40 | 
 41 |     console.log(`\nBucket: ${bucket.type}`);
 42 |     console.log(
 43 |       `${capitalize(config.displayName)} key patterns: ${keyPatterns.join(", ")}`,
 44 |     );
 45 | 
 46 |     for (const bucketConfig of bucket.paths) {
 47 |       const sourceLocale = resolveOverriddenLocale(
 48 |         i18nConfig.locale.source,
 49 |         bucketConfig.delimiter,
 50 |       );
 51 |       const sourcePath = bucketConfig.pathPattern.replace(
 52 |         /\[locale\]/g,
 53 |         sourceLocale,
 54 |       );
 55 | 
 56 |       try {
 57 |         // Create a loader to read the source file
 58 |         const loader = createBucketLoader(
 59 |           bucket.type,
 60 |           bucketConfig.pathPattern,
 61 |           {
 62 |             defaultLocale: sourceLocale,
 63 |             injectLocale: bucket.injectLocale,
 64 |           },
 65 |           [], // Don't apply any filtering when reading
 66 |           [],
 67 |           [],
 68 |         );
 69 |         loader.setDefaultLocale(sourceLocale);
 70 | 
 71 |         // Read the source file content
 72 |         const data = await loader.pull(sourceLocale);
 73 | 
 74 |         if (!data || Object.keys(data).length === 0) {
 75 |           continue;
 76 |         }
 77 | 
 78 |         // Filter keys that match the patterns
 79 |         const matchedEntries = Object.entries(data).filter(([key]) =>
 80 |           matchesKeyPattern(key, keyPatterns),
 81 |         );
 82 | 
 83 |         if (matchedEntries.length > 0) {
 84 |           console.log(`\nMatches in ${sourcePath}:`);
 85 |           for (const [key, value] of matchedEntries) {
 86 |             const displayValue = formatDisplayValue(value);
 87 |             console.log(`  - ${key}: ${displayValue}`);
 88 |           }
 89 |           console.log(
 90 |             `Total: ${matchedEntries.length} ${config.displayName} key(s)`,
 91 |           );
 92 |         }
 93 |       } catch (error: any) {
 94 |         console.error(`  Error reading ${sourcePath}: ${error.message}`);
 95 |       }
 96 |     }
 97 |   }
 98 | 
 99 |   if (!hasAnyKeys) {
100 |     if (options.bucket) {
101 |       console.log(
102 |         `No ${config.displayName} keys configured for bucket: ${options.bucket}`,
103 |       );
104 |     } else {
105 |       console.log(`No ${config.displayName} keys configured in any bucket.`);
106 |     }
107 |   }
108 | }
109 | 
110 | function capitalize(str: string): string {
111 |   return str.charAt(0).toUpperCase() + str.slice(1);
112 | }
113 | 
```

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

```typescript
  1 | import { ILoader, ILoaderDefinition } from "./_types";
  2 | 
  3 | export function composeLoaders(
  4 |   ...loaders: ILoader<any, any, any>[]
  5 | ): ILoader<any, any> {
  6 |   return {
  7 |     init: async () => {
  8 |       for (const loader of loaders) {
  9 |         await loader.init?.();
 10 |       }
 11 |     },
 12 |     setDefaultLocale(locale: string) {
 13 |       for (const loader of loaders) {
 14 |         loader.setDefaultLocale?.(locale);
 15 |       }
 16 |       return this;
 17 |     },
 18 |     pull: async (locale, input) => {
 19 |       let result: any = input;
 20 |       for (let i = 0; i < loaders.length; i++) {
 21 |         result = await loaders[i].pull(locale, result);
 22 |       }
 23 |       return result;
 24 |     },
 25 |     push: async (locale, data) => {
 26 |       let result: any = data;
 27 |       for (let i = loaders.length - 1; i >= 0; i--) {
 28 |         result = await loaders[i].push(locale, result);
 29 |       }
 30 |       return result;
 31 |     },
 32 |     pullHints: async (originalInput) => {
 33 |       let result: any = originalInput;
 34 |       for (let i = 0; i < loaders.length; i++) {
 35 |         const subResult = await loaders[i].pullHints?.(result);
 36 |         if (subResult) {
 37 |           result = subResult;
 38 |         }
 39 |       }
 40 |       return result;
 41 |     },
 42 |   };
 43 | }
 44 | 
 45 | export function createLoader<I, O, C>(
 46 |   lDefinition: ILoaderDefinition<I, O, C>,
 47 | ): ILoader<I, O, C> {
 48 |   const state = {
 49 |     defaultLocale: undefined as string | undefined,
 50 |     originalInput: undefined as I | undefined | null,
 51 |     pullInput: undefined as I | undefined | null,
 52 |     pullOutput: undefined as O | undefined | null,
 53 |     initCtx: undefined as C | undefined,
 54 |   };
 55 |   return {
 56 |     async init() {
 57 |       if (state.initCtx) {
 58 |         return state.initCtx;
 59 |       }
 60 |       state.initCtx = await lDefinition.init?.();
 61 |       return state.initCtx as C;
 62 |     },
 63 |     setDefaultLocale(locale) {
 64 |       if (state.defaultLocale) {
 65 |         throw new Error("Default locale already set");
 66 |       }
 67 |       state.defaultLocale = locale;
 68 |       return this;
 69 |     },
 70 |     async pullHints() {
 71 |       return lDefinition.pullHints?.(state.originalInput!);
 72 |     },
 73 |     async pull(locale, input) {
 74 |       if (!state.defaultLocale) {
 75 |         throw new Error("Default locale not set");
 76 |       }
 77 |       if (state.originalInput === undefined && locale !== state.defaultLocale) {
 78 |         throw new Error("The first pull must be for the default locale");
 79 |       }
 80 |       if (locale === state.defaultLocale) {
 81 |         state.originalInput = input || null;
 82 |       }
 83 | 
 84 |       state.pullInput = input;
 85 |       const result = await lDefinition.pull(
 86 |         locale,
 87 |         input,
 88 |         state.initCtx!,
 89 |         state.defaultLocale,
 90 |         state.originalInput!,
 91 |       );
 92 |       state.pullOutput = result;
 93 | 
 94 |       return result;
 95 |     },
 96 |     async push(locale, data) {
 97 |       if (!state.defaultLocale) {
 98 |         throw new Error("Default locale not set");
 99 |       }
100 |       if (state.originalInput === undefined) {
101 |         throw new Error("Cannot push data without pulling first");
102 |       }
103 | 
104 |       const pushResult = await lDefinition.push(
105 |         locale,
106 |         data,
107 |         state.originalInput,
108 |         state.defaultLocale,
109 |         state.pullInput!,
110 |         state.pullOutput!,
111 |       );
112 |       return pushResult;
113 |     },
114 |   };
115 | }
116 | 
```

--------------------------------------------------------------------------------
/.claude/commands/analyze-bucket-type.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | argument-hint: <bucket-type>
 3 | description: Analyze a bucket type implementation to identify all behaviors and configurations
 4 | ---
 5 | 
 6 | Given the bucket type ID "$ARGUMENTS" (e.g., "json", "mdx", "typescript"), analyze the implementation code to identify ALL bucket-specific behaviors, configurations, and characteristics.
 7 | 
 8 | ## Instructions
 9 | 
10 | 1. **Locate where this bucket type is processed** in the codebase by searching for the bucket type string. Start with the main loader composition/pipeline code.
11 | 
12 | 2. **Trace the complete execution pipeline** for this bucket:
13 | 
14 |    - List every function/loader in the processing chain, in order
15 |    - For each function/loader, read its implementation to understand:
16 |      - Input parameters it receives
17 |      - Transformations it performs on the data
18 |      - Output format it produces
19 |      - Any side effects or file operations
20 | 
21 | 3. **Identify configuration parameters** by:
22 | 
23 |    - Finding which variables are passed into the loaders (e.g., lockedKeys, ignoredKeys)
24 |    - Tracing these variables back to their source (configuration parsing)
25 |    - Determining if they're bucket-specific or universal
26 | 
27 | 4. **Analyze file I/O behavior**:
28 | 
29 |    - How are file paths constructed?
30 |    - Does the path pattern contain locale placeholders that would create separate files?
31 |    - What file operations are performed (read, write, create, delete)?
32 |    - Are files overwritten or are new files created?
33 |    - **IMPORTANT**: Note that "overwrites existing files completely" and "[locale] placeholder support" are mutually exclusive in practice:
34 |      - If a bucket type stores all locales in a single file (like CSV with columns per locale), it overwrites that single file and does NOT support `[locale]` placeholders
35 |      - If a bucket type creates separate files per locale using `[locale]` placeholders, each locale file is overwritten individually
36 |      - Clarify which pattern the bucket type follows
37 | 
38 | 5. **Examine data transformation logic**:
39 | 
40 |    - How is the file content parsed?
41 |    - What internal data structures are used?
42 |    - How is the data serialized back to file format?
43 |    - Are there any format-preserving mechanisms?
44 | 
45 | 6. **Identify special behaviors** by examining:
46 | 
47 |    - Conditional logic specific to this bucket
48 |    - Error handling unique to this format
49 |    - Any validation or normalization steps
50 |    - Interactions between multiple loaders in the pipeline
51 | 
52 | 7. **Determine constraints and capabilities**:
53 | 
54 |    - What data types/structures are supported?
55 |    - Are there any size or complexity limitations?
56 |    - What happens with edge cases (empty files, malformed content)?
57 | 
58 | ## Required Depth
59 | 
60 | - Read the ACTUAL implementation of each loader/function
61 | - Follow all function calls to understand the complete flow
62 | - Don't make assumptions - verify behavior in the code
63 | - Consider the order of operations in the pipeline
64 | 
65 | ## Output Format
66 | 
67 | List all findings categorized as:
68 | 
69 | - Configuration parameters (with their types and defaults)
70 | - Processing pipeline (ordered list of transformations)
71 | - File handling behavior
72 | - Data transformation characteristics
73 | - Special capabilities or limitations
74 | - Edge case handling
75 | 
```

--------------------------------------------------------------------------------
/packages/spec/src/config.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import {
  3 |   parseI18nConfig,
  4 |   defaultConfig,
  5 |   LATEST_CONFIG_DEFINITION,
  6 | } from "./config";
  7 | 
  8 | // Helper function to create a v0 config
  9 | const createV0Config = () => ({
 10 |   version: 0,
 11 | });
 12 | 
 13 | // Helper function to create a v1 config
 14 | const createV1Config = () => ({
 15 |   version: 1,
 16 |   locale: {
 17 |     source: "en",
 18 |     targets: ["es"],
 19 |   },
 20 |   buckets: {
 21 |     "src/ui/[locale]/.json": "json",
 22 |     "src/blog/[locale]/*.md": "markdown",
 23 |   },
 24 | });
 25 | 
 26 | // Helper function to create a v1.1 config
 27 | const createV1_1Config = () => ({
 28 |   version: 1.1,
 29 |   locale: {
 30 |     source: "en",
 31 |     targets: ["es", "fr", "pt-PT", "pt_BR"],
 32 |   },
 33 |   buckets: {
 34 |     json: {
 35 |       include: ["src/ui/[locale]/.json"],
 36 |     },
 37 |     markdown: {
 38 |       include: ["src/blog/[locale]/*.md"],
 39 |       exclude: ["src/blog/[locale]/drafts.md"],
 40 |     },
 41 |   },
 42 | });
 43 | 
 44 | const createV1_2Config = () => ({
 45 |   ...createV1_1Config(),
 46 |   version: 1.2,
 47 | });
 48 | 
 49 | const createV1_3Config = () => ({
 50 |   ...createV1_2Config(),
 51 |   version: 1.3,
 52 | });
 53 | 
 54 | const createV1_4Config = () => ({
 55 |   ...createV1_3Config(),
 56 |   version: 1.4,
 57 |   $schema: "https://lingo.dev/schema/i18n.json",
 58 | });
 59 | 
 60 | const createInvalidLocaleConfig = () => ({
 61 |   version: 1,
 62 |   locale: {
 63 |     source: "bbbb",
 64 |     targets: ["es", "aaaa"],
 65 |   },
 66 |   buckets: {
 67 |     "src/ui/[locale]/.json": "json",
 68 |     "src/blog/[locale]/*.md": "markdown",
 69 |   },
 70 | });
 71 | 
 72 | describe("I18n Config Parser", () => {
 73 |   it("should upgrade v0 config to latest version", () => {
 74 |     const v0Config = createV0Config();
 75 |     const result = parseI18nConfig(v0Config);
 76 | 
 77 |     expect(result["$schema"]).toBeDefined();
 78 |     expect(result.version).toBe(LATEST_CONFIG_DEFINITION.defaultValue.version);
 79 |     expect(result.locale).toEqual(defaultConfig.locale);
 80 |     expect(result.buckets).toEqual({});
 81 |   });
 82 | 
 83 |   it("should upgrade v1 config to latest version", () => {
 84 |     const v1Config = createV1Config();
 85 |     const result = parseI18nConfig(v1Config);
 86 | 
 87 |     expect(result["$schema"]).toBeDefined();
 88 |     expect(result.version).toBe(LATEST_CONFIG_DEFINITION.defaultValue.version);
 89 |     expect(result.locale).toEqual(v1Config.locale);
 90 |     expect(result.buckets).toEqual({
 91 |       json: {
 92 |         include: ["src/ui/[locale]/.json"],
 93 |       },
 94 |       markdown: {
 95 |         include: ["src/blog/[locale]/*.md"],
 96 |       },
 97 |     });
 98 |   });
 99 | 
100 |   it("should handle empty config and use defaults", () => {
101 |     const emptyConfig = {};
102 |     const result = parseI18nConfig(emptyConfig);
103 | 
104 |     expect(result).toEqual(defaultConfig);
105 |   });
106 | 
107 |   it("should ignore extra fields in the config", () => {
108 |     const configWithExtra = {
109 |       ...createV1_4Config(),
110 |       extraField: "should be ignored",
111 |     };
112 |     const result = parseI18nConfig(configWithExtra);
113 | 
114 |     expect(result).not.toHaveProperty("extraField");
115 |     expect(result).toEqual(createV1_4Config());
116 |   });
117 | 
118 |   it("should throw an error for unsupported locales", () => {
119 |     const invalidLocaleConfig = createInvalidLocaleConfig();
120 |     expect(() => parseI18nConfig(invalidLocaleConfig)).toThrow(
121 |       `\nUnsupported locale: ${invalidLocaleConfig.locale.source}\nUnsupported locale: ${invalidLocaleConfig.locale.targets[1]}`,
122 |     );
123 |   });
124 | });
125 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/utils/jsx-content-whitespace.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from "vitest";
 2 | import { extractJsxContent } from "./jsx-content";
 3 | import * as t from "@babel/types";
 4 | import traverse, { NodePath } from "@babel/traverse";
 5 | import { parse } from "@babel/parser";
 6 | 
 7 | describe("Whitespace Issue Test", () => {
 8 |   function parseJSX(code: string): t.File {
 9 |     return parse(code, {
10 |       plugins: ["jsx"],
11 |       sourceType: "module",
12 |     });
13 |   }
14 | 
15 |   function getJSXElementPath(code: string): NodePath<t.JSXElement> {
16 |     const ast = parseJSX(code);
17 |     let result: NodePath<t.JSXElement>;
18 | 
19 |     traverse(ast, {
20 |       JSXElement(path) {
21 |         result = path;
22 |         path.stop();
23 |       },
24 |     });
25 | 
26 |     return result!;
27 |   }
28 | 
29 |   it("should preserve leading space in nested elements", () => {
30 |     const path = getJSXElementPath(`
31 |       <h1 className="text-5xl md:text-7xl font-bold text-white mb-6 leading-tight">
32 |         Hello World
33 |         <span className="bg-gradient-to-r from-purple-400 via-pink-400 to-yellow-400 bg-clip-text text-transparent"> From Lingo.dev Compiler</span>
34 |       </h1>
35 |     `);
36 | 
37 |     const content = extractJsxContent(path);
38 |     console.log("Extracted content:", JSON.stringify(content));
39 | 
40 |     // Let's also check the raw JSX structure to understand what's happening
41 |     let jsxTexts: string[] = [];
42 |     path.traverse({
43 |       JSXText(textPath) {
44 |         jsxTexts.push(JSON.stringify(textPath.node.value));
45 |       },
46 |     });
47 |     console.log("JSXText nodes found:", jsxTexts);
48 | 
49 |     // The span should have " From Lingo.dev Compiler" with the leading space
50 |     expect(content).toContain(
51 |       "<element:span> From Lingo.dev Compiler</element:span>",
52 |     );
53 |   });
54 | 
55 |   it("should handle explicit whitespace correctly", () => {
56 |     const path = getJSXElementPath(`
57 |       <div>
58 |         Hello{" "}
59 |         <span> World</span>
60 |       </div>
61 |     `);
62 | 
63 |     const content = extractJsxContent(path);
64 |     console.log("Explicit whitespace test:", JSON.stringify(content));
65 | 
66 |     // Should preserve both the explicit space and the leading space in span
67 |     expect(content).toContain("Hello <element:span> World</element:span>");
68 |   });
69 | 
70 |   it("should preserve space before nested bold element like in HeroSubtitle", () => {
71 |     const path = getJSXElementPath(`
72 |       <p className="text-lg sm:text-xl text-gray-600 mb-10 max-w-xl mx-auto leading-relaxed">
73 |         Localize your React app in every language in minutes. Scale to millions
74 |         <b> from day one</b>.
75 |       </p>
76 |     `);
77 | 
78 |     const content = extractJsxContent(path);
79 |     console.log("HeroSubtitle test content:", JSON.stringify(content));
80 | 
81 |     // Let's also check the raw JSX structure
82 |     let jsxTexts: string[] = [];
83 |     path.traverse({
84 |       JSXText(textPath) {
85 |         jsxTexts.push(JSON.stringify(textPath.node.value));
86 |       },
87 |     });
88 |     console.log("HeroSubtitle JSXText nodes found:", jsxTexts);
89 | 
90 |     // The bold element should have " from day one" with the leading space
91 |     expect(content).toContain("<element:b> from day one</element:b>");
92 |     // The full content should preserve the space between "millions" and the bold element
93 |     expect(content).toContain("millions <element:b> from day one</element:b>");
94 |   });
95 | });
96 | 
```

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

```typescript
 1 | import { describe, expect, it } from "vitest";
 2 | import { parse } from "csv-parse/sync";
 3 | import createCsvLoader from "./csv";
 4 | 
 5 | // Helper to build CSV strings easily
 6 | function buildCsv(rows: string[][]): string {
 7 |   return rows.map((r) => r.join(",")).join("\n");
 8 | }
 9 | 
10 | describe("csv loader", () => {
11 |   const sampleCsv = buildCsv([
12 |     ["id", "en", "es"],
13 |     ["hello", "Hello", "Hola"],
14 |     ["bye", "Bye", "Adiós"],
15 |     ["unused", "", "Sin uso"],
16 |   ]);
17 | 
18 |   it("pull should extract translation map for the requested locale and skip empty values", async () => {
19 |     const loader = createCsvLoader();
20 |     loader.setDefaultLocale("en");
21 | 
22 |     const enResult = await loader.pull("en", sampleCsv);
23 |     expect(enResult).toEqual({ hello: "Hello", bye: "Bye" });
24 | 
25 |     const esResult = await loader.pull("es", sampleCsv);
26 |     expect(esResult).toEqual({
27 |       hello: "Hola",
28 |       bye: "Adiós",
29 |       unused: "Sin uso",
30 |     });
31 |   });
32 | 
33 |   it("push should update existing rows and append new keys for the same locale", async () => {
34 |     const loader = createCsvLoader();
35 |     loader.setDefaultLocale("en");
36 |     await loader.pull("en", sampleCsv);
37 | 
38 |     const updatedCsv = await loader.push("en", {
39 |       hello: "Hello edited",
40 |       newKey: "New Message",
41 |     });
42 | 
43 |     const parsed = parse(updatedCsv, { columns: true, skip_empty_lines: true });
44 |     expect(parsed).toEqual([
45 |       { id: "hello", en: "Hello edited", es: "Hola" },
46 |       { id: "bye", en: "Bye", es: "Adiós" },
47 |       { id: "unused", en: "", es: "Sin uso" },
48 |       { id: "", en: "New Message", es: "" },
49 |     ]);
50 |   });
51 | 
52 |   it("push should add a new locale column when pushing for a different locale", async () => {
53 |     const loader = createCsvLoader();
54 |     loader.setDefaultLocale("en");
55 |     await loader.pull("en", sampleCsv);
56 | 
57 |     const esCsv = await loader.push("es", {
58 |       hello: "Hola",
59 |       bye: "Adiós",
60 |     });
61 | 
62 |     const parsed = parse(esCsv, { columns: true, skip_empty_lines: true });
63 |     expect(parsed).toEqual([
64 |       { id: "hello", en: "Hello", es: "Hola" },
65 |       { id: "bye", en: "Bye", es: "Adiós" },
66 |       { id: "unused", en: "", es: "Sin uso" },
67 |     ]);
68 |   });
69 | 
70 |   it("push should add a completely new locale column when it previously didn't exist", async () => {
71 |     const loader = createCsvLoader();
72 |     loader.setDefaultLocale("en");
73 |     await loader.pull("en", sampleCsv); // sampleCsv only has en & es columns
74 | 
75 |     const frCsv = await loader.push("fr", {
76 |       hello: "Bonjour",
77 |       bye: "Au revoir",
78 |     });
79 | 
80 |     const parsed = parse(frCsv, { columns: true, skip_empty_lines: true });
81 |     // Expect new column 'fr' to exist alongside existing ones, with empty strings when no translation provided
82 |     expect(parsed).toEqual([
83 |       { id: "hello", en: "Hello", es: "Hola", fr: "Bonjour" },
84 |       { id: "bye", en: "Bye", es: "Adiós", fr: "Au revoir" },
85 |       { id: "unused", en: "", es: "Sin uso", fr: "" },
86 |     ]);
87 |   });
88 | 
89 |   it("should throw an error if the first pull is not for the default locale", async () => {
90 |     const loader = createCsvLoader();
91 |     loader.setDefaultLocale("en");
92 | 
93 |     await expect(loader.pull("es", sampleCsv)).rejects.toThrow(
94 |       "The first pull must be for the default locale",
95 |     );
96 |   });
97 | });
98 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/utils/invokations.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { it, describe, expect } from "vitest";
  2 | import { parse } from "@babel/parser";
  3 | import * as t from "@babel/types";
  4 | import { findInvokations } from "./invokations";
  5 | 
  6 | describe("findInvokations", () => {
  7 |   it("should find named import invocation", () => {
  8 |     const ast = parseCode(`
  9 |       import { targetFunc } from 'target-module';
 10 |       
 11 |       function test() {
 12 |         targetFunc(1, 2);
 13 |         otherFunc();
 14 |       }
 15 |     `);
 16 | 
 17 |     const result = findInvokations(ast, {
 18 |       moduleName: "target-module",
 19 |       functionName: "targetFunc",
 20 |     });
 21 | 
 22 |     expect(result.length).toBe(1);
 23 |     expect(result[0].type).toBe("CallExpression");
 24 | 
 25 |     const callee = result[0].callee as t.Identifier;
 26 |     expect(callee.name).toBe("targetFunc");
 27 |   });
 28 | 
 29 |   it("should find default import invocation", () => {
 30 |     const ast = parseCode(`
 31 |       import defaultFunc from 'target-module';
 32 |       
 33 |       function test() {
 34 |         defaultFunc('test');
 35 |       }
 36 |     `);
 37 | 
 38 |     const result = findInvokations(ast, {
 39 |       moduleName: "target-module",
 40 |       functionName: "default",
 41 |     });
 42 | 
 43 |     expect(result.length).toBe(1);
 44 | 
 45 |     const callee = result[0].callee as t.Identifier;
 46 |     expect(callee.name).toBe("defaultFunc");
 47 |   });
 48 | 
 49 |   it("should find namespace import invocation", () => {
 50 |     const ast = parseCode(`
 51 |       import * as targetModule from 'target-module';
 52 |       
 53 |       function test() {
 54 |         targetModule.targetFunc();
 55 |         targetModule.otherFunc();
 56 |       }
 57 |     `);
 58 | 
 59 |     const result = findInvokations(ast, {
 60 |       moduleName: "target-module",
 61 |       functionName: "targetFunc",
 62 |     });
 63 | 
 64 |     expect(result.length).toBe(1);
 65 | 
 66 |     const callee = result[0].callee as t.MemberExpression;
 67 |     expect((callee.object as t.Identifier).name).toBe("targetModule");
 68 |     expect((callee.property as t.Identifier).name).toBe("targetFunc");
 69 |   });
 70 | 
 71 |   it("should find renamed import invocation", () => {
 72 |     const ast = parseCode(`
 73 |       import { targetFunc as renamedFunc } from 'target-module';
 74 |       
 75 |       function test() {
 76 |         renamedFunc();
 77 |       }
 78 |     `);
 79 | 
 80 |     const result = findInvokations(ast, {
 81 |       moduleName: "target-module",
 82 |       functionName: "targetFunc",
 83 |     });
 84 | 
 85 |     expect(result.length).toBe(1);
 86 | 
 87 |     const callee = result[0].callee as t.Identifier;
 88 |     expect(callee.name).toBe("renamedFunc");
 89 |   });
 90 | 
 91 |   it("should return empty array when no matching imports exist", () => {
 92 |     const ast = parseCode(`
 93 |       import { otherFunc } from 'other-module';
 94 |       
 95 |       function test() {
 96 |         otherFunc();
 97 |       }
 98 |     `);
 99 | 
100 |     const result = findInvokations(ast, {
101 |       moduleName: "target-module",
102 |       functionName: "targetFunc",
103 |     });
104 | 
105 |     expect(result.length).toBe(0);
106 |   });
107 | 
108 |   it("should return empty array when import exists but not invoked", () => {
109 |     const ast = parseCode(`
110 |       import { targetFunc } from 'target-module';
111 |       
112 |       function test() {
113 |         // No invocation here
114 |       }
115 |     `);
116 | 
117 |     const result = findInvokations(ast, {
118 |       moduleName: "target-module",
119 |       functionName: "targetFunc",
120 |     });
121 | 
122 |     expect(result.length).toBe(0);
123 |   });
124 | });
125 | 
126 | function parseCode(code: string): t.File {
127 |   return parse(code, {
128 |     sourceType: "module",
129 |     plugins: ["typescript"],
130 |   });
131 | }
132 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/utils/jsx-functions.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { parse } from "@babel/parser";
  2 | import traverse from "@babel/traverse";
  3 | import generate from "@babel/generator";
  4 | import { getJsxFunctions } from "./jsx-functions";
  5 | import { describe, expect, it } from "vitest";
  6 | 
  7 | function parseJSX(code: string) {
  8 |   return parse(code, {
  9 |     plugins: ["jsx"],
 10 |     sourceType: "module",
 11 |   });
 12 | }
 13 | 
 14 | describe("getJsxFunctions", () => {
 15 |   it("extracts simple function calls", () => {
 16 |     const ast = parseJSX("<div>{getName()}</div>");
 17 |     let result;
 18 | 
 19 |     traverse(ast, {
 20 |       JSXElement(path) {
 21 |         result = getJsxFunctions(path);
 22 |         path.stop();
 23 |       },
 24 |     });
 25 | 
 26 |     expect(generate(result).code).toBe('{\n  "getName": [getName()]\n}');
 27 |   });
 28 | 
 29 |   it("extracts function calls with arguments", () => {
 30 |     const ast = parseJSX("<div>{getName(user, 123)}</div>");
 31 |     let result;
 32 | 
 33 |     traverse(ast, {
 34 |       JSXElement(path) {
 35 |         result = getJsxFunctions(path);
 36 |         path.stop();
 37 |       },
 38 |     });
 39 | 
 40 |     expect(generate(result).code).toBe(
 41 |       '{\n  "getName": [getName(user, 123)]\n}',
 42 |     );
 43 |   });
 44 | 
 45 |   it("extracts multiple function calls", () => {
 46 |     const ast = parseJSX("<div>{getName(user)} {getCount()}</div>");
 47 |     let result;
 48 | 
 49 |     traverse(ast, {
 50 |       JSXElement(path) {
 51 |         result = getJsxFunctions(path);
 52 |         path.stop();
 53 |       },
 54 |     });
 55 | 
 56 |     expect(generate(result).code).toBe(
 57 |       '{\n  "getName": [getName(user)],\n  "getCount": [getCount()]\n}',
 58 |     );
 59 |   });
 60 | 
 61 |   it("ignores non-function expressions", () => {
 62 |     const ast = parseJSX("<div>{user.name} {getCount()}</div>");
 63 |     let result;
 64 | 
 65 |     traverse(ast, {
 66 |       JSXElement(path) {
 67 |         result = getJsxFunctions(path);
 68 |         path.stop();
 69 |       },
 70 |     });
 71 | 
 72 |     expect(generate(result).code).toBe('{\n  "getCount": [getCount()]\n}');
 73 |   });
 74 | 
 75 |   it("extracts function with chained names", () => {
 76 |     const ast = parseJSX(
 77 |       "<div>{getCount()} {user.details.products.items.map((item) => item.value).filter(value => value > 0)}</div>",
 78 |     );
 79 |     let result;
 80 | 
 81 |     traverse(ast, {
 82 |       JSXElement(path) {
 83 |         result = getJsxFunctions(path);
 84 |         path.stop();
 85 |       },
 86 |     });
 87 | 
 88 |     expect(generate(result!).code).toBe(
 89 |       '{\n  "getCount": [getCount()],\n  "user.details.products.items.map": [user.details.products.items.map(item => item.value).filter(value => value > 0)]\n}',
 90 |     );
 91 |   });
 92 | 
 93 |   it("extracts multiple usages of the same function", () => {
 94 |     const ast = parseJSX(
 95 |       "<div>{getCount(foo)} is more than {getCount(bar)} but less than {getCount(baz)}</div>",
 96 |     );
 97 |     let result;
 98 | 
 99 |     traverse(ast, {
100 |       JSXElement(path) {
101 |         result = getJsxFunctions(path);
102 |         path.stop();
103 |       },
104 |     });
105 | 
106 |     expect(generate(result!).code).toBe(
107 |       '{\n  "getCount": [getCount(foo), getCount(bar), getCount(baz)]\n}',
108 |     );
109 |   });
110 | 
111 |   it("should extract function calls on classes with 'new' keyword", () => {
112 |     const ast = parseJSX("<div>&copy; {new Date().getFullYear()} vitest</div>");
113 |     let result;
114 |     traverse(ast, {
115 |       JSXElement(path) {
116 |         result = getJsxFunctions(path);
117 |         path.stop();
118 |       },
119 |     });
120 | 
121 |     expect(generate(result!).code).toBe(
122 |       '{\n  "Date.getFullYear": [new Date().getFullYear()]\n}',
123 |     );
124 |   });
125 | });
126 | 
```

--------------------------------------------------------------------------------
/scripts/docs/src/utils.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { existsSync } from "fs";
  2 | import path from "path";
  3 | import { fileURLToPath } from "url";
  4 | import { readFileSync } from "fs";
  5 | import { Octokit } from "@octokit/rest";
  6 | import * as prettier from "prettier";
  7 | 
  8 | export function getRepoRoot(): string {
  9 |   const __filename = fileURLToPath(import.meta.url);
 10 |   const __dirname = path.dirname(__filename);
 11 |   let currentDir = __dirname;
 12 | 
 13 |   while (currentDir !== path.parse(currentDir).root) {
 14 |     if (existsSync(path.join(currentDir, ".git"))) {
 15 |       return currentDir;
 16 |     }
 17 |     currentDir = path.dirname(currentDir);
 18 |   }
 19 | 
 20 |   throw new Error("Could not find project root");
 21 | }
 22 | 
 23 | export function getGitHubToken() {
 24 |   const token = process.env.GITHUB_TOKEN;
 25 | 
 26 |   if (!token) {
 27 |     throw new Error("GITHUB_TOKEN environment variable is required.");
 28 |   }
 29 | 
 30 |   return token;
 31 | }
 32 | 
 33 | export function getGitHubRepo() {
 34 |   const repository = process.env.GITHUB_REPOSITORY;
 35 | 
 36 |   if (!repository) {
 37 |     throw new Error("GITHUB_REPOSITORY environment variable is missing.");
 38 |   }
 39 | 
 40 |   const [_, repo] = repository.split("/");
 41 | 
 42 |   return repo;
 43 | }
 44 | 
 45 | export function getGitHubOwner() {
 46 |   const repository = process.env.GITHUB_REPOSITORY;
 47 | 
 48 |   if (!repository) {
 49 |     throw new Error("GITHUB_REPOSITORY environment variable is missing.");
 50 |   }
 51 | 
 52 |   const [owner] = repository.split("/");
 53 | 
 54 |   return owner;
 55 | }
 56 | 
 57 | export function getGitHubPRNumber() {
 58 |   const prNumber = process.env.PR_NUMBER;
 59 | 
 60 |   if (prNumber) {
 61 |     return Number(prNumber);
 62 |   }
 63 | 
 64 |   const eventPath = process.env.GITHUB_EVENT_PATH;
 65 | 
 66 |   if (eventPath && existsSync(eventPath)) {
 67 |     try {
 68 |       const eventData = JSON.parse(readFileSync(eventPath, "utf8"));
 69 |       return Number(eventData.pull_request?.number);
 70 |     } catch (err) {
 71 |       console.warn("Failed to parse GITHUB_EVENT_PATH JSON:", err);
 72 |     }
 73 |   }
 74 | 
 75 |   throw new Error("Could not determine pull request number.");
 76 | }
 77 | 
 78 | export type GitHubCommentOptions = {
 79 |   commentMarker: string;
 80 |   body: string;
 81 | };
 82 | 
 83 | export async function createOrUpdateGitHubComment(
 84 |   options: GitHubCommentOptions,
 85 | ): Promise<void> {
 86 |   const token = getGitHubToken();
 87 |   const owner = getGitHubOwner();
 88 |   const repo = getGitHubRepo();
 89 |   const prNumber = getGitHubPRNumber();
 90 | 
 91 |   const octokit = new Octokit({ auth: token });
 92 | 
 93 |   const commentsResponse = await octokit.rest.issues.listComments({
 94 |     owner,
 95 |     repo,
 96 |     issue_number: prNumber,
 97 |     per_page: 100,
 98 |   });
 99 | 
100 |   const comments = commentsResponse.data;
101 | 
102 |   const existing = comments.find((c) => {
103 |     if (!c.body) {
104 |       return false;
105 |     }
106 |     return c.body.startsWith(options.commentMarker);
107 |   });
108 | 
109 |   if (existing) {
110 |     console.log(`Updating existing comment (id: ${existing.id}).`);
111 |     await octokit.rest.issues.updateComment({
112 |       owner,
113 |       repo,
114 |       comment_id: existing.id,
115 |       body: options.body,
116 |     });
117 |     return;
118 |   }
119 | 
120 |   console.log("Creating new comment.");
121 |   await octokit.rest.issues.createComment({
122 |     owner,
123 |     repo,
124 |     issue_number: prNumber,
125 |     body: options.body,
126 |   });
127 | }
128 | 
129 | export async function formatMarkdown(markdown: string): Promise<string> {
130 |   const repoRoot = getRepoRoot();
131 |   const prettierConfig = await prettier.resolveConfig(repoRoot);
132 |   return await prettier.format(markdown, {
133 |     ...prettierConfig,
134 |     parser: "markdown",
135 |   });
136 | }
137 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/ensure-key-order.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import createEnsureKeyOrderLoader from "./ensure-key-order";
  3 | 
  4 | describe("ensure-key-order loader", () => {
  5 |   const loader = createEnsureKeyOrderLoader();
  6 |   loader.setDefaultLocale("en");
  7 | 
  8 |   it("should return input unchanged on pull", async () => {
  9 |     const input = { b: 1, a: 2 };
 10 |     const result = await loader.pull("en", input);
 11 |     expect(result).toEqual(input);
 12 |   });
 13 | 
 14 |   it("should reorder keys to match original input order on push", async () => {
 15 |     const originalInput = { a: 1, b: 2, c: 3 };
 16 |     await loader.pull("en", originalInput);
 17 |     const data = { b: 22, a: 11, c: 33 };
 18 |     const result = await loader.push("en", data);
 19 |     expect(result).toEqual({ a: 11, b: 22, c: 33 });
 20 |   });
 21 | 
 22 |   it("should reorder keys in objects of nested arrays to match original input order on push", async () => {
 23 |     const originalInput = [
 24 |       { a: 1, b: 2, c: 3 },
 25 |       { a: 4, b: 5, c: 6 },
 26 |       {
 27 |         values: [
 28 |           { a: 7, b: 8, c: 9 },
 29 |           { a: 10, b: 11, c: 12 },
 30 |         ],
 31 |       },
 32 |     ];
 33 |     await loader.pull("en", originalInput);
 34 |     const data = [
 35 |       { b: 22, a: 11, c: 33 },
 36 |       { b: 55, c: 66, a: 44 },
 37 |       {
 38 |         values: [
 39 |           { b: 88, c: 99, a: 77 },
 40 |           { c: 122, b: 111, a: 100 },
 41 |         ],
 42 |       },
 43 |     ];
 44 |     const result = await loader.push("en", data);
 45 |     expect(result).toEqual([
 46 |       { a: 11, b: 22, c: 33 },
 47 |       { a: 44, b: 55, c: 66 },
 48 |       {
 49 |         values: [
 50 |           { a: 77, b: 88, c: 99 },
 51 |           { a: 100, b: 111, c: 122 },
 52 |         ],
 53 |       },
 54 |     ]);
 55 |   });
 56 | 
 57 |   it("should reorder falsy keys to match original input order on push", async () => {
 58 |     const originalInput = {
 59 |       a: 1,
 60 |       b: 0,
 61 |       c: null,
 62 |       d: "a",
 63 |       e: false,
 64 |       g: "",
 65 |       h: undefined,
 66 |     };
 67 |     await loader.pull("en", originalInput);
 68 |     const data = {
 69 |       b: 0,
 70 |       a: 11,
 71 |       c: null,
 72 |       d: "b",
 73 |       e: false,
 74 |       g: "",
 75 |       h: undefined,
 76 |     };
 77 |     const result = await loader.push("en", data);
 78 |     expect(result).toEqual({
 79 |       a: 11,
 80 |       b: 0,
 81 |       c: null,
 82 |       d: "b",
 83 |       e: false,
 84 |       g: "",
 85 |       h: undefined,
 86 |     });
 87 |   });
 88 | 
 89 |   it("should handle nested objects and preserve key order", async () => {
 90 |     const originalInput = { x: { b: 2, a: 1 }, y: 3, z: { d: 9, f: 7, e: 8 } };
 91 |     await loader.pull("en", originalInput);
 92 |     const data = { x: { a: 11, b: 22 }, z: { d: 99, e: 88, f: 77 }, y: 33 };
 93 |     const result = await loader.push("en", data);
 94 |     expect(result).toEqual({
 95 |       x: { b: 22, a: 11 },
 96 |       y: 33,
 97 |       z: { d: 99, e: 88, f: 77 },
 98 |     });
 99 |   });
100 | 
101 |   it("should skip keys not in original input of source locale", async () => {
102 |     const originalInput = { a: 1, b: 2 };
103 |     await loader.pull("en", originalInput);
104 |     const data = { a: 11, b: 22, c: 33 };
105 |     const result = await loader.push("en", data);
106 |     expect(result).toEqual({ a: 11, b: 22 });
107 |   });
108 | 
109 |   it("should skip keys not in the target locale data", async () => {
110 |     const originalInput = { a: 1, b: 2, c: 2 };
111 |     await loader.pull("en", originalInput);
112 |     const data = { a: 11, c: 33 };
113 |     const result = await loader.push("en", data);
114 |     expect(result).toEqual({ a: 11, c: 33 });
115 |   });
116 | });
117 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/utils/exec.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect, vi } from "vitest";
 2 | import { execAsync, ExecAsyncOptions } from "./exec";
 3 | 
 4 | // describe('execAsync', () => {
 5 | //   // Helper function to create a delayed async function
 6 | //   const createDelayedFunction = (value: any, delay: number) => {
 7 | //     return async () => {
 8 | //       console.log(`[${Date.now()}] start`, value);
 9 | //       await new Promise(resolve => setTimeout(resolve, delay));
10 | //       console.log(`[${Date.now()}] end`, value);
11 | //       return value;
12 | //     };
13 | //   };
14 | 
15 | //   it('run', async () => {
16 | //     await execAsync([
17 | //       createDelayedFunction(1, 750),
18 | //       createDelayedFunction(2, 750),
19 | //       createDelayedFunction(3, 750),
20 | //       createDelayedFunction(4, 750),
21 | //     ], {
22 | //       concurrency: 2,
23 | //       delay: 250,
24 | //     });
25 | //   });
26 | // });
27 | 
28 | describe("execAsync", () => {
29 |   it("executes all functions and returns their results", async () => {
30 |     const fns = [async () => 1, async () => 2, async () => 3];
31 |     const options: ExecAsyncOptions = { concurrency: 1, delay: 0 };
32 |     const results = await execAsync(fns, options);
33 |     expect(results).toEqual([1, 2, 3]);
34 |   });
35 | 
36 |   it("calls onProgress with correct values", async () => {
37 |     const fns = [async () => 1, async () => 2, async () => 3];
38 |     const onProgress = vi.fn();
39 |     const options: ExecAsyncOptions = { concurrency: 1, delay: 0, onProgress };
40 |     await execAsync(fns, options);
41 |     expect(onProgress).toHaveBeenCalledTimes(4);
42 |     expect(onProgress).toHaveBeenNthCalledWith(1, 0, 3);
43 |     expect(onProgress).toHaveBeenNthCalledWith(2, 1, 3);
44 |     expect(onProgress).toHaveBeenNthCalledWith(3, 2, 3);
45 |     expect(onProgress).toHaveBeenNthCalledWith(4, 3, 3);
46 |   });
47 | 
48 |   it("starts next function if previous finishes before delay", async () => {
49 |     const delay = 100;
50 |     const fns = [
51 |       vi.fn().mockResolvedValue(1),
52 |       vi
53 |         .fn()
54 |         .mockImplementation(
55 |           () => new Promise((resolve) => setTimeout(() => resolve(2), 50)),
56 |         ),
57 |       vi.fn().mockResolvedValue(3),
58 |     ];
59 |     const options: ExecAsyncOptions = { concurrency: 1, delay };
60 |     const start = Date.now();
61 |     await execAsync(fns, options);
62 |     const end = Date.now();
63 |     expect(end - start).toBeLessThan(delay * 3);
64 |   });
65 | 
66 |   it("respects concurrency limit", async () => {
67 |     const concurrency = 2;
68 |     const delay = 100;
69 |     let maxConcurrent = 0;
70 |     let currentConcurrent = 0;
71 | 
72 |     const fns = Array(5)
73 |       .fill(null)
74 |       .map(() => async () => {
75 |         currentConcurrent++;
76 |         maxConcurrent = Math.max(maxConcurrent, currentConcurrent);
77 |         await new Promise((resolve) => setTimeout(resolve, delay));
78 |         currentConcurrent--;
79 |       });
80 | 
81 |     const options: ExecAsyncOptions = { concurrency, delay: 0 };
82 |     await execAsync(fns, options);
83 |     expect(maxConcurrent).toBe(concurrency);
84 |   });
85 | 
86 |   it("handles empty array of functions", async () => {
87 |     const options: ExecAsyncOptions = { concurrency: 1, delay: 0 };
88 |     const results = await execAsync([], options);
89 |     expect(results).toEqual([]);
90 |   });
91 | 
92 |   it("handles single function", async () => {
93 |     const fn = async () => 42;
94 |     const options: ExecAsyncOptions = { concurrency: 1, delay: 0 };
95 |     const results = await execAsync([fn], options);
96 |     expect(results).toEqual([42]);
97 |   });
98 | });
99 | 
```

--------------------------------------------------------------------------------
/packages/locales/src/names/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import {
  2 |   loadTerritoryNames,
  3 |   loadLanguageNames,
  4 |   loadScriptNames,
  5 | } from "./loader";
  6 | 
  7 | /**
  8 |  * Gets a country name in the specified display language
  9 |  *
 10 |  * @param countryCode - The ISO country code (e.g., "US", "CN", "DE")
 11 |  * @param displayLanguage - The language to display the name in (default: "en")
 12 |  * @returns Promise<string> - The localized country name
 13 |  *
 14 |  * @example
 15 |  * ```typescript
 16 |  * // Default English
 17 |  * await getCountryName("US");           // "United States"
 18 |  * await getCountryName("CN");           // "China"
 19 |  *
 20 |  * // Spanish
 21 |  * await getCountryName("US", "es");     // "Estados Unidos"
 22 |  * await getCountryName("CN", "es");     // "China"
 23 |  *
 24 |  * // French
 25 |  * await getCountryName("US", "fr");     // "États-Unis"
 26 |  * ```
 27 |  */
 28 | export async function getCountryName(
 29 |   countryCode: string,
 30 |   displayLanguage: string = "en",
 31 | ): Promise<string> {
 32 |   if (!countryCode) {
 33 |     throw new Error("Country code is required");
 34 |   }
 35 | 
 36 |   const territories = await loadTerritoryNames(displayLanguage);
 37 |   const name = territories[countryCode.toUpperCase()];
 38 | 
 39 |   if (!name) {
 40 |     throw new Error(`Country code "${countryCode}" not found`);
 41 |   }
 42 | 
 43 |   return name;
 44 | }
 45 | 
 46 | /**
 47 |  * Gets a language name in the specified display language
 48 |  *
 49 |  * @param languageCode - The ISO language code (e.g., "en", "zh", "es")
 50 |  * @param displayLanguage - The language to display the name in (default: "en")
 51 |  * @returns Promise<string> - The localized language name
 52 |  *
 53 |  * @example
 54 |  * ```typescript
 55 |  * // Default English
 56 |  * await getLanguageName("en");          // "English"
 57 |  * await getLanguageName("zh");          // "Chinese"
 58 |  *
 59 |  * // Spanish
 60 |  * await getLanguageName("en", "es");    // "inglés"
 61 |  * await getLanguageName("zh", "es");    // "chino"
 62 |  *
 63 |  * // Chinese
 64 |  * await getLanguageName("en", "zh");    // "英语"
 65 |  * ```
 66 |  */
 67 | export async function getLanguageName(
 68 |   languageCode: string,
 69 |   displayLanguage: string = "en",
 70 | ): Promise<string> {
 71 |   if (!languageCode) {
 72 |     throw new Error("Language code is required");
 73 |   }
 74 | 
 75 |   const languages = await loadLanguageNames(displayLanguage);
 76 |   const name = languages[languageCode.toLowerCase()];
 77 | 
 78 |   if (!name) {
 79 |     throw new Error(`Language code "${languageCode}" not found`);
 80 |   }
 81 | 
 82 |   return name;
 83 | }
 84 | 
 85 | /**
 86 |  * Gets a script name in the specified display language
 87 |  *
 88 |  * @param scriptCode - The ISO script code (e.g., "Hans", "Hant", "Latn")
 89 |  * @param displayLanguage - The language to display the name in (default: "en")
 90 |  * @returns Promise<string> - The localized script name
 91 |  *
 92 |  * @example
 93 |  * ```typescript
 94 |  * // Default English
 95 |  * await getScriptName("Hans");          // "Simplified"
 96 |  * await getScriptName("Hant");          // "Traditional"
 97 |  * await getScriptName("Latn");          // "Latin"
 98 |  *
 99 |  * // Spanish
100 |  * await getScriptName("Hans", "es");    // "simplificado"
101 |  * await getScriptName("Cyrl", "es");    // "cirílico"
102 |  *
103 |  * // Chinese
104 |  * await getScriptName("Latn", "zh");    // "拉丁文"
105 |  * ```
106 |  */
107 | export async function getScriptName(
108 |   scriptCode: string,
109 |   displayLanguage: string = "en",
110 | ): Promise<string> {
111 |   if (!scriptCode) {
112 |     throw new Error("Script code is required");
113 |   }
114 | 
115 |   const scripts = await loadScriptNames(displayLanguage);
116 |   const name = scripts[scriptCode];
117 | 
118 |   if (!name) {
119 |     throw new Error(`Script code "${scriptCode}" not found`);
120 |   }
121 | 
122 |   return name;
123 | }
124 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/utils/jsx-attribute-scope.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import * as t from "@babel/types";
  2 | import traverse from "@babel/traverse";
  3 | import { NodePath } from "@babel/traverse";
  4 | 
  5 | export function collectJsxAttributeScopes(
  6 |   node: t.Node,
  7 | ): Array<[NodePath<t.JSXElement>, string[]]> {
  8 |   const result: Array<[NodePath<t.JSXElement>, string[]]> = [];
  9 | 
 10 |   traverse(node, {
 11 |     JSXElement(path: NodePath<t.JSXElement>) {
 12 |       if (!hasJsxAttributeScopeAttribute(path)) return;
 13 | 
 14 |       const localizableAttributes = getJsxAttributeScopeAttribute(path);
 15 |       if (!localizableAttributes) return;
 16 | 
 17 |       result.push([path, localizableAttributes]);
 18 |     },
 19 |   });
 20 | 
 21 |   return result;
 22 | }
 23 | 
 24 | export function getJsxAttributeScopes(
 25 |   node: t.Node,
 26 | ): Array<[NodePath<t.JSXElement>, string[]]> {
 27 |   const result: Array<[NodePath<t.JSXElement>, string[]]> = [];
 28 | 
 29 |   // List of attributes that should be considered localizable
 30 |   const LOCALIZABLE_ATTRIBUTES = [
 31 |     "title",
 32 |     "aria-label",
 33 |     "aria-description",
 34 |     "alt",
 35 |     "label",
 36 |     "description",
 37 |     "placeholder",
 38 |     "content",
 39 |     "subtitle",
 40 |   ];
 41 | 
 42 |   traverse(node, {
 43 |     JSXElement(path: NodePath<t.JSXElement>) {
 44 |       const openingElement = path.node.openingElement;
 45 | 
 46 |       // Only process lowercase HTML elements (not components)
 47 |       const elementName = openingElement.name;
 48 |       if (!t.isJSXIdentifier(elementName) || !elementName.name) {
 49 |         return;
 50 |       }
 51 | 
 52 |       const hasAttributeScope = openingElement.attributes.find(
 53 |         (attr) =>
 54 |           t.isJSXAttribute(attr) &&
 55 |           attr.name.name === "data-jsx-attribute-scope",
 56 |       );
 57 |       if (hasAttributeScope) {
 58 |         return;
 59 |       }
 60 | 
 61 |       // Find all localizable attributes
 62 |       const localizableAttrs = openingElement.attributes
 63 |         .filter(
 64 |           (
 65 |             attr: t.JSXAttribute | t.JSXSpreadAttribute,
 66 |           ): attr is t.JSXAttribute => {
 67 |             if (!t.isJSXAttribute(attr) || !t.isStringLiteral(attr.value)) {
 68 |               return false;
 69 |             }
 70 | 
 71 |             const name = attr.name.name;
 72 |             return (
 73 |               typeof name === "string" && LOCALIZABLE_ATTRIBUTES.includes(name)
 74 |             );
 75 |           },
 76 |         )
 77 |         .map((attr: t.JSXAttribute) => attr.name.name as string);
 78 | 
 79 |       // Only add the element if we found localizable attributes
 80 |       if (localizableAttrs.length > 0) {
 81 |         result.push([path, localizableAttrs]);
 82 |       }
 83 |     },
 84 |   });
 85 | 
 86 |   return result;
 87 | }
 88 | 
 89 | export function hasJsxAttributeScopeAttribute(path: NodePath<t.JSXElement>) {
 90 |   return !!getJsxAttributeScopeAttribute(path);
 91 | }
 92 | 
 93 | export function getJsxAttributeScopeAttribute(path: NodePath<t.JSXElement>) {
 94 |   const attribute = path.node.openingElement.attributes.find(
 95 |     (attr) =>
 96 |       attr.type === "JSXAttribute" &&
 97 |       attr.name.name === "data-jsx-attribute-scope",
 98 |   );
 99 | 
100 |   if (!attribute || !t.isJSXAttribute(attribute)) {
101 |     return undefined;
102 |   }
103 | 
104 |   // Handle array of string literals
105 |   if (
106 |     t.isJSXExpressionContainer(attribute.value) &&
107 |     t.isArrayExpression(attribute.value.expression)
108 |   ) {
109 |     const arrayExpr = attribute.value.expression;
110 |     return arrayExpr.elements
111 |       .filter((el): el is t.StringLiteral => t.isStringLiteral(el))
112 |       .map((el) => el.value);
113 |   }
114 | 
115 |   // Fallback for single string literal
116 |   if (t.isStringLiteral(attribute.value)) {
117 |     return [attribute.value.value];
118 |   }
119 | 
120 |   return undefined;
121 | }
122 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/jsx-provider.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import traverse, { NodePath } from "@babel/traverse";
  2 | import * as t from "@babel/types";
  3 | import { CompilerPayload, createCodeMutation } from "./_base";
  4 | import { getJsxElementName } from "./utils/jsx-element";
  5 | import { getModuleExecutionMode, getOrCreateImport } from "./utils";
  6 | import { ModuleId } from "./_const";
  7 | 
  8 | /**
  9 |  * This mutation is used to wrap the html component with the LingoProvider component.
 10 |  * It only works with server components.
 11 |  */
 12 | const jsxProviderMutation = createCodeMutation((payload) => {
 13 |   traverse(payload.ast, {
 14 |     JSXElement: (path) => {
 15 |       if (getJsxElementName(path)?.toLowerCase() === "html") {
 16 |         const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
 17 |         if (mode === "client") {
 18 |           return;
 19 |         }
 20 | 
 21 |         // TODO: later
 22 |         // replaceHtmlComponent(payload, path);
 23 | 
 24 |         const lingoProviderImport = getOrCreateImport(payload.ast, {
 25 |           moduleName: ModuleId.ReactRSC,
 26 |           exportedName: "LingoProvider",
 27 |         });
 28 |         const loadDictionaryImport = getOrCreateImport(payload.ast, {
 29 |           moduleName: ModuleId.ReactRSC,
 30 |           exportedName: "loadDictionary",
 31 |         });
 32 | 
 33 |         const loadDictionaryArrow = t.arrowFunctionExpression(
 34 |           [t.identifier("locale")],
 35 |           t.callExpression(t.identifier(loadDictionaryImport.importedName), [
 36 |             t.identifier("locale"),
 37 |           ]),
 38 |         );
 39 | 
 40 |         const providerProps = [
 41 |           t.jsxAttribute(
 42 |             t.jsxIdentifier("loadDictionary"),
 43 |             t.jsxExpressionContainer(loadDictionaryArrow),
 44 |           ),
 45 |         ];
 46 | 
 47 |         const provider = t.jsxElement(
 48 |           t.jsxOpeningElement(
 49 |             t.jsxIdentifier(lingoProviderImport.importedName),
 50 |             providerProps,
 51 |             false,
 52 |           ),
 53 |           t.jsxClosingElement(
 54 |             t.jsxIdentifier(lingoProviderImport.importedName),
 55 |           ),
 56 |           [path.node],
 57 |           false,
 58 |         );
 59 | 
 60 |         path.replaceWith(provider);
 61 |         path.skip();
 62 |       }
 63 |     },
 64 |   });
 65 | 
 66 |   return payload;
 67 | });
 68 | 
 69 | export default jsxProviderMutation;
 70 | 
 71 | function replaceHtmlComponent(
 72 |   payload: CompilerPayload,
 73 |   path: NodePath<t.JSXElement>,
 74 | ) {
 75 |   // Find the parent function and make it async since locale is retrieved from cookies asynchronously
 76 |   const parentFunction = path.findParent(
 77 |     (p): p is NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression> =>
 78 |       t.isFunctionDeclaration(p.node) || t.isArrowFunctionExpression(p.node),
 79 |   );
 80 |   if (
 81 |     parentFunction?.node.type === "FunctionDeclaration" ||
 82 |     parentFunction?.node.type === "ArrowFunctionExpression"
 83 |   ) {
 84 |     parentFunction.node.async = true;
 85 |   }
 86 | 
 87 |   // html lang attribute
 88 |   const loadLocaleFromCookiesImport = getOrCreateImport(payload.ast, {
 89 |     moduleName: ModuleId.ReactRSC,
 90 |     exportedName: "loadLocaleFromCookies",
 91 |   });
 92 |   let langAttribute = path.node.openingElement.attributes.find(
 93 |     (attr) => attr.type === "JSXAttribute" && attr.name.name === "lang",
 94 |   );
 95 |   if (!t.isJSXAttribute(langAttribute)) {
 96 |     (langAttribute = t.jsxAttribute(
 97 |       t.jsxIdentifier("lang"),
 98 |       t.stringLiteral(""),
 99 |     )),
100 |       path.node.openingElement.attributes.push(langAttribute);
101 |   }
102 |   langAttribute.value = t.jsxExpressionContainer(
103 |     t.awaitExpression(
104 |       t.callExpression(
105 |         t.identifier(loadLocaleFromCookiesImport.importedName),
106 |         [],
107 |       ),
108 |     ),
109 |   );
110 | }
111 | 
```

--------------------------------------------------------------------------------
/demo/adonisjs/adonisrc.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { defineConfig } from '@adonisjs/core/app'
  2 | 
  3 | export default defineConfig({
  4 |   /*
  5 |   |--------------------------------------------------------------------------
  6 |   | Experimental flags
  7 |   |--------------------------------------------------------------------------
  8 |   |
  9 |   | The following features will be enabled by default in the next major release
 10 |   | of AdonisJS. You can opt into them today to avoid any breaking changes
 11 |   | during upgrade.
 12 |   |
 13 |   */
 14 |   experimental: {
 15 |     mergeMultipartFieldsAndFiles: true,
 16 |     shutdownInReverseOrder: true,
 17 |   },
 18 | 
 19 |   /*
 20 |   |--------------------------------------------------------------------------
 21 |   | Commands
 22 |   |--------------------------------------------------------------------------
 23 |   |
 24 |   | List of ace commands to register from packages. The application commands
 25 |   | will be scanned automatically from the "./commands" directory.
 26 |   |
 27 |   */
 28 |   commands: [() => import('@adonisjs/core/commands')],
 29 | 
 30 |   /*
 31 |   |--------------------------------------------------------------------------
 32 |   | Service providers
 33 |   |--------------------------------------------------------------------------
 34 |   |
 35 |   | List of service providers to import and register when booting the
 36 |   | application
 37 |   |
 38 |   */
 39 |   providers: [
 40 |     () => import('@adonisjs/core/providers/app_provider'),
 41 |     () => import('@adonisjs/core/providers/hash_provider'),
 42 |     {
 43 |       file: () => import('@adonisjs/core/providers/repl_provider'),
 44 |       environment: ['repl', 'test'],
 45 |     },
 46 |     () => import('@adonisjs/core/providers/vinejs_provider'),
 47 |     () => import('@adonisjs/core/providers/edge_provider'),
 48 |     () => import('@adonisjs/session/session_provider'),
 49 |     () => import('@adonisjs/vite/vite_provider'),
 50 |     () => import('@adonisjs/shield/shield_provider'),
 51 |     () => import('@adonisjs/static/static_provider'),
 52 |     () => import('@adonisjs/cors/cors_provider'),
 53 |     () => import('@adonisjs/inertia/inertia_provider'),
 54 |   ],
 55 | 
 56 |   /*
 57 |   |--------------------------------------------------------------------------
 58 |   | Preloads
 59 |   |--------------------------------------------------------------------------
 60 |   |
 61 |   | List of modules to import before starting the application.
 62 |   |
 63 |   */
 64 |   preloads: [() => import('#start/routes'), () => import('#start/kernel')],
 65 | 
 66 |   /*
 67 |   |--------------------------------------------------------------------------
 68 |   | Tests
 69 |   |--------------------------------------------------------------------------
 70 |   |
 71 |   | List of test suites to organize tests by their type. Feel free to remove
 72 |   | and add additional suites.
 73 |   |
 74 |   */
 75 |   tests: {
 76 |     suites: [
 77 |       {
 78 |         files: ['tests/unit/**/*.spec(.ts|.js)'],
 79 |         name: 'unit',
 80 |         timeout: 2000,
 81 |       },
 82 |       {
 83 |         files: ['tests/functional/**/*.spec(.ts|.js)'],
 84 |         name: 'functional',
 85 |         timeout: 30000,
 86 |       },
 87 |     ],
 88 |     forceExit: false,
 89 |   },
 90 | 
 91 |   /*
 92 |   |--------------------------------------------------------------------------
 93 |   | Metafiles
 94 |   |--------------------------------------------------------------------------
 95 |   |
 96 |   | A collection of files you want to copy to the build folder when creating
 97 |   | the production build.
 98 |   |
 99 |   */
100 |   metaFiles: [
101 |     {
102 |       pattern: 'resources/views/**/*.edge',
103 |       reloadServer: false,
104 |     },
105 |     {
106 |       pattern: 'public/**',
107 |       reloadServer: false,
108 |     },
109 |   ],
110 | 
111 |   assetsBundler: false,
112 |   hooks: {
113 |     onBuildStarting: [() => import('@adonisjs/vite/build_hook')],
114 |   },
115 | })
116 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/cmd/ci/platforms/gitlab.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Gitlab } from "@gitbeaker/rest";
  2 | import Z from "zod";
  3 | import { PlatformKit } from "./_base";
  4 | 
  5 | const gl = new Gitlab({ token: "" });
  6 | 
  7 | export class GitlabPlatformKit extends PlatformKit {
  8 |   private _gitlab?: InstanceType<typeof Gitlab>;
  9 | 
 10 |   constructor() {
 11 |     super();
 12 | 
 13 |     // change directory to current repository before executing replexica
 14 |     process.chdir(this.platformConfig.projectDir);
 15 |   }
 16 | 
 17 |   private get gitlab(): InstanceType<typeof Gitlab> {
 18 |     if (!this._gitlab) {
 19 |       this._gitlab = new Gitlab({
 20 |         token: this.platformConfig.glToken || "",
 21 |       });
 22 |     }
 23 |     return this._gitlab;
 24 |   }
 25 | 
 26 |   get platformConfig() {
 27 |     const env = Z.object({
 28 |       GL_TOKEN: Z.string().optional(),
 29 |       CI_COMMIT_BRANCH: Z.string(),
 30 |       CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: Z.string().optional(),
 31 |       CI_PROJECT_NAMESPACE: Z.string(),
 32 |       CI_PROJECT_NAME: Z.string(),
 33 |       CI_PROJECT_ID: Z.string(),
 34 |       CI_PROJECT_DIR: Z.string(),
 35 |       CI_REPOSITORY_URL: Z.string(),
 36 |     }).parse(process.env);
 37 | 
 38 |     const config = {
 39 |       glToken: env.GL_TOKEN,
 40 |       baseBranchName:
 41 |         env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ?? env.CI_COMMIT_BRANCH,
 42 |       repositoryOwner: env.CI_PROJECT_NAMESPACE,
 43 |       repositoryName: env.CI_PROJECT_NAME,
 44 |       gitlabProjectId: env.CI_PROJECT_ID,
 45 |       projectDir: env.CI_PROJECT_DIR,
 46 |       reporitoryUrl: env.CI_REPOSITORY_URL,
 47 |     };
 48 | 
 49 |     return config;
 50 |   }
 51 | 
 52 |   async branchExists({ branch }: { branch: string }): Promise<boolean> {
 53 |     try {
 54 |       await this.gitlab.Branches.show(
 55 |         this.platformConfig.gitlabProjectId,
 56 |         branch,
 57 |       );
 58 |       return true;
 59 |     } catch {
 60 |       return false;
 61 |     }
 62 |   }
 63 | 
 64 |   async getOpenPullRequestNumber({
 65 |     branch,
 66 |   }: {
 67 |     branch: string;
 68 |   }): Promise<number | undefined> {
 69 |     const mergeRequests = await this.gitlab.MergeRequests.all({
 70 |       projectId: this.platformConfig.gitlabProjectId,
 71 |       sourceBranch: branch,
 72 |       state: "opened",
 73 |     });
 74 |     return mergeRequests[0]?.iid;
 75 |   }
 76 | 
 77 |   async closePullRequest({
 78 |     pullRequestNumber,
 79 |   }: {
 80 |     pullRequestNumber: number;
 81 |   }): Promise<void> {
 82 |     await this.gitlab.MergeRequests.edit(
 83 |       this.platformConfig.gitlabProjectId,
 84 |       pullRequestNumber,
 85 |       {
 86 |         stateEvent: "close",
 87 |       },
 88 |     );
 89 |   }
 90 | 
 91 |   async createPullRequest({
 92 |     head,
 93 |     title,
 94 |     body,
 95 |   }: {
 96 |     head: string;
 97 |     title: string;
 98 |     body?: string;
 99 |   }): Promise<number> {
100 |     const mr = await this.gitlab.MergeRequests.create(
101 |       this.platformConfig.gitlabProjectId,
102 |       head,
103 |       this.platformConfig.baseBranchName,
104 |       title,
105 |       {
106 |         description: body,
107 |       },
108 |     );
109 |     return mr.iid;
110 |   }
111 | 
112 |   async commentOnPullRequest({
113 |     pullRequestNumber,
114 |     body,
115 |   }: {
116 |     pullRequestNumber: number;
117 |     body: string;
118 |   }): Promise<void> {
119 |     await this.gitlab.MergeRequestNotes.create(
120 |       this.platformConfig.gitlabProjectId,
121 |       pullRequestNumber,
122 |       body,
123 |     );
124 |   }
125 | 
126 |   gitConfig(): Promise<void> | void {
127 |     const glToken = this.platformConfig.glToken;
128 |     const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;
129 | 
130 |     super.gitConfig(glToken, url);
131 |   }
132 | 
133 |   buildPullRequestUrl(pullRequestNumber: number): string {
134 |     return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;
135 |   }
136 | }
137 | 
```

--------------------------------------------------------------------------------
/demo/adonisjs/inertia/lingo/meta.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "files": {
  3 |     "pages/errors/not_found.tsx": {
  4 |       "scopes": {
  5 |         "1/declaration/body/0/argument/1/1": {
  6 |           "type": "element",
  7 |           "hash": "97612e6230bc7a1ebd99380bf561b732",
  8 |           "context": "",
  9 |           "skip": false,
 10 |           "overrides": {},
 11 |           "content": "Page not found"
 12 |         },
 13 |         "1/declaration/body/0/argument/1/3": {
 14 |           "type": "element",
 15 |           "hash": "7b6bcd0a4f23e42eeb0c972c2004efad",
 16 |           "context": "",
 17 |           "skip": false,
 18 |           "overrides": {},
 19 |           "content": "This page does not exist."
 20 |         }
 21 |       }
 22 |     },
 23 |     "pages/errors/server_error.tsx": {
 24 |       "scopes": {
 25 |         "1/declaration/body/0/argument/1/1": {
 26 |           "type": "element",
 27 |           "hash": "d574aa7e2d84d112dc79ac0e59d794cf",
 28 |           "context": "",
 29 |           "skip": false,
 30 |           "overrides": {},
 31 |           "content": "Server Error"
 32 |         }
 33 |       }
 34 |     },
 35 |     "pages/home.tsx": {
 36 |       "scopes": {
 37 |         "2/declaration/body/0/argument/1-title": {
 38 |           "type": "attribute",
 39 |           "hash": "7c2d68be7446e6de191c11d53f1e07b4",
 40 |           "context": "",
 41 |           "skip": false,
 42 |           "overrides": {},
 43 |           "content": "Homepage"
 44 |         },
 45 |         "2/declaration/body/0/argument/3/1/1": {
 46 |           "type": "element",
 47 |           "hash": "0468579ef2fbc83c9d520c2f2f1c5059",
 48 |           "context": "",
 49 |           "skip": false,
 50 |           "overrides": {},
 51 |           "content": "Hello, world!"
 52 |         },
 53 |         "2/declaration/body/0/argument/3/1/3": {
 54 |           "type": "element",
 55 |           "hash": "82b29979a52b215b94b2e811e8c03005",
 56 |           "context": "",
 57 |           "skip": false,
 58 |           "overrides": {},
 59 |           "content": "This is an example app that demonstrates how <element:strong>Lingo.dev Compiler</element:strong> can be used to localize apps built with <element:a>AdonisJS</element:a> ."
 60 |         },
 61 |         "2/declaration/body/0/argument/3/1/5": {
 62 |           "type": "element",
 63 |           "hash": "9ffb5f98cf11c88f3903e060f4028b46",
 64 |           "context": "",
 65 |           "skip": false,
 66 |           "overrides": {},
 67 |           "content": "To switch between locales, use the following dropdown:"
 68 |         },
 69 |         "3/declaration/body/0/argument/1-title": {
 70 |           "type": "attribute",
 71 |           "hash": "7c2d68be7446e6de191c11d53f1e07b4",
 72 |           "context": "",
 73 |           "skip": false,
 74 |           "overrides": {},
 75 |           "content": "Homepage"
 76 |         },
 77 |         "3/declaration/body/0/argument/3/1/1": {
 78 |           "type": "element",
 79 |           "hash": "0468579ef2fbc83c9d520c2f2f1c5059",
 80 |           "context": "",
 81 |           "skip": false,
 82 |           "overrides": {},
 83 |           "content": "Hello, world!"
 84 |         },
 85 |         "3/declaration/body/0/argument/3/1/3": {
 86 |           "type": "element",
 87 |           "hash": "82b29979a52b215b94b2e811e8c03005",
 88 |           "context": "",
 89 |           "skip": false,
 90 |           "overrides": {},
 91 |           "content": "This is an example app that demonstrates how <element:strong>Lingo.dev Compiler</element:strong> can be used to localize apps built with <element:a>AdonisJS</element:a> ."
 92 |         },
 93 |         "3/declaration/body/0/argument/3/1/5": {
 94 |           "type": "element",
 95 |           "hash": "9ffb5f98cf11c88f3903e060f4028b46",
 96 |           "context": "",
 97 |           "skip": false,
 98 |           "overrides": {},
 99 |           "content": "To switch between locales, use the following dropdown:"
100 |         }
101 |       }
102 |     }
103 |   }
104 | }
105 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/utils/key-matching.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import {
  3 |   matchesKeyPattern,
  4 |   filterEntriesByPattern,
  5 |   formatDisplayValue,
  6 | } from "./key-matching";
  7 | 
  8 | describe("matchesKeyPattern", () => {
  9 |   it("should match keys with prefix matching", () => {
 10 |     const patterns = ["api", "settings"];
 11 | 
 12 |     expect(matchesKeyPattern("api/users", patterns)).toBe(true);
 13 |     expect(matchesKeyPattern("api/posts", patterns)).toBe(true);
 14 |     expect(matchesKeyPattern("settings/theme", patterns)).toBe(true);
 15 |     expect(matchesKeyPattern("other/key", patterns)).toBe(false);
 16 |   });
 17 | 
 18 |   it("should match keys with glob patterns", () => {
 19 |     const patterns = ["api/*/users", "settings/*"];
 20 | 
 21 |     expect(matchesKeyPattern("api/v1/users", patterns)).toBe(true);
 22 |     expect(matchesKeyPattern("api/v2/users", patterns)).toBe(true);
 23 |     expect(matchesKeyPattern("settings/theme", patterns)).toBe(true);
 24 |     expect(matchesKeyPattern("settings/notifications", patterns)).toBe(true);
 25 |     expect(matchesKeyPattern("api/users", patterns)).toBe(false);
 26 |   });
 27 | 
 28 |   it("should return false for empty patterns", () => {
 29 |     expect(matchesKeyPattern("any/key", [])).toBe(false);
 30 |   });
 31 | 
 32 |   it("should handle complex glob patterns", () => {
 33 |     const patterns = ["steps/*/type", "learningGoals/*/goal"];
 34 | 
 35 |     expect(matchesKeyPattern("steps/0/type", patterns)).toBe(true);
 36 |     expect(matchesKeyPattern("steps/1/type", patterns)).toBe(true);
 37 |     expect(matchesKeyPattern("learningGoals/0/goal", patterns)).toBe(true);
 38 |     expect(matchesKeyPattern("steps/0/name", patterns)).toBe(false);
 39 |   });
 40 | });
 41 | 
 42 | describe("filterEntriesByPattern", () => {
 43 |   it("should filter entries that match patterns", () => {
 44 |     const entries: [string, any][] = [
 45 |       ["api/users", "Users API"],
 46 |       ["api/posts", "Posts API"],
 47 |       ["settings/theme", "Dark"],
 48 |       ["other/key", "Value"],
 49 |     ];
 50 |     const patterns = ["api", "settings"];
 51 | 
 52 |     const result = filterEntriesByPattern(entries, patterns);
 53 | 
 54 |     expect(result).toHaveLength(3);
 55 |     expect(result).toEqual([
 56 |       ["api/users", "Users API"],
 57 |       ["api/posts", "Posts API"],
 58 |       ["settings/theme", "Dark"],
 59 |     ]);
 60 |   });
 61 | 
 62 |   it("should return empty array when no matches", () => {
 63 |     const entries: [string, any][] = [
 64 |       ["key1", "value1"],
 65 |       ["key2", "value2"],
 66 |     ];
 67 |     const patterns = ["nonexistent"];
 68 | 
 69 |     const result = filterEntriesByPattern(entries, patterns);
 70 | 
 71 |     expect(result).toHaveLength(0);
 72 |   });
 73 | });
 74 | 
 75 | describe("formatDisplayValue", () => {
 76 |   it("should return short strings as-is", () => {
 77 |     expect(formatDisplayValue("Hello")).toBe("Hello");
 78 |     expect(formatDisplayValue("Short text")).toBe("Short text");
 79 |   });
 80 | 
 81 |   it("should truncate long strings", () => {
 82 |     const longString = "a".repeat(100);
 83 |     const result = formatDisplayValue(longString);
 84 | 
 85 |     expect(result).toHaveLength(53); // 50 chars + "..."
 86 |     expect(result.endsWith("...")).toBe(true);
 87 |   });
 88 | 
 89 |   it("should use custom max length", () => {
 90 |     const text = "Hello, World!";
 91 |     const result = formatDisplayValue(text, 5);
 92 | 
 93 |     expect(result).toBe("Hello...");
 94 |   });
 95 | 
 96 |   it("should stringify non-string values", () => {
 97 |     expect(formatDisplayValue(42)).toBe("42");
 98 |     expect(formatDisplayValue(true)).toBe("true");
 99 |     expect(formatDisplayValue({ key: "value" })).toBe('{"key":"value"}');
100 |     expect(formatDisplayValue(null)).toBe("null");
101 |   });
102 | 
103 |   it("should handle arrays", () => {
104 |     expect(formatDisplayValue([1, 2, 3])).toBe("[1,2,3]");
105 |   });
106 | });
107 | 
```

--------------------------------------------------------------------------------
/packages/compiler/src/jsx-attribute-scope-inject.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { createCodeMutation } from "./_base";
  2 | import { getModuleExecutionMode, getOrCreateImport } from "./utils";
  3 | import * as t from "@babel/types";
  4 | import _ from "lodash";
  5 | import { ModuleId } from "./_const";
  6 | import { getJsxElementName, getNestedJsxElements } from "./utils/jsx-element";
  7 | import { collectJsxAttributeScopes } from "./utils/jsx-attribute-scope";
  8 | import { setJsxAttributeValue } from "./utils/jsx-attribute";
  9 | 
 10 | export const lingoJsxAttributeScopeInjectMutation = createCodeMutation(
 11 |   (payload) => {
 12 |     const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
 13 |     const jsxAttributeScopes = collectJsxAttributeScopes(payload.ast);
 14 | 
 15 |     for (const [jsxScope, attributes] of jsxAttributeScopes) {
 16 |       // Import LingoComponent based on the module execution mode
 17 |       const packagePath =
 18 |         mode === "client" ? ModuleId.ReactClient : ModuleId.ReactRSC;
 19 |       const lingoComponentImport = getOrCreateImport(payload.ast, {
 20 |         moduleName: packagePath,
 21 |         exportedName: "LingoAttributeComponent",
 22 |       });
 23 | 
 24 |       // Get the original JSX element name
 25 |       const originalJsxElementName = getJsxElementName(jsxScope);
 26 |       if (!originalJsxElementName) {
 27 |         continue;
 28 |       }
 29 | 
 30 |       // Replace the name with the lingo component
 31 |       jsxScope.node.openingElement.name = t.jsxIdentifier(
 32 |         lingoComponentImport.importedName,
 33 |       );
 34 |       if (jsxScope.node.closingElement) {
 35 |         jsxScope.node.closingElement.name = t.jsxIdentifier(
 36 |           lingoComponentImport.importedName,
 37 |         );
 38 |       }
 39 | 
 40 |       // Add $attrAs ($as) prop
 41 |       const as = /^[A-Z]/.test(originalJsxElementName)
 42 |         ? t.jsxExpressionContainer(t.identifier(originalJsxElementName))
 43 |         : t.stringLiteral(originalJsxElementName);
 44 | 
 45 |       jsxScope.node.openingElement.attributes.push(
 46 |         t.jsxAttribute(t.jsxIdentifier("$attrAs"), as),
 47 |       );
 48 | 
 49 |       // Add $fileKey prop
 50 |       setJsxAttributeValue(jsxScope, "$fileKey", payload.relativeFilePath);
 51 | 
 52 |       // Add $attributes prop
 53 |       setJsxAttributeValue(
 54 |         jsxScope,
 55 |         "$attributes",
 56 |         t.objectExpression(
 57 |           attributes.map((attributeDefinition) => {
 58 |             const [attribute, key = ""] = attributeDefinition.split(":");
 59 |             return t.objectProperty(
 60 |               t.stringLiteral(attribute),
 61 |               t.stringLiteral(key),
 62 |             );
 63 |           }),
 64 |         ),
 65 |       );
 66 | 
 67 |       // // Extract $variables from original JSX scope
 68 |       // const $variables = getJsxVariables(originalJsxScope);
 69 |       // if ($variables.properties.length > 0) {
 70 |       //   setJsxAttributeValue(jsxScope, "$variables", $variables);
 71 |       // }
 72 | 
 73 |       // // Extract nested JSX elements
 74 |       // const $elements = getNestedJsxElements(originalJsxScope);
 75 |       // if ($elements.elements.length > 0) {
 76 |       //   setJsxAttributeValue(jsxScope, "$elements", $elements);
 77 |       // }
 78 | 
 79 |       if (mode === "server") {
 80 |         // Add $loadDictionary prop
 81 |         const loadDictionaryImport = getOrCreateImport(payload.ast, {
 82 |           exportedName: "loadDictionary",
 83 |           moduleName: ModuleId.ReactRSC,
 84 |         });
 85 |         setJsxAttributeValue(
 86 |           jsxScope,
 87 |           "$loadDictionary",
 88 |           t.arrowFunctionExpression(
 89 |             [t.identifier("locale")],
 90 |             t.callExpression(t.identifier(loadDictionaryImport.importedName), [
 91 |               t.identifier("locale"),
 92 |             ]),
 93 |           ),
 94 |         );
 95 |       }
 96 |     }
 97 | 
 98 |     return payload;
 99 |   },
100 | );
101 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/loaders/formatters/biome.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import path from "path";
  2 | import fs from "fs/promises";
  3 | import { Biome, Distribution } from "@biomejs/js-api";
  4 | import { parse as parseJsonc } from "jsonc-parser";
  5 | import { ILoader } from "../_types";
  6 | import { createBaseFormatterLoader } from "./_base";
  7 | 
  8 | export type BiomeLoaderOptions = {
  9 |   bucketPathPattern: string;
 10 |   stage?: "pull" | "push" | "both";
 11 |   alwaysFormat?: boolean;
 12 | };
 13 | 
 14 | export default function createBiomeLoader(
 15 |   options: BiomeLoaderOptions,
 16 | ): ILoader<string, string> {
 17 |   return createBaseFormatterLoader(options, async (data, filePath) => {
 18 |     return await formatDataWithBiome(data, filePath, options);
 19 |   });
 20 | }
 21 | 
 22 | async function findBiomeConfig(startPath: string): Promise<string | null> {
 23 |   let currentDir = path.dirname(startPath);
 24 |   const root = path.parse(currentDir).root;
 25 | 
 26 |   while (currentDir !== root) {
 27 |     for (const configName of ["biome.json", "biome.jsonc"]) {
 28 |       const configPath = path.join(currentDir, configName);
 29 |       try {
 30 |         await fs.access(configPath);
 31 |         return configPath;
 32 |       } catch {
 33 |         // Config file doesn't exist, continue searching
 34 |       }
 35 |     }
 36 | 
 37 |     const parentDir = path.dirname(currentDir);
 38 |     if (parentDir === currentDir) break;
 39 |     currentDir = parentDir;
 40 |   }
 41 | 
 42 |   return null;
 43 | }
 44 | 
 45 | async function formatDataWithBiome(
 46 |   data: string,
 47 |   filePath: string,
 48 |   options: BiomeLoaderOptions,
 49 | ): Promise<string> {
 50 |   let configPath: string | null = null;
 51 | 
 52 |   try {
 53 |     const biome = await Biome.create({
 54 |       distribution: Distribution.NODE,
 55 |     });
 56 | 
 57 |     // Open a project (required in v3.0.0+)
 58 |     const openResult = biome.openProject(".");
 59 |     const projectKey = openResult.projectKey;
 60 | 
 61 |     // Load config from biome.json/biome.jsonc if exists
 62 |     configPath = await findBiomeConfig(filePath);
 63 |     if (!configPath && !options.alwaysFormat) {
 64 |       console.log();
 65 |       console.log(
 66 |         `⚠️  Biome config not found for ${path.basename(filePath)} - skipping formatting`,
 67 |       );
 68 |       return data;
 69 |     }
 70 | 
 71 |     if (configPath) {
 72 |       const configContent = await fs.readFile(configPath, "utf-8");
 73 |       try {
 74 |         // Parse JSONC (JSON with comments) properly using jsonc-parser
 75 |         const config = parseJsonc(configContent);
 76 | 
 77 |         // WORKAROUND: Biome JS API v3 has a bug where applying the full config
 78 |         // causes formatter settings to be ignored. Apply only relevant sections.
 79 |         // Specifically, exclude $schema, vcs, and files from the config.
 80 |         const { $schema, vcs, files, ...relevantConfig } = config;
 81 | 
 82 |         biome.applyConfiguration(projectKey, relevantConfig);
 83 |       } catch (parseError) {
 84 |         throw new Error(
 85 |           `Invalid Biome configuration in ${configPath}: ${parseError instanceof Error ? parseError.message : "JSON parse error"}`,
 86 |         );
 87 |       }
 88 |     }
 89 | 
 90 |     const formatted = biome.formatContent(projectKey, data, {
 91 |       filePath,
 92 |     });
 93 | 
 94 |     return formatted.content;
 95 |   } catch (error) {
 96 |     // Extract error message from Biome
 97 |     const errorMessage =
 98 |       error instanceof Error
 99 |         ? error.message || (error as any).stackTrace?.toString().split("\n")[0]
100 |         : "";
101 | 
102 |     if (errorMessage?.includes("does not exist in the workspace")) {
103 |       // Biome says "file does not exist in workspace" for unsupported formats - skip
104 |     } else {
105 |       console.log(`⚠️  Biome skipped ${path.basename(filePath)}`);
106 |       if (errorMessage) {
107 |         console.log(`   ${errorMessage}`);
108 |       }
109 |     }
110 | 
111 |     return data; // Fallback to unformatted
112 |   }
113 | }
114 | 
```

--------------------------------------------------------------------------------
/packages/spec/src/locales.spec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from "vitest";
 2 | import {
 3 |   getLocaleCodeDelimiter,
 4 |   normalizeLocale,
 5 |   resolveLocaleCode,
 6 |   resolveOverriddenLocale,
 7 | } from "./locales";
 8 | 
 9 | describe("normalizeLocale", () => {
10 |   it("should return normalized locale for short locale codes", () => {
11 |     expect(normalizeLocale("en")).toEqual("en");
12 |     expect(normalizeLocale("fr")).toEqual("fr");
13 |   });
14 | 
15 |   it("should return normalized locale for full locale codes", () => {
16 |     expect(normalizeLocale("en-US")).toEqual("en-US");
17 |     expect(normalizeLocale("fr-FR")).toEqual("fr-FR");
18 |   });
19 | 
20 |   it("should return normalized locale for full underscore locale codes", () => {
21 |     expect(normalizeLocale("en_US")).toEqual("en-US");
22 |     expect(normalizeLocale("fr_FR")).toEqual("fr-FR");
23 |     expect(normalizeLocale("zh_Hans_CN")).toEqual("zh-Hans-CN");
24 |   });
25 | 
26 |   it("should return normalized locale for full explicit region locale codes", () => {
27 |     expect(normalizeLocale("en-rUS")).toEqual("en-US");
28 |     expect(normalizeLocale("fr-rFR")).toEqual("fr-FR");
29 |     expect(normalizeLocale("zh-rCN")).toEqual("zh-CN");
30 |   });
31 | });
32 | 
33 | describe("resolveLocaleCode", () => {
34 |   it("should resolve a short locale code to the first full locale code in the map", () => {
35 |     expect(resolveLocaleCode("en")).toEqual("en-US");
36 |     expect(resolveLocaleCode("fr")).toEqual("fr-FR");
37 |     expect(resolveLocaleCode("az")).toEqual("az-AZ");
38 |   });
39 | 
40 |   it("should return the full locale code if it is already provided", () => {
41 |     expect(resolveLocaleCode("en-US")).toEqual("en-US");
42 |     expect(resolveLocaleCode("fr-CA")).toEqual("fr-CA");
43 |     expect(resolveLocaleCode("es-MX")).toEqual("es-MX");
44 |   });
45 | 
46 |   it("should throw an error for an invalid or unsupported locale code", () => {
47 |     expect(() => resolveLocaleCode("az-US")).toThrow("Invalid locale code");
48 |     expect(() => resolveLocaleCode("au")).toThrow("Invalid locale code");
49 |   });
50 | 
51 |   it("should return first code for locales with multiple variants", () => {
52 |     expect(resolveLocaleCode("sr")).toEqual("sr-RS");
53 |     expect(resolveLocaleCode("zh")).toEqual("zh-CN");
54 |   });
55 | });
56 | 
57 | describe("getLocaleCodeDelimiter", () => {
58 |   it("should return '-' for locale codes with hyphen delimiter", () => {
59 |     expect(getLocaleCodeDelimiter("en-US")).toEqual("-");
60 |     expect(getLocaleCodeDelimiter("fr-FR")).toEqual("-");
61 |   });
62 | 
63 |   it("should return '_' for locale codes with underscore delimiter", () => {
64 |     expect(getLocaleCodeDelimiter("en_US")).toEqual("_");
65 |     expect(getLocaleCodeDelimiter("fr_FR")).toEqual("_");
66 |   });
67 | 
68 |   it("should return undefined for locale codes without a recognized delimiter", () => {
69 |     expect(getLocaleCodeDelimiter("enUS")).toBeNull();
70 |     expect(getLocaleCodeDelimiter("frFR")).toBeNull();
71 |     expect(getLocaleCodeDelimiter("kaGE")).toBeNull();
72 |   });
73 | });
74 | 
75 | describe("resolveOverridenLocale", () => {
76 |   it("should return the same locale if no delimiter is provided", () => {
77 |     expect(resolveOverriddenLocale("en-US")).toEqual("en-US");
78 |     expect(resolveOverriddenLocale("fr_FR")).toEqual("fr_FR");
79 |   });
80 | 
81 |   it("should replace the delimiter with the specified one", () => {
82 |     expect(resolveOverriddenLocale("en-US", "_")).toEqual("en_US");
83 |     expect(resolveOverriddenLocale("fr_FR", "-")).toEqual("fr-FR");
84 |   });
85 | 
86 |   it("should return the same locale if no recognized delimiter is found", () => {
87 |     expect(resolveOverriddenLocale("enUS", "_")).toEqual("enUS");
88 |     expect(resolveOverriddenLocale("frFR", "-")).toEqual("frFR");
89 |   });
90 | });
91 | 
```

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

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import createFlutterLoader from "./flutter";
  3 | 
  4 | const locale = "en";
  5 | const originalLocale = "en";
  6 | 
  7 | describe("createFlutterLoader", () => {
  8 |   describe("pull", () => {
  9 |     it("should remove metadata keys starting with @", async () => {
 10 |       const loader = createFlutterLoader();
 11 |       loader.setDefaultLocale(locale);
 12 |       const input = {
 13 |         "@metadata": "some-data",
 14 |         hello: "world",
 15 |         another_key: "another_value",
 16 |         "@@locale": "en",
 17 |       };
 18 |       const expected = {
 19 |         hello: "world",
 20 |         another_key: "another_value",
 21 |       };
 22 |       const result = await loader.pull("en", input);
 23 |       expect(result).toEqual(expected);
 24 |     });
 25 | 
 26 |     it("should return an empty object if all keys are metadata", async () => {
 27 |       const loader = createFlutterLoader();
 28 |       loader.setDefaultLocale(locale);
 29 |       const input = {
 30 |         "@metadata": "some-data",
 31 |         "@@locale": "en",
 32 |       };
 33 |       const expected = {};
 34 |       const result = await loader.pull("en", input);
 35 |       expect(result).toEqual(expected);
 36 |     });
 37 | 
 38 |     it("should return the same object if no keys are metadata", async () => {
 39 |       const loader = createFlutterLoader();
 40 |       loader.setDefaultLocale(locale);
 41 |       const input = {
 42 |         hello: "world",
 43 |         another_key: "another_value",
 44 |       };
 45 |       const expected = {
 46 |         hello: "world",
 47 |         another_key: "another_value",
 48 |       };
 49 |       const result = await loader.pull("en", input);
 50 |       expect(result).toEqual(expected);
 51 |     });
 52 | 
 53 |     it("should handle empty input", async () => {
 54 |       const loader = createFlutterLoader();
 55 |       loader.setDefaultLocale(locale);
 56 |       const input = {};
 57 |       const expected = {};
 58 |       const result = await loader.pull("en", input);
 59 |       expect(result).toEqual(expected);
 60 |     });
 61 |   });
 62 | 
 63 |   describe("push", () => {
 64 |     it("should merge data and add locale", async () => {
 65 |       const loader = createFlutterLoader();
 66 |       loader.setDefaultLocale(locale);
 67 |       const originalInput = {
 68 |         hello: "world",
 69 |         "@metadata": "some-data",
 70 |       };
 71 |       await loader.pull(originalLocale, originalInput);
 72 |       const data = {
 73 |         foo: "bar",
 74 |         hello: "monde",
 75 |       };
 76 |       const expected = {
 77 |         hello: "monde",
 78 |         foo: "bar",
 79 |         "@metadata": "some-data",
 80 |         "@@locale": "fr",
 81 |       };
 82 |       const result = await loader.push("fr", data);
 83 |       expect(result).toEqual(expected);
 84 |     });
 85 | 
 86 |     it("should handle empty original input", async () => {
 87 |       const loader = createFlutterLoader();
 88 |       loader.setDefaultLocale(locale);
 89 |       const originalInput = {};
 90 |       await loader.pull(originalLocale, originalInput);
 91 |       const data = {
 92 |         foo: "bar",
 93 |       };
 94 |       const expected = {
 95 |         foo: "bar",
 96 |         "@@locale": "en",
 97 |       };
 98 |       const result = await loader.push("en", data);
 99 |       expect(result).toEqual(expected);
100 |     });
101 | 
102 |     it("should handle empty data, not add extra keys from originalInput", async () => {
103 |       const loader = createFlutterLoader();
104 |       loader.setDefaultLocale(locale);
105 |       const originalInput = {
106 |         hello: "world",
107 |       };
108 |       await loader.pull(originalLocale, originalInput);
109 |       const data = {
110 |         goodbye: "moon",
111 |       };
112 |       const expected = {
113 |         goodbye: "moon",
114 |         "@@locale": "en",
115 |       };
116 |       const result = await loader.push("en", data);
117 |       expect(result).toEqual(expected);
118 |     });
119 |   });
120 | });
121 | 
```

--------------------------------------------------------------------------------
/packages/cli/src/cli/cmd/ci/platforms/github.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Octokit } from "octokit";
  2 | import { PlatformKit } from "./_base";
  3 | import Z from "zod";
  4 | 
  5 | export class GitHubPlatformKit extends PlatformKit {
  6 |   private _octokit?: Octokit;
  7 | 
  8 |   private get octokit(): Octokit {
  9 |     if (!this._octokit) {
 10 |       this._octokit = new Octokit({ auth: this.platformConfig.ghToken });
 11 |     }
 12 |     return this._octokit;
 13 |   }
 14 | 
 15 |   async branchExists({ branch }: { branch: string }) {
 16 |     return await this.octokit.rest.repos
 17 |       .getBranch({
 18 |         branch,
 19 |         owner: this.platformConfig.repositoryOwner,
 20 |         repo: this.platformConfig.repositoryName,
 21 |       })
 22 |       .then((r) => r.data)
 23 |       .then((v) => !!v)
 24 |       .catch((r) => (r.status === 404 ? false : Promise.reject(r)));
 25 |   }
 26 | 
 27 |   async getOpenPullRequestNumber({ branch }: { branch: string }) {
 28 |     return await this.octokit.rest.pulls
 29 |       .list({
 30 |         head: `${this.platformConfig.repositoryOwner}:${branch}`,
 31 |         owner: this.platformConfig.repositoryOwner,
 32 |         repo: this.platformConfig.repositoryName,
 33 |         base: this.platformConfig.baseBranchName,
 34 |         state: "open",
 35 |       })
 36 |       .then(({ data }) => data[0])
 37 |       .then((pr) => pr?.number);
 38 |   }
 39 | 
 40 |   async closePullRequest({ pullRequestNumber }: { pullRequestNumber: number }) {
 41 |     await this.octokit.rest.pulls.update({
 42 |       pull_number: pullRequestNumber,
 43 |       owner: this.platformConfig.repositoryOwner,
 44 |       repo: this.platformConfig.repositoryName,
 45 |       state: "closed",
 46 |     });
 47 |   }
 48 | 
 49 |   async createPullRequest({
 50 |     head,
 51 |     title,
 52 |     body,
 53 |   }: {
 54 |     head: string;
 55 |     title: string;
 56 |     body?: string;
 57 |   }) {
 58 |     return await this.octokit.rest.pulls
 59 |       .create({
 60 |         head,
 61 |         title,
 62 |         body,
 63 |         owner: this.platformConfig.repositoryOwner,
 64 |         repo: this.platformConfig.repositoryName,
 65 |         base: this.platformConfig.baseBranchName,
 66 |       })
 67 |       .then(({ data }) => data.number);
 68 |   }
 69 | 
 70 |   async commentOnPullRequest({
 71 |     pullRequestNumber,
 72 |     body,
 73 |   }: {
 74 |     pullRequestNumber: number;
 75 |     body: string;
 76 |   }) {
 77 |     await this.octokit.rest.issues.createComment({
 78 |       issue_number: pullRequestNumber,
 79 |       body,
 80 |       owner: this.platformConfig.repositoryOwner,
 81 |       repo: this.platformConfig.repositoryName,
 82 |     });
 83 |   }
 84 | 
 85 |   async gitConfig() {
 86 |     const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;
 87 |     const { processOwnCommits } = this.config;
 88 | 
 89 |     if (ghToken && processOwnCommits) {
 90 |       console.log(
 91 |         "Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again.",
 92 |       );
 93 | 
 94 |       const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;
 95 | 
 96 |       super.gitConfig(ghToken, url);
 97 |     }
 98 |   }
 99 | 
100 |   get platformConfig() {
101 |     const env = Z.object({
102 |       GITHUB_REPOSITORY: Z.string(),
103 |       GITHUB_REPOSITORY_OWNER: Z.string(),
104 |       GITHUB_REF_NAME: Z.string(),
105 |       GITHUB_HEAD_REF: Z.string(),
106 |       GH_TOKEN: Z.string().optional(),
107 |     }).parse(process.env);
108 | 
109 |     const baseBranchName = !env.GITHUB_REF_NAME.endsWith("/merge")
110 |       ? env.GITHUB_REF_NAME
111 |       : env.GITHUB_HEAD_REF;
112 | 
113 |     return {
114 |       ghToken: env.GH_TOKEN,
115 |       baseBranchName,
116 |       repositoryOwner: env.GITHUB_REPOSITORY_OWNER,
117 |       repositoryName: env.GITHUB_REPOSITORY.split("/")[1],
118 |     };
119 |   }
120 | 
121 |   buildPullRequestUrl(pullRequestNumber: number) {
122 |     const { repositoryOwner, repositoryName } = this.platformConfig;
123 |     return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;
124 |   }
125 | }
126 | 
```

--------------------------------------------------------------------------------
/packages/locales/src/parser.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, it, expect } from "vitest";
  2 | import {
  3 |   parseLocale,
  4 |   getLanguageCode,
  5 |   getScriptCode,
  6 |   getRegionCode,
  7 | } from "./parser";
  8 | 
  9 | describe("parseLocale", () => {
 10 |   it("should parse basic language-region locales with hyphen", () => {
 11 |     expect(parseLocale("en-US")).toEqual({
 12 |       language: "en",
 13 |       region: "US",
 14 |     });
 15 |   });
 16 | 
 17 |   it("should parse basic language-region locales with underscore", () => {
 18 |     expect(parseLocale("en_US")).toEqual({
 19 |       language: "en",
 20 |       region: "US",
 21 |     });
 22 |   });
 23 | 
 24 |   it("should parse language-script-region locales with hyphen", () => {
 25 |     expect(parseLocale("zh-Hans-CN")).toEqual({
 26 |       language: "zh",
 27 |       script: "Hans",
 28 |       region: "CN",
 29 |     });
 30 |   });
 31 | 
 32 |   it("should parse language-script-region locales with underscore", () => {
 33 |     expect(parseLocale("zh_Hans_CN")).toEqual({
 34 |       language: "zh",
 35 |       script: "Hans",
 36 |       region: "CN",
 37 |     });
 38 |   });
 39 | 
 40 |   it("should parse language-only locales", () => {
 41 |     expect(parseLocale("es")).toEqual({
 42 |       language: "es",
 43 |     });
 44 |   });
 45 | 
 46 |   it("should parse complex script locales", () => {
 47 |     expect(parseLocale("sr-Cyrl-RS")).toEqual({
 48 |       language: "sr",
 49 |       script: "Cyrl",
 50 |       region: "RS",
 51 |     });
 52 |   });
 53 | 
 54 |   it("should handle numeric region codes", () => {
 55 |     expect(parseLocale("es-419")).toEqual({
 56 |       language: "es",
 57 |       region: "419",
 58 |     });
 59 |   });
 60 | 
 61 |   it("should normalize language to lowercase", () => {
 62 |     expect(parseLocale("EN-US")).toEqual({
 63 |       language: "en",
 64 |       region: "US",
 65 |     });
 66 |   });
 67 | 
 68 |   it("should normalize region to uppercase", () => {
 69 |     expect(parseLocale("en-us")).toEqual({
 70 |       language: "en",
 71 |       region: "US",
 72 |     });
 73 |   });
 74 | 
 75 |   it("should preserve script case", () => {
 76 |     expect(parseLocale("zh-hans-cn")).toEqual({
 77 |       language: "zh",
 78 |       script: "hans",
 79 |       region: "CN",
 80 |     });
 81 |   });
 82 | 
 83 |   it("should throw error for invalid locale format", () => {
 84 |     expect(() => parseLocale("invalid")).toThrow(
 85 |       "Invalid locale format: invalid",
 86 |     );
 87 |   });
 88 | 
 89 |   it("should throw error for empty string", () => {
 90 |     expect(() => parseLocale("")).toThrow("Locale cannot be empty");
 91 |   });
 92 | 
 93 |   it("should throw error for non-string input", () => {
 94 |     expect(() => parseLocale(null as any)).toThrow("Locale must be a string");
 95 |   });
 96 | });
 97 | 
 98 | describe("getLanguageCode", () => {
 99 |   it("should extract language code from various formats", () => {
100 |     expect(getLanguageCode("en-US")).toBe("en");
101 |     expect(getLanguageCode("zh-Hans-CN")).toBe("zh");
102 |     expect(getLanguageCode("es-MX")).toBe("es");
103 |     expect(getLanguageCode("fr_CA")).toBe("fr");
104 |     expect(getLanguageCode("es")).toBe("es");
105 |   });
106 | });
107 | 
108 | describe("getScriptCode", () => {
109 |   it("should extract script code when present", () => {
110 |     expect(getScriptCode("zh-Hans-CN")).toBe("Hans");
111 |     expect(getScriptCode("zh-Hant-TW")).toBe("Hant");
112 |     expect(getScriptCode("sr-Cyrl-RS")).toBe("Cyrl");
113 |   });
114 | 
115 |   it("should return null when script is not present", () => {
116 |     expect(getScriptCode("en-US")).toBeNull();
117 |     expect(getScriptCode("es")).toBeNull();
118 |   });
119 | });
120 | 
121 | describe("getRegionCode", () => {
122 |   it("should extract region code when present", () => {
123 |     expect(getRegionCode("en-US")).toBe("US");
124 |     expect(getRegionCode("zh-Hans-CN")).toBe("CN");
125 |     expect(getRegionCode("fr_CA")).toBe("CA");
126 |   });
127 | 
128 |   it("should return null when region is not present", () => {
129 |     expect(getRegionCode("es")).toBeNull();
130 |     expect(getRegionCode("zh-Hans")).toBeNull();
131 |   });
132 | });
133 | 
```
Page 5/20FirstPrevNextLast