feat(twenty-sdk): extract & compile app translations into the manifest (#22236)

## Summary

**PR 2/4** of the app-metadata-translations stack. Gives app developers
the authoring side, as part of the normal manifest build — and it stays
out of the way of developers who don't translate.

- `twenty-sdk` CLI i18n pipeline: collect translatable strings from the
manifest, generate value-as-key message ids (`sha256(value)` truncated,
byte-identical to the server's `generateMessageId`), a `dev
i18n-extract` command to scaffold per-locale catalog files, and a
compile step folded into `build` that emits `manifest.translations`.
- Opt-in: no `locales/` dir → `compileApplicationTranslations` returns
`undefined` → manifest is unchanged.
- Adds an optional `locale` to the front-component execution context so
components can translate against the host locale.

## Stack
Stacks on #22235 (PR 1/4). Base branch:
`claude/app-translation-1-runtime-resolution`.

## Tests
Unit (vitest): extract/compile round-trip + message-id determinism.

## Verification note
`yarn install` could not complete in the remote dev environment, so
typecheck/lint/tests were not run locally — **CI is the source of
truth**.

https://claude.ai/code/session_01NiE7o3cd3zCLZarVkJa6UA

---
_Generated by [Claude
Code](https://claude.ai/code/session_01NiE7o3cd3zCLZarVkJa6UA)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22236?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Félix Malfait
2026-06-28 07:36:48 +02:00
committed by GitHub
parent 41c10b9ee7
commit d81b3c3fa3
13 changed files with 407 additions and 2 deletions
@@ -7,6 +7,7 @@ import { AppBuildCommand } from './build';
import { AppDevCommand } from './dev';
import { AppDevOnceCommand } from './dev-once';
import { AppGenerateClientCommand } from './generate-client';
import { AppI18nExtractCommand } from './i18n-extract';
import { AppTypecheckCommand } from './typecheck';
import { registerDevFunctionCommands } from './function';
@@ -17,6 +18,7 @@ export const registerDevCommands = (program: Command): void => {
const typecheckCommand = new AppTypecheckCommand();
const addCommand = new EntityAddCommand();
const generateClientCommand = new AppGenerateClientCommand();
const i18nExtractCommand = new AppI18nExtractCommand();
const devAction = async (
appPath: string | undefined,
@@ -123,5 +125,19 @@ export const registerDevCommands = (program: Command): void => {
});
});
program
.command('dev:i18n-extract [appPath]')
.description('Extract translatable strings into locales/ catalogs')
.option(
'--locale <locale>',
'Scaffold an empty catalog for a target locale (e.g. fr-FR)',
)
.action(async (appPath, options) => {
await i18nExtractCommand.execute({
appPath: formatPath(appPath),
locale: options.locale,
});
});
registerDevFunctionCommands(program);
};