@proulr/rosetta 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/ROSETTA.md +10 -0
- package/package.json +5 -2
- package/src/generated/locale-messages.ts +26 -0
- package/src/locales/en/auth.json +14 -1
- package/src/locales/pt-BR/auth.json +14 -1
- package/src/resolve-locale.test.ts +44 -0
package/docs/ROSETTA.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Pacote de i18n do Proulr (inspirado na Rosetta da Nubank): **única fonte de copy** para BFFs (wire SDUI) e app nativo (auth, alerts, hybrids).
|
|
4
4
|
|
|
5
|
+
**Obrigações para agentes / PRs:**
|
|
6
|
+
|
|
7
|
+
| Artefato | Path |
|
|
8
|
+
|----------|------|
|
|
9
|
+
| Rule (always apply) | `.cursor/rules/rosetta-i18n.mdc` |
|
|
10
|
+
| Skill | `.cursor/skills/proulr-rosetta-i18n/SKILL.md` |
|
|
11
|
+
| AGENTS | `AGENTS.md` → seção i18n / Rosetta |
|
|
12
|
+
|
|
13
|
+
Toda tarefa com copy de produto deve seguir esse padrão (zero literal em app/BFF).
|
|
14
|
+
|
|
5
15
|
---
|
|
6
16
|
|
|
7
17
|
## O que foi feito
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proulr/rosetta",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"validate:locales": "node ./scripts/validate-locales.mjs",
|
|
26
26
|
"validate:locales:strict": "node ./scripts/validate-locales.mjs --strict",
|
|
27
27
|
"typecheck": "npm run locale:generate && npm run validate:locales && tsc --noEmit",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
28
30
|
"prepublishOnly": "npm run typecheck",
|
|
29
31
|
"lint": "eslint .",
|
|
30
32
|
"lint:fix": "eslint . --fix",
|
|
@@ -41,7 +43,8 @@
|
|
|
41
43
|
"husky": "^9.1.7",
|
|
42
44
|
"lint-staged": "^16.4.0",
|
|
43
45
|
"typescript": "~6.0.3",
|
|
44
|
-
"typescript-eslint": "^8.61.1"
|
|
46
|
+
"typescript-eslint": "^8.61.1",
|
|
47
|
+
"vitest": "^3.2.7"
|
|
45
48
|
},
|
|
46
49
|
"engines": {
|
|
47
50
|
"node": ">=22"
|
|
@@ -44,6 +44,19 @@ export const PT_BR_MESSAGES = {
|
|
|
44
44
|
"alerts.unavailable": "Indisponível",
|
|
45
45
|
"alerts.wait": "Aguarde",
|
|
46
46
|
"alerts.yourHandle": "Seu @",
|
|
47
|
+
"auth.ageGate.banner": "É rápido e seguro: o vídeo serve só para verificar idade adulta — não pedimos documento nem data de nascimento nesta etapa.",
|
|
48
|
+
"auth.ageGate.cta": "Continuar — leva poucos segundos",
|
|
49
|
+
"auth.ageGate.errorTitle": "Erro na verificação",
|
|
50
|
+
"auth.ageGate.manualReviewHint": "Se a validação automática falhar, revisamos manualmente — você não perde o cadastro.",
|
|
51
|
+
"auth.ageGate.processingSubtitle": "Analisando seu vídeo. Em geral leva poucos segundos.",
|
|
52
|
+
"auth.ageGate.processingTitle": "Verificando idade",
|
|
53
|
+
"auth.ageGate.steps": "1. Centralize seu rosto no oval\n2. Grave um vídeo de 3 segundos\n3. Aguarde a validação automática",
|
|
54
|
+
"auth.ageGate.subtitle": "Só um vídeo de 3 segundos para confirmar que você tem 18 anos ou mais",
|
|
55
|
+
"auth.ageGate.title": "Verificação rápida e segura",
|
|
56
|
+
"auth.ageGate.trustFastBody": "Vídeo de 3 segundos e validação automática",
|
|
57
|
+
"auth.ageGate.trustFastTitle": "Rápido",
|
|
58
|
+
"auth.ageGate.trustSafeBody": "Usado só para confirmar idade. Seus dados ficam protegidos",
|
|
59
|
+
"auth.ageGate.trustSafeTitle": "Seguro",
|
|
47
60
|
"auth.common.back": "Voltar",
|
|
48
61
|
"auth.locale.changeA11y": "Alterar idioma",
|
|
49
62
|
"auth.locale.currentA11y": "Idioma atual: {label}",
|
|
@@ -502,6 +515,19 @@ export const EN_MESSAGES: FlatMessages = {
|
|
|
502
515
|
"alerts.unavailable": "Unavailable",
|
|
503
516
|
"alerts.wait": "Please wait",
|
|
504
517
|
"alerts.yourHandle": "Your @",
|
|
518
|
+
"auth.ageGate.banner": "It’s quick and secure: the video is only used to verify adult age — we don’t ask for an ID or date of birth in this step.",
|
|
519
|
+
"auth.ageGate.cta": "Continue — it only takes a moment",
|
|
520
|
+
"auth.ageGate.errorTitle": "Verification error",
|
|
521
|
+
"auth.ageGate.manualReviewHint": "If automatic validation fails, we review it manually — you won’t lose your signup.",
|
|
522
|
+
"auth.ageGate.processingSubtitle": "Analyzing your video. It usually takes a few seconds.",
|
|
523
|
+
"auth.ageGate.processingTitle": "Verifying age",
|
|
524
|
+
"auth.ageGate.steps": "1. Center your face in the oval\n2. Record a 3-second video\n3. Wait for automatic validation",
|
|
525
|
+
"auth.ageGate.subtitle": "Just a 3-second video to confirm you’re 18 or older",
|
|
526
|
+
"auth.ageGate.title": "Quick and secure verification",
|
|
527
|
+
"auth.ageGate.trustFastBody": "3-second video and automatic validation",
|
|
528
|
+
"auth.ageGate.trustFastTitle": "Quick",
|
|
529
|
+
"auth.ageGate.trustSafeBody": "Used only to confirm age. Your data stays protected",
|
|
530
|
+
"auth.ageGate.trustSafeTitle": "Secure",
|
|
505
531
|
"auth.common.back": "Back",
|
|
506
532
|
"auth.locale.changeA11y": "Change language",
|
|
507
533
|
"auth.locale.currentA11y": "Current language: {label}",
|
package/src/locales/en/auth.json
CHANGED
|
@@ -42,5 +42,18 @@
|
|
|
42
42
|
"auth.signup.validReady": "All set — you can create your account",
|
|
43
43
|
"auth.welcome.createAccount": "Create account",
|
|
44
44
|
"auth.welcome.heroCaption": "Join the night hunt",
|
|
45
|
-
"auth.welcome.signIn": "Sign in"
|
|
45
|
+
"auth.welcome.signIn": "Sign in",
|
|
46
|
+
"auth.ageGate.title": "Quick and secure verification",
|
|
47
|
+
"auth.ageGate.subtitle": "Just a 3-second video to confirm you’re 18 or older",
|
|
48
|
+
"auth.ageGate.banner": "It’s quick and secure: the video is only used to verify adult age — we don’t ask for an ID or date of birth in this step.",
|
|
49
|
+
"auth.ageGate.trustFastTitle": "Quick",
|
|
50
|
+
"auth.ageGate.trustFastBody": "3-second video and automatic validation",
|
|
51
|
+
"auth.ageGate.trustSafeTitle": "Secure",
|
|
52
|
+
"auth.ageGate.trustSafeBody": "Used only to confirm age. Your data stays protected",
|
|
53
|
+
"auth.ageGate.steps": "1. Center your face in the oval\n2. Record a 3-second video\n3. Wait for automatic validation",
|
|
54
|
+
"auth.ageGate.cta": "Continue — it only takes a moment",
|
|
55
|
+
"auth.ageGate.manualReviewHint": "If automatic validation fails, we review it manually — you won’t lose your signup.",
|
|
56
|
+
"auth.ageGate.processingTitle": "Verifying age",
|
|
57
|
+
"auth.ageGate.processingSubtitle": "Analyzing your video. It usually takes a few seconds.",
|
|
58
|
+
"auth.ageGate.errorTitle": "Verification error"
|
|
46
59
|
}
|
|
@@ -42,5 +42,18 @@
|
|
|
42
42
|
"auth.signup.validReady": "Tudo certo — pode criar sua conta",
|
|
43
43
|
"auth.welcome.createAccount": "Criar conta",
|
|
44
44
|
"auth.welcome.heroCaption": "Entre na caçada noturna",
|
|
45
|
-
"auth.welcome.signIn": "Entrar"
|
|
45
|
+
"auth.welcome.signIn": "Entrar",
|
|
46
|
+
"auth.ageGate.title": "Verificação rápida e segura",
|
|
47
|
+
"auth.ageGate.subtitle": "Só um vídeo de 3 segundos para confirmar que você tem 18 anos ou mais",
|
|
48
|
+
"auth.ageGate.banner": "É rápido e seguro: o vídeo serve só para verificar idade adulta — não pedimos documento nem data de nascimento nesta etapa.",
|
|
49
|
+
"auth.ageGate.trustFastTitle": "Rápido",
|
|
50
|
+
"auth.ageGate.trustFastBody": "Vídeo de 3 segundos e validação automática",
|
|
51
|
+
"auth.ageGate.trustSafeTitle": "Seguro",
|
|
52
|
+
"auth.ageGate.trustSafeBody": "Usado só para confirmar idade. Seus dados ficam protegidos",
|
|
53
|
+
"auth.ageGate.steps": "1. Centralize seu rosto no oval\n2. Grave um vídeo de 3 segundos\n3. Aguarde a validação automática",
|
|
54
|
+
"auth.ageGate.cta": "Continuar — leva poucos segundos",
|
|
55
|
+
"auth.ageGate.manualReviewHint": "Se a validação automática falhar, revisamos manualmente — você não perde o cadastro.",
|
|
56
|
+
"auth.ageGate.processingTitle": "Verificando idade",
|
|
57
|
+
"auth.ageGate.processingSubtitle": "Analisando seu vídeo. Em geral leva poucos segundos.",
|
|
58
|
+
"auth.ageGate.errorTitle": "Erro na verificação"
|
|
46
59
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_PROULR_LOCALE,
|
|
4
|
+
resolveLocaleFromPref,
|
|
5
|
+
resolveLocaleFromQuery,
|
|
6
|
+
resolveLocaleFromSystemTag,
|
|
7
|
+
t,
|
|
8
|
+
tFormat,
|
|
9
|
+
} from './index';
|
|
10
|
+
|
|
11
|
+
describe('resolveLocaleFromQuery', () => {
|
|
12
|
+
it('defaults when query is empty', () => {
|
|
13
|
+
expect(resolveLocaleFromQuery(null)).toBe(DEFAULT_PROULR_LOCALE);
|
|
14
|
+
expect(resolveLocaleFromQuery({})).toBe(DEFAULT_PROULR_LOCALE);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('prefers locale over app_locale', () => {
|
|
18
|
+
expect(resolveLocaleFromQuery({ locale: 'en', app_locale: 'pt-BR' })).toBe('en');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('resolveLocaleFromPref / system tag', () => {
|
|
23
|
+
it('uses system locale when pref is system', () => {
|
|
24
|
+
expect(resolveLocaleFromPref('system', 'en')).toBe('en');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('maps language tags to supported locales', () => {
|
|
28
|
+
expect(resolveLocaleFromSystemTag('pt-BR')).toBe('pt-BR');
|
|
29
|
+
expect(resolveLocaleFromSystemTag('en-US')).toBe('en');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('t / tFormat', () => {
|
|
34
|
+
it('returns a non-empty string for a known key', () => {
|
|
35
|
+
const value = t('alerts.attention', 'pt-BR');
|
|
36
|
+
expect(value).toBe('Atenção');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('interpolates placeholders when present', () => {
|
|
40
|
+
expect(tFormat('alerts.soonFeature', 'pt-BR', { feature: 'Chat' })).toBe(
|
|
41
|
+
'Chat ainda não está disponível.',
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
});
|