Skip to content

Portuguese (Brazil) Translation & Language Persistence - #90

Open
filiperochalopes wants to merge 3 commits into
OpenConceptLab:mainfrom
filiperochalopes:feature/translation-pt-br-i18n
Open

filiperochalopes wants to merge 3 commits into
OpenConceptLab:mainfrom
filiperochalopes:feature/translation-pt-br-i18n

Conversation

@filiperochalopes

Copy link
Copy Markdown
Contributor

TL;DR

  • Adds Portuguese (Brazil) translation (815 lines, full app coverage)
  • Adds language preference persistence via localStorage (new feature — survives browser restarts)
  • Updates dependencies + version bump to 1.0.11-beta
  • Implements OpenConceptLab/ocl_issues#2866

Summary

This PR adds Portuguese (Brazil) localization to OCL Mapper and introduces language preference persistence via localStorage. The work addresses OpenConceptLab/ocl_issues#2866.

Changes

1. Portuguese (Brazil) Translation (pt-BR)

  • New file: src/i18n/locales/pt-BR/translations.json (815 lines)
  • Complete translation coverage for all application strings including:
    • Common UI elements (buttons, labels, actions)
    • Error messages and validation
    • Dashboard, authentication, user/org/profile pages
    • Repository management and search
    • URL Registry and locale management
    • Mapper module (full translation of mapping workflow, algorithms, AI Assistant, preview/quota limits, bulk actions, candidate review, export/import)

2. Language Preference Persistence (New Feature)

Files modified:

  • src/i18n/config.js - Added localStorage persistence layer
  • src/common/constants.js - Added pt-BR to supported languages list

Implementation details:

  • Added LANGUAGE_STORAGE_KEY = 'ocl.language' constant
  • Added SUPPORTED_LANGUAGES = ['en', 'es', 'zh', 'pt-BR'] allow-list
  • getInitialLanguage(): Reads persisted preference from localStorage on app load (falls back to 'en')
  • persistLanguage(): Saves selected language to localStorage on change
  • i18n.on('languageChanged', persistLanguage): Automatically persists when user switches languages
  • Initial lng now uses getInitialLanguage() instead of hardcoded 'en'

User experience: Language selection now persists across browser sessions — users no longer need to re-select their preferred language on each visit.

3. Dependency Updates

  • package-lock.json updated with latest compatible versions of dev/production dependencies (babel, eslint, jsonjoy, mui/x-data-grid, noble/hashes, peculiar/asn1, etc.)
  • Version bumped to 1.0.11-beta

Testing Checklist

  • Language selector shows "Português (Brasil)" option
  • Selecting Portuguese (Brazil) translates the entire UI
  • Language preference persists after refresh/close browser
  • Switching languages updates localStorage
  • Fallback to English works for any missing keys
  • All existing languages (en, es, zh) still work correctly

Related Issues

  • Implements: OpenConceptLab/ocl_issues#2866 — Portuguese (Brazil) translation for OCL Mapper
  • The language persistence feature was added as a quality-of-life improvement alongside the translation work

- Add pt-BR translation catalog with all English keys translated
- Register pt-BR in i18n config
- Add pt-BR to LANGUAGES in constants.js
* Add pt-BR translation files and update configuration to support Portuguese locales
"user_has_not_created_public_repos_suffix": "has not created any public repositories",
"recent_activity": "recent activity"
"recent_activity": "recent activity",
"own_recent_activity": "Your recent activity",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two translation keys are not used anywhere in this project

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obrigado, Filipe! This is the same Claude + Codex review as oclweb3#60, with the same three kinds of inline one-click suggestion: Bug (please apply), Assembled text (quotes what the user actually sees once the code joins your strings), and Second opinion (your call).

Before merge:

  • package-lock.json: this PR also bumps 68 transitive dependency versions, which is unrelated to the translation. Please restore main's copy (git checkout origin/main -- package-lock.json) so dependency updates go in their own PR.
  • Unused keys: +1 to Sunny's comment. user.own_recent_activity and user.recent_activity_of aren't used in the Mapper; please drop them from all four catalogs.
  • Missing strings: the two missing on this branch have one-click suggestions. Nine more landed on main after you branched (the *_throttled and waiting_for_capacity* messages, rerank_step, search_failed, announcement.pricing_link_label). They show in English until translated and don't block the merge.

Several shared keys (error pages, URL registry, names) get the same suggestions as on TBv3, to keep the two catalogs identical. One more glossary question for OpenConceptLab/ocl_issues#2878: the Mapper mixes Bridge / bridge / Ponte. Which should we standardize on? Code-side fixes are tracked in OpenConceptLab/ocl_issues#2877.

"match_api_token": "Token da API de Correspondência",
"match_api_token_placeholder": "ex.: XXXXXXXXXXX",
"batch_size": "Tamanho do Lote",
"concurrent_requests": "Requisições Concurrentes",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: spelling. "Concurrentes" is the Spanish form.

Suggested change
"concurrent_requests": "Requisições Concurrentes",
"concurrent_requests": "Requisições Simultâneas",

"fetching": "Buscando...",
"fetch_more": "Buscar Mais",
"unrecognized_algorithm": "Algoritmo não reconhecido",
"source_code": "Código Fonte",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: this labels the input row's code (MappingDecisionResult.jsx), but "Código Fonte" reads as software source code.

Suggested change
"source_code": "Código Fonte",
"source_code": "Código de Origem",

"rows": "linhas",
"finished_running": "Finalizado",
"finished_reranking": "Re-ranqueamento finalizado",
"proposed_successfully": "Proposto com sucesso.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: duplicate key; proposed_successfully is already on line 563. This suggestion deletes this copy.

Suggested change
"proposed_successfully": "Proposto com sucesso.",

"preview_limit_body_rows_no_count": "O limite de linhas de sua prévia gratuita para este projeto foi atingido. Nada está bloqueado: continue revisando, editando e exportando este projeto quanto quiser.",
"preview_limit_body_match_operations": "Sua prévia gratuita cobre {{limit}} operações de correspondência - você usou todas as {{limit}}. Nada está bloqueado: continue revisando, editando e exportando este projeto quanto quiser.",
"preview_limit_body_match_operations_no_count": "O limite de operações de correspondência de sua prévia gratuita foi atingido. Nada está bloqueado: continue revisando, editando e exportando este projeto quanto quiser.",
"preview_limit_body_projects": "Você já tem um projeto do Mapper ativo. A prévia gratuita cobre um projeto por vez - delete o que você tem, ou peça mais acesso abaixo.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: "delete" is the English verb; the rest of the catalog uses "excluir".

Suggested change
"preview_limit_body_projects": "Você já tem um projeto do Mapper ativo. A prévia gratuita cobre um projeto por vez - delete o que você tem, ou peça mais acesso abaixo.",
"preview_limit_body_projects": "Você já tem um projeto do Mapper ativo. A prévia gratuita cobre um projeto por vez - exclua o que você tem, ou peça mais acesso abaixo.",

"semantic_search_not_configured": "{{owner}}:{{repo}}:{{version}} não está configurado para executar Busca Semântica",
"no_valid_columns_for_matching": "Nenhuma das colunas é válida para correspondência, por favor edite e atribua colunas válidas.",
"proposed_successfully": "Proposto com sucesso.",
"algorithm_es_label": "Correspondência Genérica de Busca Elástica",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Elasticsearch is a product name, so it shouldn't be translated.

Suggested change
"algorithm_es_label": "Correspondência Genérica de Busca Elástica",
"algorithm_es_label": "Correspondência Genérica com Elasticsearch",

"preview_limit_body_custom_algorithms_no_count": "Algoritmos hospedados externamente não fazem parte da prévia ainda. Use os algoritmos integrados do OCL, ou peça mais acesso abaixo.",
"preview_limit_body_ai_assistant": "Sua prévia gratuita cobre {{limit}} chamadas do Assistente de IA - você usou {{used}} de {{limit}}. A correspondência pode continuar sem análise de IA para as linhas restantes.",
"preview_limit_body_ai_assistant_no_count": "O Assistente de IA não está disponível para sua conta. Peça mais acesso abaixo e nós resolveremos.",
"preview_limit_body_rows_not_entitled_no_count": "Este projeto não tem uma permissão de linhas configurada ainda. Peça mais acesso abaixo e nós resolveremos.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion: "cota".

Suggested change
"preview_limit_body_rows_not_entitled_no_count": "Este projeto não tem uma permissão de linhas configurada ainda. Peça mais acesso abaixo e nós resolveremos.",
"preview_limit_body_rows_not_entitled_no_count": "Este projeto ainda não tem uma cota de linhas configurada. Peça mais acesso abaixo e nós resolveremos.",

"none": "Nenhum",
"decision_changed_to_none": "Decisão Alterada para Nenhum",
"loading_project": "Carregando projeto...",
"stopping_gracefully": "Parando graciosamente...",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second opinion, your call: "graciosamente" doesn't carry the technical sense of "graceful shutdown".

Suggested change
"stopping_gracefully": "Parando graciosamente...",
"stopping_gracefully": "Encerrando com segurança...",

"preview_limit_body_projects_not_entitled_no_count": "Sua conta não tem acesso a projetos de mapeamento configurado ainda. Peça mais acesso abaixo e nós resolveremos.",
"preview_limit_body_generic": "Você atingiu um limite de prévia ({{used}} de {{limit}}).",
"preview_limit_body_generic_no_count": "Você atingiu um limite de prévia para o Mapper.",
"preview_limit_hardship_note": "Trabalhando com um orçamento de concessão que não estica, ou para um ministério da saúde? Use \"Solicitar mais acesso\" abaixo - se o limite não funciona para sua situação, nós encontraremos algo que funcione.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second opinion, your call: "um orçamento de concessão que não estica" reads as a literal translation. This note is aimed at Brazilian public-sector teams, so it's worth getting right. The suggestion keeps the grant-funding sense; reword however you prefer.

Suggested change
"preview_limit_hardship_note": "Trabalhando com um orçamento de concessão que não estica, ou para um ministério da saúde? Use \"Solicitar mais acesso\" abaixo - se o limite não funciona para sua situação, nós encontraremos algo que funcione.",
"preview_limit_hardship_note": "Trabalha com recursos de financiamento (grant) que não cobrem esse custo, ou para um ministério da saúde? Use \"Solicitar mais acesso\" abaixo - se o limite não funcionar para a sua situação, encontraremos algo que funcione.",

"run_ai_analysis_note": "Nota: Habilitar este recurso executará Análise de IA nos resultados de cada linha. Isso tem implicações diretas de custo.",
"decision": "Decisão",
"decision_map": "Mapear",
"decision_exclude": "Excluir",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second opinion, your call: "Excluir" is also the app's word for Delete, so the bulk log at line 616 ("Excluído em massa…") reads as "deleted in bulk". Here Exclude means "don't map this row". Would "Desconsiderar" / "Desconsiderado" (lines 598, 616, 633) be clearer?

"ai_effective_locales": "IA receberá nomes em: {{locales}}",
"ai_all_locales": "IA receberá nomes em: todas as localidades (sem filtro)",
"default_filter": "Filtro Padrão",
"matching_algorithm": "Inteligência de mapeamento",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: was "Inteligência de mapeamento" deliberate? The English is "Matching Algorithm", and elsewhere the catalog says "algoritmos de correspondência". ("Um ou mais" on the next line is a good improvement: the Mapper does allow several algorithms.)

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review record (codex-cli 0.160.0; oclweb3 07c8260, oclmap b05133c). One combined review covers both PRs, so the same record is posted on oclweb3#60 and oclmap#90. The actionable items are the inline suggestions in the review above. This record also lists items that were later downgraded (pass 2) or moved to OpenConceptLab/ocl_issues#2877, so nothing here needs action beyond that review.

Pass 1: independent review (coverage, language, i18n & code)

Summary

  • oclweb3#60: request changes. Fix the stored-zh menu crash, missing catalog key, and event sentence construction before merging.
  • oclmap#90: request changes. Complete catalog coverage, correct a few clear language errors, align feature names, and separate dependency churn.
  • OCL Web covers 1,040/1,041 branch English keys and 1,014/1,026 current-main keys. Mapper covers 750/752 branch keys and 748/759 current-main keys.
  • Both catalogs have zero interpolation-variable mismatches, tag mismatches, or empty strings. Neither English catalog defines plural-suffix keys.
  • Of 332 shared keys with identical branch English, 331 have identical Portuguese translations; the remaining difference is OCL Web’s misspelled key.
  • The translations are broadly coherent Brazilian Portuguese. “Source” is consistently retained in OCL Web; its retention is a maintainer glossary decision, not a translation error.
  • Language persistence generally works, but document language and formatting do not consistently follow it. Mapper’s lockfile additionally changes 68 dependency versions.
  • Review was read-only. Dependencies are absent, so lint/build execution was not possible; neither project defines npm run lint—the script is npm run eslint.

oclweb3#60

Coverage

Counts use unique, flattened string keys.

Comparison English keys Covered Missing Portuguese-only
PR branch 1,041 1,040 1 1
Current origin/main 1,026 1,014 12 27

Branch missing key — high confidence: src/i18n/locales/pt-BR/translations.json:574 names the key repo.repo_summary_is_calculando. Rename it repo.repo_summary_is_calculating, preserving its translated value. src/components/repos/RepoSummary.jsx:66 requests the English key, so the current translation is unreachable.

Missing against current main, grouped by section:

Section Missing keys
announcement pricing_label
common error
concept form.id_auto_assign_back, form.id_live_at, form.id_optional_sequential, form.id_optional_uuid, success_retired, success_unretired
org success_delete
reference unresolved
repo auto_id_manual_short, repo_summary_is_calculating

These missing entries have no Portuguese PR-head line. Their insertion sections begin at pt-BR/translations.json:2, :260, :419, :506, :867, and :1112. High confidence: translate the 11 main additions and repair the existing typo; fallback avoids missing-key failures but does not satisfy full translation coverage.

Portuguese-only keys:

  • Against branch English: only repo.repo_summary_is_calculando.
  • Against main: that typo; user.own_recent_activity, user.recent_activity_of; and these PR additions:
    • common.event_types.{created,updated,released,deleted,followed,unfollowed,joined,source}
    • common.object_types.{Source,Collection,Source Version,Collection Version,Organization,Concept,Mapping,Repository}
    • common.event_object_types with the same eight object names.

The 26 legitimate additions will cease being extras when the PR’s English changes merge.

Identical-to-English values: 46. None is a clear accidental untranslated sentence.

Classification Keys
Deliberate Source terminology common.event_types.source, common.object_types.Source, reference.source, reference.sources, reference.group_sources, repo.source, sort.source
Identifiers, formats, literal displays common.id, common.uid, common.json, repo.ids, repo.auto_id_uuid, repo.version.form.id.label, repo.external_export_file_types, repo.reindex_dialog_title, repo.processing_stage_caption, repo.clear_processing_title, sort.a_z, sort.z_a, sort.num_asc, sort.num_desc
Technical FHIR parameter names repo.version.expansion_form.parameters_fields.{activeOnly,includeDesignations,includeDefinition,excludeNested,excludeNotForUI,excludePostCoordinated,exclude-system,system-version,check-system-version,force-system-version}.label
Portuguese cognates/accepted technical terms common.status, concept.namespace, reference.intensional, reference.extensional, repo.experimental, repo.meta, repo.code_system, repo.valueset, repo.concept_map, repo.changelog, repo.stage_changelog, repo.mapper, user.avatar, url_registry.global, import.args

“Changelog”, “Namespace”, and “Args” could receive more accessible labels, but they are glossary choices, not high-confidence omissions.

Structural checks: zero variable mismatches, zero Trans/HTML tag mismatches, zero empty values. Neither catalog has _one, _other, _zero, _two, _few, or _many keys. Existing explicit singular/plural strings remain present.

Use of added English keys:

  • common.event_types and common.event_object_types: used dynamically in Events.jsx:29–31 and DashboardEvents.jsx:29–43.
  • user.own_recent_activity, user.recent_activity_of: used in Events.jsx:111.
  • common.object_types is unused; en/translations.json:61 adds an unused parallel object-name catalog. High confidence: remove it or use it deliberately.
  • common.event_types.source has no explicit consumer; the dynamic lookup would use it only if the API supplied event_type: "Source".

Nearby English UI remaining — high confidence:

PR-head location Current output Suggested output Reason
Events.jsx:24–26 Server description + site title Localized whole-event message No-object events bypass translation.
Events.jsx:59; DashboardEvents.jsx:67 Moment fromNow(), e.g. “a day ago” Portuguese relative time OCL Web never synchronizes Moment with UI language.
src/components/users/UserProfile.jsx:104 Joined {date} Entrou em {date} through an i18n key Adjacent profile UI remains hardcoded English.

Language

Locations below refer to src/i18n/locales/pt-BR/translations.json.

Line/key Current string Suggested string Confidence / reason
910, user.last_name Último Nome Sobrenome High: “last name” means family name in this form.
878, org.org_have_not_created_public_repos_suffix não criaram nenhum repositório público não criou nenhum repositório público High: OrgOverview.jsx:17 prefixes one organization’s name.
322, concept.ai_assistant_not_configured Assistente de IA não está configurado… O Assistente de IA do OCL não está configurado… High: restore the required OCL feature brand; English already omits it.
990, url_registry.resolve_success_with_entry …usando uma entrada no <2/> Registro …usando uma entrada no Registro <2/> High: with the Global component this renders “Global Registro”.
992–993, registry failure messages …no <0/> Registro… …no Registro <0/>… High: same English noun-order carryover.
1146, 1155, quota Auto Match messages Correspondência Automática Auto Match if the feature name is invariant Medium: internally consistent translation, but the stated product name is “Auto Match”; confirm the branding policy.

Register is coherent: infinitives for actions (“Salvar”, “Excluir”, “Buscar”), você forms for instructions. Mixing these functions is appropriate. Capitalization varies between sentence case and title case, generally following English; standardizing it is optional.

There is no “Fonte” anywhere in this catalog, including event_types: common.event_types.source is "Source". Repository is sometimes abbreviated “Repo”; that is an internal terminology inconsistency rather than a mistranslation.

i18n & code

Stored Chinese crashes the user menu — high confidence.

  • src/i18n/config.js:9,16–17 accepts stored "zh".
  • src/common/constants.js:3–6 excludes Chinese from LANGUAGES.
  • src/components/users/UserMenu.jsx:31,86 obtains undefined, then evaluates selectedLanguage.name.
  • Suggestion: either include Chinese in the selector or restrict accepted persisted languages to selector entries; also guard the lookup. The application can initialize in Chinese, but opening the menu throws.

Event fragments need complete sentence keys — high confidence. Current construction is in Events.jsx:29–31 and DashboardEvents.jsx:29–43.

Every referenced-object combination is exactly the following verb + noun phrase. The two tables describe the complete catalog-defined Cartesian product, including combinations the API may never emit.

event_type Portuguese fragment
Created Criou
Updated Atualizou
Released Publicou
Deleted Excluiu
Followed Passou a seguir
Unfollowed Deixou de seguir
Joined Entrou
Source Source
referenced_object.type Portuguese fragment
Source um Source
Collection uma Coleção
Source Version uma Versão do Source
Collection Version uma Versão da Coleção
Organization uma Organização
Concept um Conceito
Mapping um Mapeamento
Repository um Repositório

Thus “Criou uma Coleção” and “Passou a seguir um Source” work. “Entrou uma Organização” does not: use “Entrou em uma Organização”. Every Joined/object combination lacks the required preposition. The Source event row mechanically produces “Source um Source”, etc.; verify whether that event type is real before maintaining it.

For no referenced object:

  • Dashboard: verb + getSiteTitle(), defaulting to OCL. Joined renders “Entrou OCL”; use “Entrou no OCL”, or “Ingressou em {{site}}” for a configurable site name.
  • Profile timeline: retains API description, appending the site title only for Joined. An API description "Joined" renders “Joined OCL”.
  • Unknown event/object types retain raw English API values.

Suggestion: event-specific complete messages, with object/site interpolation and explicit gender/preposition handling. The new recent-activity heading already correctly adopts this approach.

Spanish/Chinese event behavior: neither receives the new event catalogs, so both fall back to English.

  • Profile referenced-object events gain English articles: formerly “Created Collection”, now “Created a Collection”.
  • Dashboard formerly combined lowercase API labels with translated common.a/an; now it renders English “Created a Collection” / “Created an Organization”.
  • No-object dashboard verbs change from raw lowercase to English catalog capitalization.
  • Recent-activity headings improve: proper Spanish and Chinese whole phrases replace English possessive concatenation.

Persistence and formatting:

Location Finding / suggestion Confidence
config.js:11–28,53 Valid stored selections survive reload; blocked storage and missing window are caught. No competing language detector or lng persistence was found. Storage remains origin-specific; equal keys do not share language across different hosts. High; fine
config.js:52 i18n.languages = SUPPORTED_LANGUAGES overwrites the selected language’s resolution hierarchy. Remove it; use supportedLngs for supported locales. High
public/index.html:2 <html lang="en"> remains English. Update it at initialization and on language change. High
common/constants.js:10–12; common/utils.js:26–28 Dates retain M/D/YYYY and 12-hour formatting. Number calls generally use toLocaleString() without the selected locale. High

The i18n.languages assignment does not itself make Portuguese translations fall back to English. i18next’s translator computes its lookup hierarchy separately; changeLanguage also resets languages. The assignment corrupts public hierarchy state and is unnecessary, but claiming it reverses ordinary translation lookup would be incorrect. i18next translator, language-change implementation.

Other fragment problems — high confidence: DeleteEntityDialog.jsx:33–41 combines resource labels with "este {{resourceType}}" and "o {{resourceType}} inteiro" from the new catalog. Collection expansion deletion can produce “este Expansão de Coleção”; some callers pass raw English resource types. Use resource-specific messages or gender-neutral wording. The Portuguese confirmation title also adds ?, while the component appends the ID and another ?.

Lint/build: no obvious import or JSON syntax failure. Medium-confidence lint concern: constants.js:6 introduces "Português (Brasil)" into a file governed by an English spell-check rule; neither word is allowlisted. Verify and narrowly exempt language display names. No lint/build pass is claimed.

oclmap#90

Coverage

Comparison English keys Covered Missing Portuguese-only
PR branch 752 750 2 0
Current origin/main 759 748 11 2

Missing against branch — high confidence:

  • map_project.preview_limit_title_scispacy
  • map_project.preview_limit_body_scispacy_no_count

English entries are at src/i18n/locales/en/translations.json:532,550; Portuguese insertion anchors are pt-BR/translations.json:530,546.

Suggested translations:

  • ScispaCy LOINC ainda não está disponível
  • A correspondência ScispaCy LOINC não está disponível para sua conta. Solicite mais acesso abaixo.

Missing against main, grouped by section:

Section Missing keys
announcement pricing_link_label
map_project algorithm_throttled, auto_match_rows_throttled, preview_limit_body_scispacy_no_count, preview_limit_title_scispacy, rerank_step, row_throttled, search_failed, search_throttled, waiting_for_capacity, waiting_for_capacity_short

The nine main-only additions have no Portuguese PR-head line. High confidence: add translations when updating the branch; English fallback currently covers the gaps.

Extras against main: only user.own_recent_activity, user.recent_activity_of, which this PR also adds to English.

Identical-to-English values: 11, all plausible deliberate keeps:

common.id, common.ok, common.json, concept.namespace, repo.source, repo.experimental, user.avatar, url_registry.global, map_project.source, map_project.lookup_configuration_token, map_project.bridge_canonical_short.

No high-confidence accidental untranslated English sentence.

Structural checks: zero variable mismatches, zero tag mismatches, zero empty strings, and no plural-suffix keys on either side.

Duplicate JSON member — high confidence: pt-BR/translations.json:563,719 repeats map_project.proposed_successfully, identically "Proposto com sucesso.". Remove one. Parsing succeeds and the value is unchanged, but duplicate members obscure maintenance. Counts above count it once.

Unused new keys — high confidence: pt-BR/translations.json:342–343 and corresponding English entries add user.own_recent_activity / user.recent_activity_of; neither is used anywhere in Mapper’s code. The earlier reviewer’s observation is correct. Remove them unless a consumer is added.

Nearby English UI sample — high confidence: src/components/map-projects/ProjectLogs.jsx:170 renders {log.user} at {date}. Suggested: a complete localized message, e.g. {{user}} em {{date}}. The PR touches catalogs/configuration rather than these components; this is existing visible English, not newly introduced English.

Language

Locations below refer to src/i18n/locales/pt-BR/translations.json.

Line/key Current string Suggested string Confidence / reason
327, user.last_name Último Nome Sobrenome High: family-name field.
303, organization suffix não criaram nenhum repositório público não criou nenhum repositório público High: singular organization subject; shared with OCL Web.
440, concurrent_requests Requisições Concurrentes Requisições concorrentes or Requisições simultâneas High: “Concurrentes” is misspelled Portuguese.
629, source_code Código Fonte Código de origem High: MappingDecisionResult.jsx:126 labels the terminology’s source-side code, not software source code.
555, preview_limit_notify_me Notifique-me quando planos lançarem Notifique-me quando os planos forem lançados High: plans are launched; they do not launch something.
748, algo_canonical_url_description URL canônica do code system que este algoritmo corresponde… URL canônica do sistema de códigos no qual este algoritmo busca correspondências… High: incorrect verb construction and unclear matching target.
386, 388–389, registry messages …no <n/> Registro… …no Registro <n/>… High: same noun-order problem as OCL Web.
482, Auto Match and related messages Correspondência Automática Auto Match if invariant Medium: coherent localization; needs an explicit branding decision.

OCL AI Assistant branding — high confidence: generic “Assistente de IA” appears at lines 427, 501, 515, 522, 524, 531, 547, 548, 683, 754. Add “do OCL” when naming the feature. Lines 672–673 already correctly retain OCL. These omissions largely mirror omissions in English; fix the source wording too.

Prohibited “AI recommendations” wording — high confidence: lines 499, 520–521, 692, 760–761 use “Recomendado por IA”, “Recomendações de IA”, or “recomendações de mapeamento com IA”. Suggested wording should name the feature or its output, for example:

  • Sugerido pelo Assistente de IA do OCL
  • Análise do Assistente de IA do OCL para…
  • A análise do Assistente de IA do OCL falhou — clique para tentar novamente

Again, these faithfully reproduce problematic English branding; they are not independent mistranslations.

Register broadly follows the same appropriate infinitive/instruction pattern as OCL Web. Capitalization is uneven but not a blocker. “Bridge”, “ponte”, and “bridge” vary within Mapper; agree on one human-facing term while retaining API identifiers.

i18n & code

  • Persistence — high confidence, generally fine: config.js:11–28,54 validates stored values and catches storage failures. All four accepted languages exist in Mapper’s selector. No competing detector or language-storage implementation was found.
  • Hierarchy assignment — high confidence: config.js:53 has the same unnecessary i18n.languages overwrite discussed above; remove it. Ordinary translation fallback remains English.
  • Moment restoration gap — high confidence: UserMenu.jsx:34–35 calls both changeLanguage(locale) and moment.locale(locale) on manual selection. config.js:33 restores i18next language on reload without restoring Moment. Thus persisted Portuguese UI can have English relative dates until language is selected again. Synchronize both at startup and on every language change.
  • Document/formatting — high confidence: public/index.html:2 remains lang="en"; common/constants.js:12–14 fixes US date/time patterns; number formatting is not tied to UI selection.
  • Interface versus content language — checked: the PR does not change input-language, name-locale, target terminology, or assistant-output locale selection. Keep those independent.

Unrelated dependency changes — high confidence: package-lock.json:3,9 updates root metadata from 1.0.0-beta to 1.0.11-beta, matching the existing package.json. That metadata correction is reasonable. However, the same diff changes 68 shared package versions, adds no package paths, and removes node_modules/body-parser/node_modules/qs. Examples include Babel parser, webpack, ws, undici-types, and the jsonjoy packages.

Suggestion: retain only the root metadata correction if needed; move dependency refresh to a separate dependency PR with install/build/test validation. The churn is not demonstrably unsafe, but its safety is unverified and unrelated to pt-BR.

No obvious new JavaScript/JSON syntax problem was found. Dependencies are absent; no executable lint/build verification was performed.

Cross-app consistency

There are 332 shared keys whose branch English values are identical. 331 Portuguese values match exactly. There are no differing Portuguese values where both apps contain the correctly named shared key.

Shared key oclweb3 oclmap Recommendation
repo.repo_summary_is_calculating Missing; translation exists under repo_summary_is_calculando at line 574 Resumo do repositório está sendo calculado, por favor verifique novamente após algum tempo. at line 289 Repair OCL Web’s key; the actual wording already matches.

Canonical-term inventory:

Term oclweb3 oclmap Recommendation
Source Source / Sources Source / Sources Agree explicitly on retention or localization.
Source Version Versão do Source; versão de Source Versão do Source de Origem/Destino is not independently cataloged Use agreed Source term consistently; do not invent absent Mapper labels.
Collection Coleção Coleção Keep.
Collection Version Versão da Coleção No standalone equivalent Keep when introduced.
Organization Organização Organização Keep; repair singular verb agreement.
Repository Repositório / Repo Repositório / Repo Prefer Repositório in visible prose.
Concept Conceito Conceito / Conceitos Keep.
Mapping Mapeamento Mapeamento / Mapeamentos Keep.
Reference Referência “referências” in collection-save help Keep; means collection membership expression, not citation.
Expansion Expansão No canonical expansion label Keep.
Release / Released Publicar / Publicado / versões publicadas Status de Publicação / versões publicadas Keep; agree on gender for standalone state labels.
HEAD HEAD No explicit HEAD label Preserve identifier; explain as working version where needed.
Retired Aposentado / aposentados Aposentado / aposentados Acceptable choice; document inactive-status meaning.
Map Type Tipo de mapeamento Tipo de mapeamento Keep.
Concept Class Classe do Conceito / Classes de Conceito Same Keep; normalize capitalization if desired.
Datatype Tipo de Dado / Tipo de dado Same Keep; optionally standardize “tipo de dados”.
Locale Localidade Localidade; idioma in code help Distinguish locale/content language from interface language.
Mapping / Mapper project Projeto de Mapeamento Projeto de Mapeamento / projeto do Mapper Distinguish resource name from branded app attribution.
Auto Match Correspondência Automática in quota messages Correspondência Automática throughout Decide whether translated names are allowed; otherwise retain Auto Match.
Candidate No candidate feature terminology Candidato Keep.
Target repository No corresponding feature label Repositório de Destino Keep.
OCL AI Assistant Assistente de IA do OCL; one generic omission Assistente de IA do OCL plus multiple generic omissions Always retain OCL when naming the feature.

Canonical terms: options and how to document them

Keeping “Source” is defensible, especially for technical users moving between the UI, API, and /sources/ URLs. “Fonte” is ambiguous: it can mean provenance, information source, or a typeface. “do Source” is not inherently a grammatical error—a borrowed resource name can take masculine gender by association with “repositório”—but it may sound less natural to newcomers and needs an explicit glossary rule.

Maintainer options:

  1. Source / Coleção: retain the PR’s choice. Define “Source” as an OCL terminology repository and document masculine gender and “Sources” plural.
  2. Fonte / Coleção: more fully localized, with feminine agreement: “uma Fonte”, “Versão da Fonte”. Explain that this is an OCL resource, not generic provenance.
  3. Repositório de terminologia / Coleção: clearer on first use, with “Source” in help text for API correspondence. Avoid equating every Source directly with FHIR CodeSystem.

Brazilian FHIR material supports retaining CodeSystem/ValueSet identifiers while explaining them in Portuguese, but it does not establish “Source” as standard Brazilian terminology. RNDS publishes both TabelaSUS CodeSystem resources and ValueSet resources with Portuguese displays. Brazilian HL7 documentation similarly mixes Portuguese descriptions with canonical resource identifiers. HL7 Brasil example. This favors preserving interoperability identifiers; it does not require untranslated OCL resource labels.

SNOMED CT Brasil, LOINC, and TabelaSUS should remain named terminologies, not be relabeled “Fontes” as proper names. Also, a Coleção can contain concepts and mappings; avoid defining it simply as a ValueSet.

Concrete documentation proposal: record an approved glossary in the OCL design-system documentation, e.g. docs/i18n/pt-BR-glossary.md, and link it from both repositories’ localization guidance. Each entry should include:

  • English term, approved pt-BR term, gender and plural.
  • Resource meaning versus ordinary prose meaning.
  • Example complete sentences, especially events and deletion confirmations.
  • Protected feature names: OCL AI Assistant and Auto Match.
  • Separate definitions for interface language, terminology locale, and assistant output language.

Use Filipe’s review for final language approval and maintainer approval for branding/resource names. Add a read-only catalog check for missing keys, duplicate members, placeholders/tags, and shared-key divergence. No documentation was edited.

Things you checked and found fine

  • pt-BR is imported, registered, and added as Português (Brasil) in both selectors.
  • English remains configured as fallback.
  • Every existing matching key preserves interpolation variables and Trans/HTML tags.
  • No empty Portuguese values or missing plural-suffix counterparts.
  • New OCL Web recent-activity headings correctly handle Portuguese, Spanish, and Chinese word order.
  • No European-Portuguese vocabulary issue rose to high-confidence severity.
  • No unexpected Source/Fonte alternation exists in OCL Web. Mapper’s “Código Fonte” is a separate source-code context error.
  • Both working trees remained clean; no files were modified.
Pass 2: reconciliation with Claude's review

Pass 2 checked the second reviewer's findings (A) and the points where it disagreed with pass 1 (B):
A1 CI spellcheck failure · A2 name escaping in the recent-activity heading · A3 dashboard verb capitalization · A4 real event types, missing User, "Entrou em" · A5 processing-banner agreement · A6 Error 403 sentence · A7 allowance → "cota" · A8 "delete" → "exclua" · A9 Mapper "Excluir" vs Delete · A10 "graciosamente" · A11 Elasticsearch name · A12 "buscar por" · A13 hardship note · A14 "Importações" · A15 "mais específico" · A16 "salvo {{time}}" · A17 minor wording.
B1 stored-zh severity · B2 i18n.languages overwrite · B3 "Aposentado" · B4 overall verdicts · B5 Source / Coleção rule.

A. Claude-only findings

  • A1 — NUANCE: The spellcheck configuration confirms the missing exemptions and proposed fix, but I could not retrieve CI run 37136671460 to verify its failure independently.
  • A2 — AGREE: The new heading interpolates names with escaping enabled by i18next’s defaults, producing literal entities in React; the existing per-call exemption fixes the regression.
  • A3 — AGREE: The dashboard replaced lowercase event prose with capitalized catalog fragments, introducing mid-sentence capitalization in both languages.
  • A4 — NUANCE: The backend confirms the six event constants and user references, while the catalog lacks User; “Entrou em” fixes organization events, but site joins need separate wording, and I did not re-query production counts.
  • A5 — AGREE: RepoHome supplies plural resource labels, so “A lista de {{resource}} exibida aqui pode estar incompleta” fixes agreement consistently.
  • A6 — AGREE: The actual link label produces “Ir voltar,” and the proposed sentence composes correctly with both links.
  • A7 — AGREE: These strings describe quantitative allowances, making “cota” more precise than “permissão.”
  • A8 — AGREE: “delete” is untranslated English here, and “exclua” matches the Portuguese instruction.
  • A9 — NUANCE: “Excluir” can legitimately mean exclusion, but its collision with deletion warrants clarification; “Desconsiderar” is a defensible Mapper-specific choice for Filipe to approve.
  • A10 — AGREE: “Parando graciosamente” misrepresents graceful shutdown, whereas “Encerrando com segurança” conveys the intended operation.
  • A11 — AGREE: The adjacent description explicitly identifies Elasticsearch, so preserving that product name corrects “Busca Elástica.”
  • A12 — AGREE: Both strings precede a search term, for which “buscar … por” is the appropriate construction.
  • A13 — NUANCE: The current wording is awkward, but “orçamento de projeto” loses the English grant-funding distinction; revise the sentence while preserving that meaning.
  • A14 — AGREE: “Importações” is the appropriate Portuguese noun and matches the catalog’s terminology.
  • A15 — AGREE: In this terminology-and-cascade context, “conceito mais específico” expresses narrower scope more naturally than “mais estreito.”
  • A16 — AGREE: Controls supplies fromNow() text, so removing “em” prevents “salvo em há 5 minutos.”
  • A17 — AGREE: The catalog supports these corrections: “de Origem,” “Ver membros,” “Sources de origem e de destino,” and feminine “Nenhuma” for “Decisão.”

B. Disagreements

  • B1 — AGREE: Separate origins prevent Mapper storage from supplying zh to TBv3 through normal usage, so the real crash merits a low-severity defensive guard.
  • B2 — AGREE: Main already overwrites i18n.languages, making removal optional cleanup rather than a blocker introduced by these PRs.
  • B3 — NUANCE: Keep “Aposentado” acceptable and raise a nonblocking glossary question, without treating the cited external terminology as independently verified in this pass.
  • B4 — AGREE: Require focused corrections for introduced defects and unrelated lockfile churn, while moving broader coverage, formatting, branding, and glossary work to follow-ups.
  • B5 — AGREE: Retaining masculine “Source” while translating transparent resource names is defensible; document the convention and resolve capitalization and Bridge/Ponte outliers with Filipe.

Net changes to my pass-1 report

  • Downgrade the stored-zh crash to low, nonblocking.
  • Reclassify the hierarchy overwrite as pre-existing optional cleanup.
  • Withdraw full catalog coverage, existing branding omissions, and broader formatting work as merge requirements.
  • Replace hypothetical event combinations with backend-supported cases; add missing User coverage and dashboard capitalization.
  • Add the escaping regression and verified sentence defects; retain the lint concern, with CI confirmation unavailable.
  • Narrow Mapper’s verdict to a small correction list, with broader terminology decisions deferred.
  • No files modified.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/mapper Mapping tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants