Source code for cadrumo.application.user_profile.language_resolver
"""Resolve the active profile's preferred output language for ``core.i18n``.The ``core`` layer remains independent of application modules. It resolves theactive profile's ``preferences.output_language`` preference through a registeredcallback. Each executable host explicitly calls :func:`register_language_resolver`when it composes profile persistence. That function registers:func:`resolve_active_profile_output_language` with:func:`cadrumo.core.i18n.render.register_profile_language_resolver`.Rendering never reads storage. The profile's language is read once into aprocess-wide snapshot by :func:`refresh_active_profile_output_language` -- whenthe host composes, and whenever a session is bound, closed, or the preferenceis written -- and the registered callback only returns that snapshot. A frontendevent loop that renders text therefore never waits on the encrypted profile."""from__future__importannotationsimportthreadingfrom...core.external_constantsimportOutputLanguagefrom...core.i18n.renderimportclear_output_language_cache,register_profile_language_resolverfrom...core.loggingimportget_loggerfrom...domain.user_profile.setup_answersimportPROFILE_OUTPUT_LANGUAGE_PATHfrom.custody_portsimport(clear_profile_output_language_hint,read_profile_output_language_hint,write_profile_output_language_hint,)from.login_session_portimportprofile_current_bucket_session_logger=get_logger(__name__)__all__=["active_profile_output_language_from_storage","mirror_profile_output_language_hint","refresh_active_profile_output_language","register_language_resolver","resolve_active_profile_output_language","resolve_active_profile_output_language_hint","resolve_profile_output_language_hint",]class_ProfileLanguageSnapshot:"""The last language read from storage, shared by every thread of the process."""def__init__(self)->None:self._lock=threading.Lock()self._language:str|None=Nonedefget(self)->str|None:withself._lock:returnself._languagedefset(self,language:str|None)->None:withself._lock:self._language=language_SNAPSHOT=_ProfileLanguageSnapshot()
[docs]defresolve_active_profile_output_language()->str|None:"""Return the snapshotted ``preferences.output_language`` fact; never reads storage."""return_SNAPSHOT.get()
[docs]defrefresh_active_profile_output_language()->str|None:"""Re-read the active profile's language into the snapshot and invalidate rendering. Called where storage is already being touched: host composition, session binding and closing, and the preference write. Never raises; an unreadable profile leaves no profile language, so rendering falls back to settings. """try:language=active_profile_output_language_from_storage()exceptException:_logger.debug("profile output language could not be read; using settings",exc_info=True)language=None_SNAPSHOT.set(language)clear_output_language_cache()returnlanguage
[docs]defactive_profile_output_language_from_storage()->str|None:"""Read the active profile's ``preferences.output_language`` fact from storage. Performs a pure read of workflow state — no mutation, no bucket events — and returns ``None`` when there is no active profile or no language fact, so the caller falls back to the settings default. When no bucket session is currently bound, reads the bucket-local non-secret language hint instead of the encrypted profile envelope. """ifprofile_current_bucket_session()isNone:returnresolve_active_profile_output_language_hint()from..workflow.persistenceimportworkflow_state_repositoryfrom.projectionsimportrecord_to_path_valuesrecord=workflow_state_repository().load().active_profile_record()ifrecordisNone:returnNone# The CLI root may scope this record to the invocation that triggered the# language refresh. A calculation command then consumes it once; hosts# without that scope retain their ordinary independent profile reads.from.profile_record_repositoryimportpublish_invocation_profile_recordpublish_invocation_profile_record(record)returnrecord_to_path_values(record).get(PROFILE_OUTPUT_LANGUAGE_PATH)
[docs]defresolve_active_profile_output_language_hint()->str|None:"""Return the active bucket's last-known output-language hint, if present."""try:from...core.bucket_pointerimportresolve_active_bucket_idbucket_id=resolve_active_bucket_id()ifbucket_idisNone:returnNonereturnresolve_profile_output_language_hint(bucket_id)exceptException:returnNone
[docs]defresolve_profile_output_language_hint(bucket_id:str)->str|None:"""Return a named bucket's last-known output-language hint, if present."""try:from...core.configimportload_settingstrimmed=bucket_id.strip()ifnottrimmed:returnNonereturnread_profile_output_language_hint(storage_root=load_settings().cadrumo_local_storage_root,bucket_id=trimmed,)exceptException:returnNone
[docs]defregister_language_resolver()->None:"""Register :func:`resolve_active_profile_output_language` with ``core.i18n``. Executable hosts invoke this alongside custody and login-session composition, keeping registration explicit and greppable rather than hiding it in a package-import side effect. """register_profile_language_resolver(resolve_active_profile_output_language)refresh_active_profile_output_language()
[docs]defmirror_profile_output_language_hint(bucket_id:str,language:OutputLanguage|None)->None:"""Mirror a profile's language preference into its non-secret bucket hint. The hint answers one question the encrypted preference cannot: which language to speak for a SELECTED profile that is not unlocked. :func:`resolve_active_profile_output_language` falls back to it when no bucket session is bound, and the reader fails soft on absence -- so while nothing wrote the hint, that fallback always returned ``None`` and every locked surface silently took the settings default, however deliberately the operator had chosen a language during setup. It is keyed by bucket, so it serves a locked selection and NOT a logged-out one: once ``logout`` clears the pointer there is no bucket to resolve the hint against, and the settings default is the only answer available. That is a property of the selection model, not a gap in the mirror. Clearing the preference clears the hint, so the two cannot disagree about an absence. Failure is swallowed for the same reason the read is: this mirrors a convenience, and a hint that could not be written must not fail the fact write that owns the real value. """try:from...core.configimportload_settingstrimmed=bucket_id.strip()ifnottrimmed:returnstorage_root=load_settings().cadrumo_local_storage_rootiflanguageisNone:clear_profile_output_language_hint(storage_root=storage_root,bucket_id=trimmed)returnwrite_profile_output_language_hint(storage_root=storage_root,bucket_id=trimmed,language=language,)exceptException:_logger.debug("could not mirror the output-language hint",exc_info=True)