Files
landing-app/landing/src/app/core/copy.ts
pigui 25d0fe500b Fix the hours field's label sitting on the outline
The label read "Horas por semana" struck through by the field's own
border: 5px above the wrapper instead of 18, straddling the 2px outline.

Angular Material measures an outlined field's floating label once on
init and writes the result to the element as an inline transform. This
field is the only one inside an @if, so it is measured before it has any
geometry and keeps the unfloated offset. @pi/ui already publishes the
right values for every other field and its rule matches this one too --
it simply loses to an inline style, so the fix restates the library's
own numbers with `!important`, which is what outranks one.

It also gains a placeholder, which every other field already had.

The e2e config now derives the dev server's port from BASE_URL instead
of pinning 4200. With `reuseExistingServer`, an unrelated project's dev
server on that port gets adopted silently and all five tests fail with
"element(s) not found" while the page under test was never served --
which is exactly what happened while verifying this.

Verified: all four labels now land at -18 with a 48px box, light and
dark; lint, 15 unit tests, build and 5 e2e green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 00:13:12 +02:00

395 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* The chrome's own words. Content arrives from the API already resolved to
* one language; headings, labels and button text are ours, so they live here
* and are picked with the same locale.
*/
import type { Locale } from './api';
export interface Copy {
nav: {
services: string;
projects: string;
team: string;
contribute: string;
contact: string;
darkMode: string;
lightMode: string;
};
hero: {
eyebrow: string;
title: string;
highlight: string;
lead: string;
primaryCta: string;
secondaryCta: string;
};
stats: Record<
| 'projects'
| 'active_projects'
| 'members'
| 'open_calls'
| 'services'
| 'clients'
| 'good_first_issues'
| 'stars',
string
>;
sections: {
services: { title: string; lead: string };
projects: { title: string; lead: string };
clients: { title: string; lead: string };
testimonials: { title: string; lead: string };
radar: { title: string; lead: string };
team: { title: string; lead: string };
calls: { title: string; lead: string };
issues: { title: string; lead: string };
talks: { title: string; lead: string };
contact: { title: string; lead: string };
};
projectStatus: Record<
'idea' | 'active' | 'paused' | 'completed' | 'archived',
string
>;
rings: Record<'adopt' | 'trial' | 'assess' | 'hold', string>;
labels: {
demo: string;
repository: string;
stars: string;
needsHelp: string;
founder: string;
alumni: string;
hoursPerWeek: string;
weeks: string;
seniority: string;
closes: string;
apply: string;
watchTalk: string;
slides: string;
openIssue: string;
comments: string;
empty: string;
seeMore: string;
};
form: {
kind: string;
kindContribute: string;
kindRequest: string;
name: string;
email: string;
organisation: string;
hours: string;
hoursHint: string;
message: string;
messageHint: string;
consent: string;
submit: string;
sending: string;
success: string;
duplicate: string;
error: string;
closed: string;
};
footer: { built: string; api: string; rights: string };
maintenance: { title: string; back: string; until: string };
}
const es: Copy = {
nav: {
services: 'Servicios',
projects: 'Proyectos',
team: 'Equipo',
contribute: 'Colabora',
contact: 'Contacto',
darkMode: 'Modo oscuro',
lightMode: 'Modo claro',
},
hero: {
eyebrow: 'Colectivo de desarrollo',
title: 'Construimos software',
highlight: 'en abierto',
lead: 'Proyectos reales, código público y gente que enseña lo que aprende. Si tienes una idea que empujar o unas horas que aportar, este es el sitio.',
primaryCta: 'Ver proyectos',
secondaryCta: 'Hablemos',
},
stats: {
projects: 'Proyectos',
active_projects: 'En marcha',
members: 'Personas',
open_calls: 'Vacantes abiertas',
services: 'Servicios',
clients: 'Clientes',
good_first_issues: 'Issues para empezar',
stars: 'Estrellas',
},
sections: {
services: { title: 'Qué hacemos', lead: 'Lo que alguien nos pediría.' },
projects: { title: 'Proyectos', lead: 'Lo que ya hemos construido.' },
clients: { title: 'Con quién trabajamos', lead: 'Organizaciones detrás del trabajo.' },
testimonials: { title: 'Lo que dicen', lead: 'Palabras de quien nos contrató.' },
radar: { title: 'Radar técnico', lead: 'Qué adoptamos, qué probamos y qué dejamos pasar.' },
team: { title: 'Quiénes somos', lead: 'Las personas detrás de todo esto.' },
calls: { title: 'Buscamos gente', lead: 'Vacantes abiertas ahora mismo.' },
issues: { title: 'Primeras contribuciones', lead: 'Issues pensadas para empezar hoy.' },
talks: { title: 'Charlas', lead: 'Lo que hemos contado fuera.' },
contact: { title: 'Escríbenos', lead: 'Cuéntanos qué necesitas o qué te apetece hacer.' },
},
projectStatus: {
idea: 'Idea',
active: 'Activo',
paused: 'En pausa',
completed: 'Terminado',
archived: 'Archivado',
},
rings: { adopt: 'Adoptar', trial: 'Probar', assess: 'Evaluar', hold: 'Esperar' },
labels: {
demo: 'Demo',
repository: 'Repositorio',
stars: 'estrellas',
needsHelp: 'Busca ayuda',
founder: 'Fundador/a',
alumni: 'Alumni',
hoursPerWeek: 'h/semana',
weeks: 'semanas',
seniority: 'Nivel',
closes: 'Cierra el',
apply: 'Me interesa',
watchTalk: 'Ver charla',
slides: 'Diapositivas',
openIssue: 'Abrir issue',
comments: 'comentarios',
empty: 'Todavía no hay nada publicado aquí.',
seeMore: 'Ver más',
},
form: {
kind: 'Escribes para…',
kindContribute: 'Aportar tiempo',
kindRequest: 'Pedir ayuda para un proyecto',
name: 'Nombre',
email: 'Correo',
organisation: 'Organización',
hours: 'Horas por semana',
hoursHint: 'Por ejemplo, 4',
message: 'Mensaje',
messageHint: 'Mínimo 20 caracteres.',
consent: 'Acepto que guardéis mis datos para responderme.',
submit: 'Enviar',
sending: 'Enviando…',
success: 'Mensaje recibido. Te contestamos en cuanto podamos.',
duplicate: 'Ya teníamos este mensaje. No hace falta reenviarlo.',
error: 'No hemos podido enviarlo. Inténtalo en un rato.',
closed: 'El formulario está cerrado temporalmente.',
},
footer: {
built: 'Hecho con Angular, Analog y Nx.',
api: 'Contenido servido por la API pública de Pireon.',
rights: 'Todos los derechos reservados.',
},
maintenance: {
title: 'Estamos de mantenimiento',
back: 'Volvemos en un rato.',
until: 'Volvemos el',
},
};
const en: Copy = {
nav: {
services: 'Services',
projects: 'Projects',
team: 'Team',
contribute: 'Contribute',
contact: 'Contact',
darkMode: 'Dark mode',
lightMode: 'Light mode',
},
hero: {
eyebrow: 'A development collective',
title: 'We build software',
highlight: 'in the open',
lead: 'Real projects, public code and people who teach what they learn. If you have an idea to push or a few hours to give, you are in the right place.',
primaryCta: 'See projects',
secondaryCta: "Let's talk",
},
stats: {
projects: 'Projects',
active_projects: 'In progress',
members: 'People',
open_calls: 'Open calls',
services: 'Services',
clients: 'Clients',
good_first_issues: 'Good first issues',
stars: 'Stars',
},
sections: {
services: { title: 'What we do', lead: 'What somebody would be asking for.' },
projects: { title: 'Projects', lead: 'What we have already built.' },
clients: { title: 'Who we work with', lead: 'The organisations behind the work.' },
testimonials: { title: 'What they said', lead: 'Words from the people who hired us.' },
radar: { title: 'Tech radar', lead: 'What we adopt, trial, assess and hold.' },
team: { title: 'Who we are', lead: 'The people behind all of this.' },
calls: { title: 'We are looking for people', lead: 'Calls open right now.' },
issues: { title: 'First contributions', lead: 'Issues meant to be picked up today.' },
talks: { title: 'Talks', lead: 'What we have told elsewhere.' },
contact: { title: 'Write to us', lead: 'Tell us what you need, or what you feel like building.' },
},
projectStatus: {
idea: 'Idea',
active: 'Active',
paused: 'Paused',
completed: 'Completed',
archived: 'Archived',
},
rings: { adopt: 'Adopt', trial: 'Trial', assess: 'Assess', hold: 'Hold' },
labels: {
demo: 'Demo',
repository: 'Repository',
stars: 'stars',
needsHelp: 'Needs help',
founder: 'Founder',
alumni: 'Alumni',
hoursPerWeek: 'h/week',
weeks: 'weeks',
seniority: 'Level',
closes: 'Closes on',
apply: "I'm interested",
watchTalk: 'Watch talk',
slides: 'Slides',
openIssue: 'Open issue',
comments: 'comments',
empty: 'Nothing published here yet.',
seeMore: 'See more',
},
form: {
kind: 'You are writing to…',
kindContribute: 'Give some time',
kindRequest: 'Ask for help with a project',
name: 'Name',
email: 'E-mail',
organisation: 'Organisation',
hours: 'Hours per week',
hoursHint: 'For example, 4',
message: 'Message',
messageHint: 'At least 20 characters.',
consent: 'I agree to have my details stored so you can reply.',
submit: 'Send',
sending: 'Sending…',
success: 'Message received. We will get back to you as soon as we can.',
duplicate: 'We already had this message. No need to send it again.',
error: 'We could not send it. Please try again in a while.',
closed: 'The form is temporarily closed.',
},
footer: {
built: 'Built with Angular, Analog and Nx.',
api: 'Content served by the Pireon public API.',
rights: 'All rights reserved.',
},
maintenance: {
title: 'We are under maintenance',
back: 'Back in a while.',
until: 'Back on',
},
};
const ca: Copy = {
nav: {
services: 'Serveis',
projects: 'Projectes',
team: 'Equip',
contribute: 'Col·labora',
contact: 'Contacte',
darkMode: 'Mode fosc',
lightMode: 'Mode clar',
},
hero: {
eyebrow: 'Col·lectiu de desenvolupament',
title: 'Construïm programari',
highlight: 'en obert',
lead: 'Projectes reals, codi públic i gent que ensenya el que aprèn. Si tens una idea per empènyer o unes hores per aportar, aquest és el lloc.',
primaryCta: 'Veure projectes',
secondaryCta: 'Parlem-ne',
},
stats: {
projects: 'Projectes',
active_projects: 'En marxa',
members: 'Persones',
open_calls: 'Vacants obertes',
services: 'Serveis',
clients: 'Clients',
good_first_issues: 'Issues per començar',
stars: 'Estrelles',
},
sections: {
services: { title: 'Què fem', lead: 'El que algú ens demanaria.' },
projects: { title: 'Projectes', lead: 'El que ja hem construït.' },
clients: { title: 'Amb qui treballem', lead: 'Organitzacions darrere de la feina.' },
testimonials: { title: 'Què en diuen', lead: 'Paraules de qui ens ha contractat.' },
radar: { title: 'Radar tècnic', lead: 'Què adoptem, què provem i què deixem passar.' },
team: { title: 'Qui som', lead: 'Les persones darrere de tot això.' },
calls: { title: 'Busquem gent', lead: 'Vacants obertes ara mateix.' },
issues: { title: 'Primeres contribucions', lead: 'Issues pensades per començar avui.' },
talks: { title: 'Xerrades', lead: 'El que hem explicat a fora.' },
contact: { title: 'Escriu-nos', lead: 'Explica què necessites o què et ve de gust fer.' },
},
projectStatus: {
idea: 'Idea',
active: 'Actiu',
paused: 'En pausa',
completed: 'Acabat',
archived: 'Arxivat',
},
rings: { adopt: 'Adoptar', trial: 'Provar', assess: 'Avaluar', hold: 'Esperar' },
labels: {
demo: 'Demo',
repository: 'Repositori',
stars: 'estrelles',
needsHelp: 'Busca ajuda',
founder: 'Fundador/a',
alumni: 'Alumni',
hoursPerWeek: 'h/setmana',
weeks: 'setmanes',
seniority: 'Nivell',
closes: 'Tanca el',
apply: "M'interessa",
watchTalk: 'Veure xerrada',
slides: 'Diapositives',
openIssue: 'Obrir issue',
comments: 'comentaris',
empty: 'Encara no hi ha res publicat aquí.',
seeMore: 'Veure més',
},
form: {
kind: 'Escrius per…',
kindContribute: 'Aportar temps',
kindRequest: 'Demanar ajuda per a un projecte',
name: 'Nom',
email: 'Correu',
organisation: 'Organització',
hours: 'Hores per setmana',
hoursHint: 'Per exemple, 4',
message: 'Missatge',
messageHint: 'Mínim 20 caràcters.',
consent: 'Accepto que deseu les meves dades per respondre’m.',
submit: 'Enviar',
sending: 'Enviant…',
success: 'Missatge rebut. Et contestem tan aviat com puguem.',
duplicate: 'Ja teníem aquest missatge. No cal reenviar-lo.',
error: 'No hem pogut enviar-lo. Torna-ho a provar d’aquí una estona.',
closed: 'El formulari està tancat temporalment.',
},
footer: {
built: 'Fet amb Angular, Analog i Nx.',
api: 'Contingut servit per l’API pública de Pireon.',
rights: 'Tots els drets reservats.',
},
maintenance: {
title: 'Estem de manteniment',
back: 'Tornem d’aquí una estona.',
until: 'Tornem el',
},
};
const dictionaries: Record<Locale, Copy> = { es, en, ca };
export function copyFor(locale: Locale): Copy {
return dictionaries[locale] ?? dictionaries.es;
}