Conversation
✅ Deploy Preview for btcmap ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
📝 WalkthroughWalkthroughAdds full internationalization: new Changes
Sequence DiagramsequenceDiagram
participant App as App Init
participant Locale as getInitialLocale()
participant Storage as localStorage
participant Browser as Browser Language
participant I18n as svelte-i18n
participant Component as UI Component
App->>Locale: call getInitialLocale()
Locale->>Storage: read 'language'
alt stored language valid
Storage-->>Locale: return stored locale (en or pt-BR)
else no valid stored language
Locale->>Browser: read navigator.language
alt startsWith 'pt'
Browser-->>Locale: choose pt-BR
else
Browser-->>Locale: default to en
end
end
Locale-->>I18n: init(initialLocale, fallbackLocale: en)
I18n->>I18n: register locales via lazy imports (en, pt-BR)
I18n-->>App: ready / isLoading false
App->>Component: render using $locale and $_()
Component-->>App: display localized UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||||
There was a problem hiding this comment.
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/routes/map/components/MerchantListPanel.svelte (1)
384-423:⚠️ Potential issue | 🟡 MinorLocalize the result-count strings and zoom button aria-label to ensure complete i18n coverage.
These strings render English-only text: the "X of Y results" count (lines 391-395), the "count results" count (lines 396-397), and the zoom button's aria-label (lines 417-419).
The translation file already includes
"resultsCount": "{count} result(s)", but the component uses hardcoded English for these cases instead of$_()function calls.Add the following keys to
src/lib/i18n/locales/en.jsonand all translation files:
"search.resultsFiltered": "{count} of {total} result(s)"for the filtered results case"aria.zoomToResults": "Zoom map to show {count} result(s)"for the button aria-labelThen update the component to use these keys with appropriate
valuesparameters.
🤖 Fix all issues with AI agents
In `@src/components/layout/Footer.svelte`:
- Around line 59-72: Replace the hard-coded aria-label strings on the language
buttons with localized keys: create aria.switchToEnglish and
aria.switchToPortuguese in your locale JSONs (under an "aria" object) and use
the app's localization function to set the attributes instead of literal text;
update the two button attributes that reference aria-label (the elements that
check $locale and call switchLanguage('pt-BR')) to call the translator (e.g.,
t('aria.switchToEnglish') / t('aria.switchToPortuguese') or your project's
equivalent) so the labels follow the active locale.
In `@src/lib/i18n/locales/en.json`:
- Line 129: The string value for the "improveVisibility" key contains a grammar
typo: change "it's" (contraction) to the possessive "its" so the entry
"improveVisibility" reads "Boost this location to improve its visibility on the
map." — update the value for the "improveVisibility" JSON key accordingly.
In `@src/lib/i18n/locales/pt-BR.json`:
- Line 8: The pt-BR locale sets "home.hashtag" to "#SPEDN ✊" which differs from
other locales — confirm whether the emoji change is intentional; if you want
consistent hashtags across locales, update the value of the "home.hashtag" key
in src/lib/i18n/locales/pt-BR.json to match the canonical emoji used in the EN
locale (and, if applicable, align other locale files' "home.hashtag" values to
the same emoji for consistency).
🧹 Nitpick comments (5)
src/routes/+page.svelte (1)
9-9: Inconsistent import path for i18n.This file imports
_directly from'svelte-i18n', but other files in this PR (e.g.,PaymentMethodIcon.svelte,MapSearchBar.svelte) import from'$lib/i18n'. Consider using the centralized$lib/i18nimport for consistency across the codebase.Suggested change
- import { _ } from 'svelte-i18n'; + import { _ } from '$lib/i18n';src/routes/+layout.svelte (1)
80-86: Consider adding error handling for i18n initialization failure.The loading state blocks the entire app until i18n initializes. If i18n fails to load (e.g., network error fetching locale files), users would be stuck on the loading screen indefinitely.
Consider adding a timeout or error boundary that falls back to rendering the app without translations.
src/components/layout/Header.svelte (1)
9-9: Consider consistent i18n import path.Same as noted in
+page.svelte- this imports directly from'svelte-i18n'while other components use'$lib/i18n'.src/routes/map/components/MapSearchBar.svelte (1)
136-139: Consider reformatting for readability.The inline conditional with split tags across lines is harder to read. Consider restructuring:
Optional formatting improvement
- {$_('search.nearby')}{`#if` isLoadingCount}<span class="opacity-60"> - ...</span - >{:else if formattedCount} - {formattedCount}{/if} + {$_('search.nearby')}{`#if` isLoadingCount}<span class="opacity-60">...</span>{:else if formattedCount}{formattedCount}{/if}src/components/layout/Footer.svelte (1)
9-15: Prefer SvelteKit’sbrowserhelper for the SSR guard.Keeps SSR checks consistent and avoids repeating
typeof windowpatterns.♻️ Proposed update
+ import { browser } from '$app/environment'; import SocialLink from '$components/SocialLink.svelte'; import { socials } from '$lib/store'; import { env } from '$env/dynamic/public'; import { locale, _ } from '$lib/i18n'; import Icon from '$components/Icon.svelte'; import { trackEvent } from '$lib/analytics'; function switchLanguage(newLocale: string) { trackEvent('language_switch', { language: newLocale }); locale.set(newLocale); - if (typeof window !== 'undefined') { + if (browser) { localStorage.setItem('language', newLocale); } }
…#434 - Install svelte-i18n package - Create i18n configuration and directory structure - Add English and Brazilian Portuguese translation files - Translate home page hero section (headline, buttons, description) - Translate all navigation items (top-level menu and dropdowns) - Use placeholder support for OpenStreetMap link in description - Make navigation items reactive for future locale switching 🤖 Generated with opencode
- Import isLoading from svelte-i18n in +layout.svelte
- Add loading state while i18n initializes
- Wrap main content in {#if !} block
- Prevents 'Cannot format a message without first setting the initial locale' error
- Shows 'Loading...' text briefly while locale data loads
🤖 Generated with opencode
- Add translate icon + language toggle in footer (inline with links) - Export locale store from i18n configuration - Add localStorage persistence for language preference - Translate 'Language'/'Idioma' label based on active language - Style active language with bold + underline - Style inactive language as clickable link with hover - Manual switching only (no auto-detection for now) 🤖 Generated with opencode
- Remove 'Language'/'Idioma' label for cleaner look - Change separator from · to / (more standard) - Fix vertical alignment with other footer links (remove inline-flex) - Make translate icon match text color - Reduce spacing between footer links (mx-2.5 → mx-1.5) 🤖 Generated with opencode
- Change icon color from text-body to text-link for consistency - Icon now matches the visual style of footer links in light mode 🤖 Generated with opencode
- Add unique 'id' field to navLinks for language-independent identification - Change dropdown conditionals from comparing title text to comparing id - Fixes dropdown menus disappearing when switching to Portuguese - Desktop and mobile navigation now work correctly in both languages 🤖 Generated with opencode
- Add getInitialLocale() function with smart detection logic - Check localStorage preference first (user choice wins) - Detect browser language via navigator.language as fallback - Map Portuguese variants (pt, pt-BR, pt-PT) to pt-BR - Mirrors theme detection pattern (localStorage > system > default) - Benefits 30% of traffic (Brazilian users auto-detected) 🤖 Generated with opencode
- Add search, categories, merchant, verification sections - Add payment, boost, status, info sections - Add comments, errors, ARIA labels, footer sections - Include Portuguese translations for all new strings - Covers map components, merchant details, and UI elements 🤖 Generated with opencode
- Import locale from svelte-i18n in +layout.svelte - Add reactive statement to update document.documentElement.lang - Lang attribute now changes automatically when user switches language - Improves SEO and accessibility for screen readers 🤖 Generated with opencode
- Export _ translation function from i18n/index.ts - Make footer links use translation keys - Add translations for all footer links - Links now update when language changes - Keep Status, Analytics, White Paper, Cypherpunks as-is (standard names) 🤖 Generated with opencode
- Add translation function import - Translate deleted merchant banner - Translate action buttons: Navigate, Edit, Share, Comments - Translate payment methods section - Translate verification section (Last Surveyed, Verify Location) - Translate boost section (Boost Expires, Boost/Extend buttons) - Translate View Full Details button - All titles and tooltips now translatable 🤖 Generated with opencode
- Add translation function import - Create tooltip key map for btc/ln/nfc methods - Use translation keys for accepted/not accepted/unknown states - Simplify logic with lookup table - All payment method tooltips now translatable 🤖 Generated with opencode
- Add translation function import - Translate search placeholders (worldwide/nearby) - Translate mode toggle buttons (Worldwide/Nearby) - Translate category filter buttons with helper function - Translate all status messages (searching, no results, zoom in) - Translate Show all button - Translate all aria-labels for accessibility - Add getCategoryLabel() helper for dynamic translation - Fix unused variable ESLint warning 🤖 Generated with opencode
- Add translation function import - Translate Unknown placeholder for merchant name - Translate payment method labels passed to PaymentMethodIcon - Translate status badges (Boosted, Verified, Outdated) - All merchant list item text now translatable 🤖 Generated with opencode
- Add translation function import - Translate search placeholder based on mode - Translate mode toggle buttons (Worldwide/Nearby) - Translate all aria-labels (searchInput, clearSearch, searching) - Floating search bar now fully translatable 🤖 Generated with opencode
- Import trackEvent from analytics - Track language_switch event with selected language - Add language_switch to EventName type in analytics.ts - Analytics will now show language preference usage patterns 🤖 Generated with opencode
- Add placeholder support to noCommunity translation
- Add 'created' translation key (en: created, pt: criada)
- Use {@html} with createLink placeholder in merchant page
- Link text now translatable based on locale
- Restores navigation to /communities page
- Addresses PR review functional regression feedback
🤖 Generated with opencode
- Change img alt from method code (btc/ln/nfc) to label prop - Screen readers now announce 'On-chain' instead of 'btc' - Improves accessibility for payment method icons - Label prop is now used and meaningful - Addresses PR review accessibility feedback 🤖 Generated with opencode
- Replace JavaScript condition (link.link !== '/cypherpunks-manifesto.pdf') - Use Tailwind's last:mb-0 pseudo-class selector - All links get mb-2.5 by default, last link overrides with mb-0 - Pure CSS solution - no JavaScript logic needed - More maintainable and automatically adapts to link changes - Cleaner code, language-independent 🤖 Generated with opencode
…434 - Change 'Aplicativos' → 'Apps' (common Brazilian tech term) - Change 'Estatísticas' → 'Stats' (widely understood) - Change 'Apoie-nos' → 'Apoiar' (shorter form) - Change 'Dashboard' → 'Dashboard' (keep English, standard term) - Change 'Classificação' → 'Ranking' (shorter, commonly used) - Change 'Tornar-se um Etiquetador' → 'Tornar-se Etiquetador' (remove article) - Prevents nav text wrapping in desktop header - Maintains readability while fitting fixed-width layout 🤖 Generated with opencode
ef60a9f to
2c1913b
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/routes/merchant/[id]/+page.svelte (3)
247-294:⚠️ Potential issue | 🟡 MinorHardcoded English strings in tippy tooltips.
These tippy tooltip contents remain in English while the rest of the component uses i18n. For consistency, these should use the translation keys that already exist in the locale files (e.g.,
payment.onchainAccepted,verification.verifiedTooltip).🌐 Suggested fix for tippy tooltips
$: thirdPartyTooltip && data && tippy([thirdPartyTooltip], { - content: 'Third party app required' + content: $_('payment.thirdPartyRequired') }); $: onchainTooltip && data && tippy([onchainTooltip], { content: data.osmTags?.['payment:onchain'] === 'yes' - ? 'On-chain accepted' + ? $_('payment.onchainAccepted') : data.osmTags?.['payment:onchain'] === 'no' - ? 'On-chain not accepted' - : 'On-chain unknown' + ? $_('payment.onchainNotAccepted') + : $_('payment.onchainUnknown') }); $: lnTooltip && data && tippy([lnTooltip], { content: data.osmTags?.['payment:lightning'] === 'yes' - ? 'Lightning accepted' + ? $_('payment.lightningAccepted') : data.osmTags?.['payment:lightning'] === 'no' - ? 'Lightning not accepted' - : 'Lightning unknown' + ? $_('payment.lightningNotAccepted') + : $_('payment.lightningUnknown') }); $: nfcTooltip && data && tippy([nfcTooltip], { content: data.osmTags?.['payment:lightning_contactless'] === 'yes' - ? 'Lightning Contactless accepted' + ? $_('payment.contactlessAccepted') : data.osmTags?.['payment:lightning_contactless'] === 'no' - ? 'Lightning contactless not accepted' - : 'Lightning contactless unknown' + ? $_('payment.contactlessNotAccepted') + : $_('payment.contactlessUnknown') }); $: verifiedTooltip && tippy([verifiedTooltip], { - content: 'Verified within the last year' + content: $_('verification.verifiedTooltip') }); $: outdatedTooltip && tippy([outdatedTooltip], { - content: 'Outdated please re-verify' + content: $_('verification.outdatedTooltip') });
827-827:⚠️ Potential issue | 🟡 MinorHardcoded "No comments yet." string.
This string should use the existing translation key
$_('comments.none')for consistency with the rest of the i18n implementation.🌐 Suggested fix
{:else} - <p class="p-5 text-body dark:text-white">No comments yet.</p> + <p class="p-5 text-body dark:text-white">{$_('comments.none')}</p> {/if}
580-583:⚠️ Potential issue | 🟡 MinorHardcoded toast message.
The success toast message "Link copied to clipboard!" should be translated for consistency.
🌐 Suggested fix (requires adding translation key)
on:click={() => { navigator.clipboard.writeText(`https://btcmap.org/merchant/${data.id}`); - successToast('Link copied to clipboard!'); + successToast($_('merchant.linkCopied')); }}Add to locale files:
"merchant": { ... "linkCopied": "Link copied to clipboard!" }src/routes/map/components/MerchantListPanel.svelte (2)
391-397:⚠️ Potential issue | 🟡 MinorResult count text not fully internationalized.
The filtered results count ("X of Y results") uses English text with template literals instead of i18n. This should use a translation key with interpolation for consistency.
🌐 Suggested approach
Add a translation key like
search.filteredResultsCountwith value"{filtered} of {total} result(s)"and use:{:else if selectedCategory !== 'all' && filteredSearchResults.length !== searchResults.length} - {filteredSearchResults.length} of {searchResults.length} result{searchResults.length !== - 1 - ? 's' - : ''} + {$_('search.filteredResultsCount', { values: { filtered: filteredSearchResults.length, total: searchResults.length } })} {:else} - {searchResults.length} result{searchResults.length !== 1 ? 's' : ''} + {$_('search.resultsCount', { values: { count: searchResults.length } })} {/if}
417-419:⚠️ Potential issue | 🟡 Minoraria-label not internationalized.
The "Show All" button's aria-label uses English template string. This should use a translation key for accessibility in other locales.
🌐 Suggested fix
-aria-label="Zoom map to show {filteredSearchResults.length === 1 - ? '1 result' - : `all ${filteredSearchResults.length} results`}" +aria-label={$_('search.showAllAriaLabel', { values: { count: filteredSearchResults.length } })}Add translation key:
"search": { "showAllAriaLabel": "Zoom map to show all {count} result(s)" }
🧹 Nitpick comments (2)
src/components/layout/Header.svelte (1)
9-9: Inconsistent import path for i18n.This file imports
_directly from'svelte-i18n', while other components in this PR (e.g.,PaymentMethodIcon.svelte,MapSearchBar.svelte,MerchantListPanel.svelte) import from'$lib/i18n'. Using the centralized$lib/i18npath ensures consistent configuration and makes future refactoring easier.♻️ Suggested fix
-import { _ } from 'svelte-i18n'; +import { _ } from '$lib/i18n';src/routes/map/components/MerchantListPanel.svelte (1)
361-361: Variable naming shadows i18n function.The destructured variable
_categorycould cause confusion since_is the imported i18n function. Consider using a clearer name likecategoryorcategoryConfig.♻️ Suggested fix
-{`#each` CATEGORY_ENTRIES as [key, _category] (key)} +{`#each` CATEGORY_ENTRIES as [key, category] (key)}

User description
Fixes: #434
Add i18n for EN and PT_BR only for now
Notes:
design somewhat cramped with longer words(let's wait what German will do :P)PR Type
Enhancement
Description
Add comprehensive i18n support for English and Portuguese (Brazil)
Implement language switching with localStorage persistence
Translate all UI text across merchant pages, navigation, and search
Add smart locale detection based on browser language and saved preference
Create translation files with 377 English and 221 Portuguese entries
Diagram Walkthrough
File Walkthrough
14 files
Initialize svelte-i18n with smart locale detectionAdd i18n initialization and loading state wrapperTranslate home page hero, buttons, and descriptionTranslate map loading status messagesTranslate merchant detail page with 40+ translation keysTranslate search, filter, and merchant list UITranslate search bar placeholders and aria labelsMake navigation links reactive with i18n translationsAdd language selector with EN/PT toggle and persistenceTranslate merchant details drawer contentTranslate merchant list item labels and statusTranslate payment method tooltips with key mappingTranslate map loading status fallback textAdd language_switch event type for tracking2 files
Complete English translation file with 377 keysComplete Portuguese (Brazil) translation file with 221 keys1 files
Add svelte-i18n dependency version 4.0.1Summary by CodeRabbit
New Features
Chore
Bug Fixes