Skip to content

Conversation

@cammiida
Copy link
Contributor

@cammiida cammiida commented Jan 28, 2026

Description

Verification

  • Related issues are connected (if applicable)
  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)

Summary by CodeRabbit

  • New Features

    • Optional lang query parameter for pages so users can request a preferred language for text resources.
    • Text resources included in global bootstrap data for initial load.
    • Slug-based routing constraint added for stricter org/app URL validation.
  • Bug Fixes & Improvements

    • Improved language resolution across URL, cookie and user preference flows; added server-side logging.
  • Refactor

    • Simplified text-resource plumbing: replaced provider nesting with a single hook-based API and reduced runtime wrappers.
  • Tests

    • Test suites updated to use the new hook and shared mocked resource maps; test runner now clears mocks between tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added skip-releasenotes Issues that do not make sense to list in our release notes skip-second-approval Pull requests that only need one approval from a reviewer. labels Jan 28, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Backend accepts an optional lang query and slug-constrained routes; bootstrap returns resolved text resources. Frontend replaces provider/context text-resource plumbing with React Query hooks, removes LangToolsStore/provider, adds useLangToolsDataSources and useTextResources, and updates many tests to mock the hook-based API.

Changes

Cohort / File(s) Summary
Backend: Home, routing & bootstrap
src/App/backend/src/Altinn.App.Api/Controllers/HomeController.cs, src/App/backend/src/Altinn.App.Api/Infrastructure/Routing/SlugRouteConstraint.cs, src/App/backend/src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs, src/App/backend/src/Altinn.App.Core/Features/Bootstrap/BootstrapGlobalService.cs, src/App/backend/src/Altinn.App.Core/Features/Bootstrap/IBootstrapGlobalService.cs, src/App/backend/src/Altinn.App.Core/Features/Bootstrap/Models/BootstrapGlobalResponse.cs
HomeController route tightened to slug constraint and now accepts optional lang query; logger injected. Slug route constraint added and registered. Bootstrap service signature expanded to accept org/app/lang, implements multi-source language resolution and fetches text resources; response now includes TextResources.
Backend: OpenAPI & snapshots
src/App/backend/test/Altinn.App.Api.Tests/OpenApi/...SaveJsonSwagger.verified.json, src/App/backend/test/Altinn.App.Integration.Tests/..._snapshots/*.verified.txt, src/App/backend/test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
API specs and integration snapshots updated to reflect slugged routes and added lang query parameter and constructor/method signature changes.
Frontend: Text resources redesign
src/App/frontend/src/features/language/textResources/TextResourcesProvider.tsx, src/App/frontend/src/features/language/LangToolsStore.tsx, src/App/frontend/src/features/language/LangDataSourcesProvider.tsx, src/App/frontend/src/features/language/useLangToolsDataSources.tsx
Provider/context implementations removed; replaced by React Query based APIs: useTextResources, toTextResourceMap, and new useLangToolsDataSources hook and related types. Legacy provider exports removed.
Frontend: App wiring & provider tree
src/App/frontend/src/index.tsx, src/App/frontend/src/core/ui/RenderStart.tsx, src/App/frontend/src/test/renderWithProviders.tsx
Removed TextResourcesProvider and LangToolsStoreProvider wrappers from app and test provider trees; simplified provider nesting and test render helpers.
Frontend: Tests & mocks migration
src/App/frontend/src/setupTests.ts, src/App/frontend/src/__mocks__/getTextResourcesMock.ts, many src/App/frontend/src/**/*.test.tsx, e2e test files under src/App/frontend/test/e2e/...
Tests updated to mock useTextResources / provide toTextResourceMap instead of mocking fetchTextResources; test setup injects textResources into global data and globally mocks useTextResources; e2e tests now use interceptAltinnAppGlobalData helper.
Frontend: Removed utilities & small edits
src/App/frontend/src/core/queries/useQueryWithStaleData.ts, src/App/frontend/src/core/routing/types.ts, src/App/frontend/src/core/texts/appTexts.ts, src/App/frontend/src/global.ts, src/App/frontend/jest.config.js, src/App/frontend/src/queries/types.ts
Removed useQueryWithStaleData. Added SearchParams.Language = 'lang'. Simplified appTexts logic to drop provider dependency. Extended global data with optional textResources. Jest config now clearMocks: true. fetchTextResources added to ignored queries types.
Misc — doc/formatting & minor test adjustments
various test and small backend doc edits
Minor XML/doc updates, formatting tweaks, and numerous test scaffolding adjustments to adopt the hook-based text-resource approach.

Sequence Diagram(s)

sequenceDiagram
    participant Client as HTTP Client
    participant HomeCtrl as HomeController
    participant Router as ASP.NET Router
    participant Bootstrap as BootstrapGlobalService
    participant Profile as ProfileClient
    participant Resources as AppResources

    Client->>Router: GET /{org:slug}/{app:slug}?lang=en
    Router->>HomeCtrl: route to HomeController.Index(org, app, returnUrl, lang)
    HomeCtrl->>Bootstrap: GetGlobalState(org, app, returnUrl, "en")
    Bootstrap->>Bootstrap: Determine language (URL -> cookie -> user pref -> default)
    Bootstrap->>Profile: GetUserProfile()
    Profile-->>Bootstrap: user preferences
    Bootstrap->>Resources: GetTextResources(org, app, resolvedLanguage)
    Resources-->>Bootstrap: TextResource result
    Bootstrap-->>HomeCtrl: BootstrapGlobalResponse (includes TextResources, AvailableLanguages)
    HomeCtrl-->>Client: Rendered HTML with embedded bootstrap state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 I hopped through code with nimble paws,

Swapped providers for queries and cleaner laws,
Languages settled from URL to cookie,
Tests now mock hooks—no old fetch rookie,
A small carrot dance for tidy cause!

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely empty, containing only the unused template placeholders. No description details, rationale, or verification information has been provided by the author. Provide a detailed description explaining the refactor's purpose, key changes (e.g., moving text resources to global data, provider restructuring), and confirm verification steps completed.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor: textresources in globaldata' clearly relates to the main changeset, which refactors how text resources are handled by moving them into global data and restructuring related providers and hooks throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cammiida cammiida force-pushed the refactor/textresources-in-globaldata branch from b4e8c16 to 1bea713 Compare January 28, 2026 13:22
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/App/backend/src/Altinn.App.Api/Controllers/HomeController.cs (1)

78-93: Validate/normalise lang before use.
lang is passed straight through; unsupported values (e.g., region variants) can cause missing resources or unexpected fallbacks. Consider normalising and rejecting invalid codes so the system behaves predictably.

🔧 Suggested guard (normalise and fallback)
@@
     public async Task<IActionResult> Index(
         [FromRoute] string org,
         [FromRoute] string app,
         [FromQuery] bool dontChooseReportee,
         [FromQuery] string? returnUrl,
         [FromQuery] string? lang = null
     )
     {
+        if (!string.IsNullOrWhiteSpace(lang))
+        {
+            lang = lang.Trim().ToLowerInvariant();
+            var isValid = lang.Length is >= 2 and <= 3;
+            if (isValid)
+            {
+                for (var i = 0; i < lang.Length; i++)
+                {
+                    var c = lang[i];
+                    if (c < 'a' || c > 'z')
+                    {
+                        isValid = false;
+                        break;
+                    }
+                }
+            }
+
+            if (!isValid)
+            {
+                _logger.LogWarning("Ignoring unsupported lang '{Lang}'", lang);
+                lang = null;
+            }
+        }
+
         // See comments in the configuration of Antiforgery in MvcConfiguration.cs.
         var tokens = _antiforgery.GetAndStoreTokens(HttpContext);

Based on learnings: “text resource files only support ISO 639 standard parts 1 and 3 language codes (2-3 letter codes like "nb", "en", "nno"). Extended language variants with region codes (like "nb_NO" or "en-GB") are not supported.”

🤖 Fix all issues with AI agents
In
`@src/App/backend/src/Altinn.App.Core/Features/Bootstrap/BootstrapGlobalService.cs`:
- Around line 159-164: The deserialization of the language cookie using
JsonSerializer.Deserialize<string> in BootstrapGlobalService can throw
JsonException on malformed JSON; wrap the call that produces languageCookieValue
in a try-catch that catches JsonException (and optionally Exception), log a
warning or error via _logger including the offending languageCookie value and
context (org/app) and return null when deserialization fails so bootstrap flow
continues safely instead of throwing.

In
`@src/App/frontend/src/features/language/textResources/TextResourcesProvider.tsx`:
- Around line 16-18: getTextResourcesFromWindow currently assumes
window.altinnAppGlobalData exists and will throw if not; change the
implementation to safely access window.altinnAppGlobalData.textResources (e.g.,
using optional chaining or a null check) and return a sensible default (like an
empty array/object) when the global is undefined so Storybook/tests/non-app
contexts don't crash; update the function getTextResourcesFromWindow to use this
guarded access and return the safe default.

In `@src/App/frontend/src/features/language/useLangToolsDataSources.tsx`:
- Around line 28-31: selectedAppLanguage can include region variants like
"nb-NO" which bypass validation and cause getLanguageFromCode to fall back to
"en"; fix by normalising region-formatted codes before lookup in
useLangToolsDataSources (or update getLanguageFromCode) — extract the primary
language subtag (substring before '-') or lower-case and trim the value, then
call getLanguageFromCode(normalised) and/or add explicit fallback mapping for
common region codes (e.g., treat "nb-..." as "nb" and "nn-..." as "nn") so
language resolution never silently defaults to "en".
🧹 Nitpick comments (12)
src/App/frontend/src/features/language/textResources/TextResourcesProvider.tsx (1)

19-39: Align React Query key/config with the shared queryOptions pattern.

Consider extracting query configuration into a queryOptions helper and using an object-based key to match the project convention and keep query config shareable across callers. Please confirm queryOptions is available in the TanStack Query version used here. As per coding guidelines, "Use objects for managing TanStack Query keys and functions; use queryOptions for sharing query configuration centrally".

src/App/frontend/src/features/expressions/shared-functions.test.tsx (1)

25-25: Consider seeding the QueryClient instead of mocking the hook.

For React Query-backed data, seeding QueryClient.setQueryData keeps tests closer to real cache behaviour and avoids hook-level mocks. Based on learnings, "In Altinn/altinn-studio PR reviews, when unit tests need React Query/TanStack Query data, prefer seeding with QueryClient.setQueryData over rendering/mocking use…Query hooks and waiting for isSuccess. Flag patterns like renderHookWithMockStore(() => use…Query(...)) and waitFor(() => expect(result.current.isSuccess).toBe(true)) and recommend the simpler setQueryData approach. Cite the internal Confluence guideline on component tests and the best-practice example in useAddItemToLayoutMutation.test.ts (lines updated 08.08.2025)."

Also applies to: 189-194

src/App/frontend/test/e2e/integration/frontend-test/language.ts (1)

28-28: Test name may need updating to reflect cookie-based storage.

The test is named 'should not crash if language is stored as "null" in local storage' but now clears a cookie instead. Consider renaming to reflect the actual storage mechanism.

📝 Suggested test name update
- it('should not crash if language is stored as "null" in local storage', () => {
+ it('should not crash if language cookie is cleared', () => {
src/App/frontend/test/e2e/integration/frontend-test/instantiation.ts (1)

124-139: Consider extracting the duplicated text resource definition.

This text resource definition is identical to lines 68-83. While duplication in tests is often acceptable for clarity, you could extract a shared constant if preferred:

const customErrorTooLongResource = {
  id: 'custom_error_too_long',
  value: 'Verdien kan ikke være lengre enn {0}, den er nå {1}',
  variables: [
    { key: 'max_length', dataSource: 'customTextParameters' },
    { key: 'current_length', dataSource: 'customTextParameters' },
  ],
};
src/App/frontend/src/index.tsx (1)

49-71: Router created inline on every render - consider hoisting.

createBrowserRouter is called inline within the JSX, meaning it will be recreated on every render of the parent component. React Router recommends creating the router instance outside of the React tree to avoid unnecessary re-instantiation.

♻️ Proposed fix: hoist router creation
+const router = createBrowserRouter(
+  [
+    {
+      path: '*',
+      element: (
+        <NavigationEffectProvider>
+          <ErrorBoundary>
+            <Root />
+          </ErrorBoundary>
+        </NavigationEffectProvider>
+      ),
+    },
+  ],
+  {
+    future: {
+      v7_relativeSplatPath: true,
+    },
+    basename: `/${window.org}/${window.app}`,
+  },
+);

 document.addEventListener('DOMContentLoaded', () => {
   propagateTraceWhenPdf();

   const container = document.getElementById('root');
   const root = container && createRoot(container);
   root?.render(
     <AppQueriesProvider {...queries}>
       <ErrorBoundary>
         <AppPrefetcher />
-        <RouterProvider
-          router={createBrowserRouter(
-            [
-              {
-                path: '*',
-                element: (
-                  <NavigationEffectProvider>
-                    <ErrorBoundary>
-                      <Root />
-                    </ErrorBoundary>
-                  </NavigationEffectProvider>
-                ),
-              },
-            ],
-            {
-              future: {
-                v7_relativeSplatPath: true,
-              },
-              basename: `/${window.org}/${window.app}`,
-            },
-          )}
-          future={{ v7_startTransition: true }}
-        />
+        <RouterProvider router={router} future={{ v7_startTransition: true }} />
       </ErrorBoundary>
     </AppQueriesProvider>,
   );
 });
src/App/frontend/src/layout/RepeatingGroup/Summary/SummaryRepeatingGroup.test.tsx (1)

10-23: Consider using the global mock from setupTests.ts or extracting shared mock.

This mock pattern is identical to the one in SummaryGroupComponent.test.tsx. Since setupTests.ts already provides a global mock for useTextResources, you could either:

  1. Add these keys to getTextResourceMapMock() if they're commonly needed, or
  2. Extract this mock setup to a shared test utility if multiple tests need the same overrides.

That said, the current approach works correctly and provides clear isolation for these tests.

src/App/frontend/src/layout/Group/SummaryGroupComponent.test.tsx (1)

9-22: Identical mock to SummaryRepeatingGroup.test.tsx - consider consolidation.

This mock is an exact duplicate of the one in SummaryRepeatingGroup.test.tsx. Both files could share a common mock setup to reduce maintenance burden.

src/App/frontend/src/components/form/Form.test.tsx (1)

9-9: Use import type for type-only imports.

TextResourceMap is only used as a type annotation. Using import type ensures it's erased at compile time and signals intent.

Suggested fix
-import { TextResourceMap } from 'src/features/language/textResources';
+import type { TextResourceMap } from 'src/features/language/textResources';
src/App/backend/src/Altinn.App.Core/Features/Bootstrap/BootstrapGlobalService.cs (1)

102-102: Use structured logging placeholders instead of string interpolation.

String interpolation in log messages bypasses structured logging benefits like filtering and performance optimisations when the log level is disabled.

Suggested fix
-        _logger.LogDebug($"languageCookieValue: {languageCookieValue}");
+        _logger.LogDebug("languageCookieValue: {LanguageCookieValue}", languageCookieValue);
src/App/backend/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json (1)

2529-2536: Clarify supported language code format for lang.

Consider stating that the value must be an ISO 639-1/3 code (e.g., nb, en, nno) and that region variants are not supported. This avoids ambiguity for API consumers.

Based on learnings, text resource files only support ISO 639 standard parts 1 and 3 language codes (2-3 letter codes like "nb", "en", "nno"), while extended variants like "nb_NO" or "en-GB" are not supported.

src/App/frontend/src/core/texts/appTexts.ts (1)

17-21: Consider renaming to reflect actual behaviour.

The function useHasAppTextsYet now only checks for orgs availability, but the name suggests it checks for text resources. Since text resources are now loaded via global data (window), this check may be appropriate, but the name could be misleading to future maintainers.

Consider renaming to something like useHasAppDataYet or adding a comment explaining why orgs availability is sufficient.

src/App/frontend/src/features/language/useLangToolsDataSources.tsx (1)

11-19: Remove 'node' and 'transposeSelector' from the Omit statement—they do not exist on TextResourceVariablesDataSources.

The interface currently contains only defaultDataType, formDataTypes, and formDataSelector among the keys listed for omission. Keeping non-existent keys in the Omit is redundant and obscures the actual contract of the type, potentially masking future type drift. Trim the statement to:

export type LimitedTextResourceVariablesDataSources = Omit<
  TextResourceVariablesDataSources,
  'defaultDataType' | 'formDataTypes' | 'formDataSelector'
>;

@github-actions
Copy link
Contributor

🔍 Lighthouse Report

📊 View Performance Report

@github-actions
Copy link
Contributor

🔍 Lighthouse Report

📊 View Performance Report

@cammiida cammiida force-pushed the refactor/textresources-in-globaldata branch from 1bea713 to 7391537 Compare January 28, 2026 13:37
@github-actions
Copy link
Contributor

🔍 Lighthouse Report

📊 View Performance Report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/App/backend/src/Altinn.App.Api/Controllers/HomeController.cs`:
- Around line 56-58: The XML doc for the HomeController constructor is missing a
<param> entry for the ILogger<HomeController> parameter named logger; update the
XML documentation block above the HomeController constructor to add a <param
name="logger">...</param> describing the logger parameter (place it alongside
the existing <param> for appMetadata) so static analysis no longer flags the
missing tag.
- Line 34: Remove the unused private readonly ILogger<HomeController> _logger
field from the HomeController class and its assignment in the HomeController
constructor (where _logger is currently set) to eliminate dead code;
alternatively, if logging is intended, use the _logger instance in relevant
methods (e.g., inside HomeController actions) for logging calls—but since the
reviewer asked to remove it, delete the _logger field and its constructor
parameter/assignment references (symbols: _logger, ILogger<HomeController>,
HomeController constructor) to keep the class clean.

In
`@src/App/frontend/src/features/language/textResources/TextResourcesProvider.tsx`:
- Around line 48-57: The warning is fired any time query.data is falsy
(including during loading); update useTextResources to only call
window.logWarnOnce after the query has finished (e.g., check query.isSuccess or
query.isFetched) so you warn only when the fetch completed but returned no
data—locate the useTextResources function and the useTextResourcesQuery call and
move the logWarnOnce behind a conditional that verifies the query finished and
still has no data.

In
`@src/App/frontend/src/layout/RepeatingGroup/Container/RepeatingGroupContainer.test.tsx`:
- Around line 24-33: Replace the manual jest.mock of useTextResources with
seeding the React Query cache: in the test that calls
renderWithInstanceAndLayout (which wraps AppQueriesProvider), obtain the
QueryClient used by that provider (or create one) and call
queryClient.setQueryData(['fetchTextResources', language], textResourceMap) with
the same map currently returned by the mock; remove the jest.mock block
referencing useTextResources and ensure the test imports/uses the same language
key as the query key so useTextResources (which calls
useQuery(['fetchTextResources', language])) reads the seeded data.
🧹 Nitpick comments (4)
src/App/backend/src/Altinn.App.Core/Features/Bootstrap/BootstrapGlobalService.cs (1)

102-102: Avoid string interpolation in logging calls.

Using $"languageCookieValue: {languageCookieValue}" bypasses structured logging benefits and incurs string allocation even when the log level is disabled. Use structured logging parameters instead.

Suggested fix
-        _logger.LogDebug($"languageCookieValue: {languageCookieValue}");
+        _logger.LogDebug("languageCookieValue: {LanguageCookieValue}", languageCookieValue);
src/App/backend/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json (1)

2529-2537: Consider aligning lang with existing language query parameters (or document the difference).

Most other endpoints expose language, while these Home endpoints add lang. If this isn’t intentionally different, a consistent name helps codegen clients and avoids confusion; otherwise, a brief note explaining the divergence would help API consumers.

Also applies to: 2586-2594, 2643-2651, 2701-2708, 2766-2773, 2831-2838, 2904-2911

src/App/frontend/src/features/validation/ValidationPlugin.test.tsx (1)

30-33: Consider moving mock inside beforeEach for consistency.

The mock is currently at the describe block level, which executes once when the module loads. While this works for the current tests (all using the same text resources), placing the mock inside a beforeEach or the render function would be more consistent with the pattern used in other test files in this PR and would make it easier to override text resources for specific tests in the future.

♻️ Suggested refactor
 describe('ValidationPlugin', () => {
-  jest.mocked(useTextResources).mockImplementation(() => ({
-    Form: { value: 'This is a page title' },
-    NextPage: { value: 'This is the next page title' },
-  }));
-
+  beforeEach(() => {
+    jest.mocked(useTextResources).mockImplementation(() => ({
+      Form: { value: 'This is a page title' },
+      NextPage: { value: 'This is the next page title' },
+    }));
+  });
+
   describe('validation visibility', () => {
src/App/frontend/src/__mocks__/getTextResourcesMock.ts (1)

1-35: Avoid duplicate mock data by deriving the map from the array.

Having two separate mock datasets risks drift. Consider building the map from getTextResourcesMock() so the list and map stay consistent.

♻️ Suggested refactor
 export function getTextResourceMapMock(): TextResourceMap {
-  return {
-    'option.from.rep.group.label': {
-      value: 'The value from the group is: {0}',
-      variables: [
-        {
-          dataSource: 'dataModel.default',
-          key: 'someGroup[{0}].labelField',
-        },
-      ],
-    },
-    'option.from.rep.group.description': {
-      value: 'Description: The value from the group is: {0}',
-      variables: [
-        {
-          dataSource: 'dataModel.default',
-          key: 'someGroup[{0}].labelField',
-        },
-      ],
-    },
-    'option.from.rep.group.helpText': {
-      value: 'Help Text: The value from the group is: {0}',
-      variables: [
-        {
-          dataSource: 'dataModel.default',
-          key: 'someGroup[{0}].labelField',
-        },
-      ],
-    },
-    'accordion.title': { value: 'This is a title' },
-    FormLayout: { value: 'This is a page title' },
-  };
+  return getTextResourcesMock().reduce<TextResourceMap>((acc, { id, ...resource }) => {
+    acc[id] = resource;
+    return acc;
+  }, {});
 }

@github-actions
Copy link
Contributor

🔍 Lighthouse Report

📊 View Performance Report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/App/backend/src/Altinn.App.Api/Controllers/HomeController.cs (1)

88-145: Preserve lang when redirecting to authentication. With the new lang query, unauthenticated users will lose the selected language because the redirect URL drops it. Consider forwarding lang in goToUrl.

Proposed fix
-        string goToUrl = HttpUtility.UrlEncode($"{scheme}://{Request.Host}/{org}/{app}");
+        string goToUrl = $"{scheme}://{Request.Host}/{org}/{app}";
+        if (!string.IsNullOrEmpty(lang))
+        {
+            goToUrl = $"{goToUrl}?lang={lang}";
+        }
+        goToUrl = HttpUtility.UrlEncode(goToUrl);
🧹 Nitpick comments (3)
src/App/frontend/src/features/language/textResources/TextResourcesProvider.tsx (1)

21-41: Centralise React Query keys/config with object-based keys + queryOptions.
This keeps keys consistent and makes reuse across hooks/tests easier.

Suggested refactor
-import { useQuery } from '@tanstack/react-query';
+import { queryOptions, useQuery } from '@tanstack/react-query';

+const textResourceQueryKeys = {
+  byLanguage: (language: string) => ['textResources', language] as const,
+};
+
+const textResourcesQueryOptions = (
+  selectedLanguage: string,
+  textResourcesFromWindow: ITextResourceResult | undefined,
+) =>
+  queryOptions<TextResourceMap, HttpClientError>({
+    queryKey: textResourceQueryKeys.byLanguage(selectedLanguage),
+    queryFn: async () => {
+      const textResourceResult =
+        textResourcesFromWindow && textResourcesFromWindow.language === selectedLanguage
+          ? textResourcesFromWindow
+          : await fetchTextResources(selectedLanguage);
+      return toTextResourceMap(textResourceResult);
+    },
+  });

-function getTextResourceQueryKey(selectedLanguage: string) {
-  return ['fetchTextResources', selectedLanguage] as const;
-}
-
-  const query = useQuery<TextResourceMap, HttpClientError>({
-    queryKey: getTextResourceQueryKey(selectedLanguage),
-    queryFn: async () => {
-      const textResourceResult =
-        textResourcesFromWindow && textResourcesFromWindow.language === selectedLanguage
-          ? textResourcesFromWindow
-          : await fetchTextResources(selectedLanguage);
-
-      return toTextResourceMap(textResourceResult);
-    },
-  });
+  const query = useQuery(textResourcesQueryOptions(selectedLanguage, textResourcesFromWindow));

As per coding guidelines, “Use objects for managing TanStack Query keys and functions; use queryOptions for sharing query configuration centrally.”

src/App/backend/src/Altinn.App.Core/Features/Bootstrap/BootstrapGlobalService.cs (2)

148-158: Consider using Debug level for normal flow logging.

Lines 148 and 155-158 use LogInformation for cases that are part of normal operation (missing cookie or successful deserialization). However, the GetTextResources method uses LogDebug for similar decision-point logging (lines 108, 118, 125, 129). This inconsistency may result in noisy logs in production.

🔧 Suggested change to align logging levels
         if (!_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue(cookieKey, out var languageCookie))
         {
-            _logger.LogInformation("No language cookie found for cookieKey {CookieKey}", cookieKey);
+            _logger.LogDebug("No language cookie found for cookieKey {CookieKey}", cookieKey);
             return null;
         }

         try
         {
             var languageCookieValue = JsonSerializer.Deserialize<string>(languageCookie);
-            _logger.LogInformation(
+            _logger.LogDebug(
                 "Successfully deserialized language cookie value for cookieKey {CookieKey}",
                 cookieKey
             );
             return languageCookieValue;
         }

163-163: Minor grammatical issue in log message.

"failed deserialize" should be "failed to deserialize".

✏️ Suggested fix
-            _logger.LogWarning(ex, "Language cookie with key {CookieKey} found, but failed deserialize it.", cookieKey);
+            _logger.LogWarning(ex, "Language cookie with key {CookieKey} found, but failed to deserialize it.", cookieKey);

@github-actions
Copy link
Contributor

🔍 Lighthouse Report

📊 View Performance Report

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

Labels

skip-releasenotes Issues that do not make sense to list in our release notes skip-second-approval Pull requests that only need one approval from a reviewer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant