Procházet zdrojové kódy

Oasis release 0.6.9

psy před 1 dnem
rodič
revize
61e6933c38
100 změnil soubory, kde provedl 8597 přidání a 5994 odebrání
  1. 2 0
      .gitignore
  2. 13 5
      README.md
  3. 202 180
      docs/CHANGELOG.md
  4. 180 0
      scripts/build-deb.sh
  5. 1 3
      scripts/patch-node-modules.js
  6. 331 62
      src/backend/backend.js
  7. 2 0
      src/backend/media-favorites.js
  8. 25 0
      src/client/assets/images/world-map.svg
  9. binární
      src/client/assets/images/worldmap-z2.png
  10. 619 3
      src/client/assets/styles/style.css
  11. 153 16
      src/client/assets/translations/oasis_ar.js
  12. 1916 1831
      src/client/assets/translations/oasis_de.js
  13. 156 53
      src/client/assets/translations/oasis_en.js
  14. 200 93
      src/client/assets/translations/oasis_es.js
  15. 177 83
      src/client/assets/translations/oasis_eu.js
  16. 231 135
      src/client/assets/translations/oasis_fr.js
  17. 154 17
      src/client/assets/translations/oasis_hi.js
  18. 1901 1812
      src/client/assets/translations/oasis_it.js
  19. 1755 1666
      src/client/assets/translations/oasis_pt.js
  20. 151 10
      src/client/assets/translations/oasis_ru.js
  21. 155 18
      src/client/assets/translations/oasis_zh.js
  22. 11 2
      src/client/middleware.js
  23. 31 1
      src/configs/banking-epochs.json
  24. 4 2
      src/configs/config-manager.js
  25. 4 2
      src/configs/oasis-config.json
  26. 223 0
      src/maps/map_renderer.js
  27. binární
      src/maps/tiles/0/0_0.png
  28. binární
      src/maps/tiles/1/0_0.png
  29. binární
      src/maps/tiles/1/0_1.png
  30. binární
      src/maps/tiles/1/1_0.png
  31. binární
      src/maps/tiles/1/1_1.png
  32. binární
      src/maps/tiles/2/0_0.png
  33. binární
      src/maps/tiles/2/0_1.png
  34. binární
      src/maps/tiles/2/0_2.png
  35. binární
      src/maps/tiles/2/0_3.png
  36. binární
      src/maps/tiles/2/1_0.png
  37. binární
      src/maps/tiles/2/1_1.png
  38. binární
      src/maps/tiles/2/1_2.png
  39. binární
      src/maps/tiles/2/1_3.png
  40. binární
      src/maps/tiles/2/2_0.png
  41. binární
      src/maps/tiles/2/2_1.png
  42. binární
      src/maps/tiles/2/2_2.png
  43. binární
      src/maps/tiles/2/2_3.png
  44. binární
      src/maps/tiles/2/3_0.png
  45. binární
      src/maps/tiles/2/3_1.png
  46. binární
      src/maps/tiles/2/3_2.png
  47. binární
      src/maps/tiles/2/3_3.png
  48. binární
      src/maps/tiles/3/0_0.png
  49. binární
      src/maps/tiles/3/0_1.png
  50. binární
      src/maps/tiles/3/0_2.png
  51. binární
      src/maps/tiles/3/0_3.png
  52. binární
      src/maps/tiles/3/0_4.png
  53. binární
      src/maps/tiles/3/0_5.png
  54. binární
      src/maps/tiles/3/0_6.png
  55. binární
      src/maps/tiles/3/0_7.png
  56. binární
      src/maps/tiles/3/1_0.png
  57. binární
      src/maps/tiles/3/1_1.png
  58. binární
      src/maps/tiles/3/1_2.png
  59. binární
      src/maps/tiles/3/1_3.png
  60. binární
      src/maps/tiles/3/1_4.png
  61. binární
      src/maps/tiles/3/1_5.png
  62. binární
      src/maps/tiles/3/1_6.png
  63. binární
      src/maps/tiles/3/1_7.png
  64. binární
      src/maps/tiles/3/2_0.png
  65. binární
      src/maps/tiles/3/2_1.png
  66. binární
      src/maps/tiles/3/2_2.png
  67. binární
      src/maps/tiles/3/2_3.png
  68. binární
      src/maps/tiles/3/2_4.png
  69. binární
      src/maps/tiles/3/2_5.png
  70. binární
      src/maps/tiles/3/2_6.png
  71. binární
      src/maps/tiles/3/2_7.png
  72. binární
      src/maps/tiles/3/3_0.png
  73. binární
      src/maps/tiles/3/3_1.png
  74. binární
      src/maps/tiles/3/3_2.png
  75. binární
      src/maps/tiles/3/3_3.png
  76. binární
      src/maps/tiles/3/3_4.png
  77. binární
      src/maps/tiles/3/3_5.png
  78. binární
      src/maps/tiles/3/3_6.png
  79. binární
      src/maps/tiles/3/3_7.png
  80. binární
      src/maps/tiles/3/4_0.png
  81. binární
      src/maps/tiles/3/4_1.png
  82. binární
      src/maps/tiles/3/4_2.png
  83. binární
      src/maps/tiles/3/4_3.png
  84. binární
      src/maps/tiles/3/4_4.png
  85. binární
      src/maps/tiles/3/4_5.png
  86. binární
      src/maps/tiles/3/4_6.png
  87. binární
      src/maps/tiles/3/4_7.png
  88. binární
      src/maps/tiles/3/5_0.png
  89. binární
      src/maps/tiles/3/5_1.png
  90. binární
      src/maps/tiles/3/5_2.png
  91. binární
      src/maps/tiles/3/5_3.png
  92. binární
      src/maps/tiles/3/5_4.png
  93. binární
      src/maps/tiles/3/5_5.png
  94. binární
      src/maps/tiles/3/5_6.png
  95. binární
      src/maps/tiles/3/5_7.png
  96. binární
      src/maps/tiles/3/6_0.png
  97. binární
      src/maps/tiles/3/6_1.png
  98. binární
      src/maps/tiles/3/6_2.png
  99. binární
      src/maps/tiles/3/6_3.png
  100. 0 0
      src/maps/tiles/3/6_4.png

+ 2 - 0
.gitignore

@@ -1,3 +1,5 @@
 node_modules/
 *.gguf
 .update_required
+packages/
+cache/

+ 13 - 5
README.md

@@ -43,9 +43,15 @@ But it has others features that are also really interesting, for example:
    ![SNH](https://solarnethub.com/git/snh-purple-theme.png "SolarNET.HuB")
    ![SNH](https://solarnethub.com/git/snh-matrix-theme.png "SolarNET.HuB")
    
- +  Even a complex Reddit-styled forum system.
+And some other nice modules, like for example:
+   
+ +  A complex Reddit-styled forum system.
  
    ![SNH](https://solarnethub.com/git/snh-forum.png "SolarNET.HuB")
+   
+ +  Or a collaborative -offline- maps system.
+ 
+   ![SNH](https://solarnethub.com/git/snh-maps.jpeg "SolarNET.HuB")
  
 And much more, that we invite you to discover by yourself ;-)
 
@@ -69,19 +75,21 @@ Oasis is TRULY MODULAR. Here's a list of what comes deployed with the "core".
  + Feed: Module to discover and share short-texts (feeds).
  + Forums: Module to discover and manage forums.	
  + Governance: Module to discover and manage votes.	
- + Images: Module to discover and manage images.	
- + Invites: Module to manage and apply invite codes.	
+ + Images: Module to discover and manage images.
+ + Invites: Module to manage and apply invite codes.
  + Jobs: Module to discover and manage jobs.	
  + Legacy: Module to manage your secret (private key) quickly and securely.	
  + Latest: Module to receive the most recent posts and discussions.
+ + Maps: Module to manage and share offline maps.
  + Market: Module to exchange goods or services.
- + Multiverse: Module to receive content from other federated peers.	
+ + Multiverse: Module to receive content from other federated peers.
  + Opinions: Module to discover and vote on opinions.	
  + Parliament: Module to elect governments and vote on laws.	
  + Pixelia: Module to draw on a collaborative grid.	
  + Projects: Module to explore, crowd-funding and manage projects.
- + Popular: Module to receive posts that are trending, most viewed, or most commented on.	
+ + Popular: Module to receive posts that are trending, most viewed, or most commented on.
  + Reports: Module to manage and track reports related to issues, bugs, abuses, and content warnings.	
+ + Shops: Module to manage and discover shops.	
  + Summaries: Module to receive summaries of long discussions or posts.	
  + Tags: Module to discover and explore taxonomy patterns (tags).	
  + Tasks: Module to discover and manage tasks.	

+ 202 - 180
docs/CHANGELOG.md

@@ -13,292 +13,314 @@ All notable changes to this project will be documented in this file.
 ### Security
 -->
 
+## v0.6.9 - 2026-04-04
+
+### Added
+
+- Maps module: create, manage and share offline maps with coordinates, markers and collaborative features (Maps plugin).
+- Shops module: create, manage and discover shops with products, favorites, opinions and purchases (Shops plugin).
+
+### Fixed
+
+- Searching blobs and items rendering (Search plugin).
+- UBI claim system: auto-execute epoch on Banking overview, prominent "Claim UBI!" button (Banking plugin).
+- UBI displayed in inhabitant profiles (Inhabitants plugin).
+- UBI filter added to transfers (Transfers plugin).
+- Favorites enrichment in Documents edit and single views (Documents plugin).
+- Bookmarks edit view favorite toggle (isFav → isFavorite) (Bookmarks plugin).
+- Favorites add/remove error handling for all media modules (Core plugin).
+- PUB wallet settings configuration (Settings plugin).
+
+### Changed
+
+- Tribes/Sub-Tribes content encryption (AES-256-GCM) (Tribes plugin).
+
 ## v0.6.8 - 2026-03-12
 
 ### Fixed
 
- + Government method image (Parliament plugin).
- + Trending text formatting + tombstoned content (Trending plugin).
- + Opinions text formatting + tombstoned content (Opinions plugin).
- + Forums text formatting (Forums plugin).
+- Government method image (Parliament plugin).
+- Trending text formatting- tombstoned content (Trending plugin).
+- Opinions text formatting- tombstoned content (Opinions plugin).
+- Forums text formatting (Forums plugin).
 
 ## v0.6.7 - 2026-03-04
 
 ### Added
 
- + Writing comments within feeds (Feed plugin).
- + PUB metadata and invitations (Invites plugin).
- + Chinese simplified (zh) translation (i18n).
- + Arab (ar) translation (i18n).
- + Hindi (hi) translation (i18n).
- + Russian (ru) translation (i18n).
+- Writing comments within feeds (Feed plugin).
+- PUB metadata and invitations (Invites plugin).
+- Chinese simplified (zh) translation (i18n).
+- Arab (ar) translation (i18n).
+- Hindi (hi) translation (i18n).
+- Russian (ru) translation (i18n).
 
 ### Fixed
 
- + SNH-Mobile Theme (Core plugin).
+- SNH-Mobile Theme (Core plugin).
  
 ### Changed
 
- + Multiple alignment of views between modules (Multiple plugins).
- + Cloud tags (Tags plugin).
- + Auto-join button for PUB: SNH "La Plaza" (Invites plugin).
- + i18n languages array expanded: ['en', 'es', 'fr', 'eu', 'de', 'it', 'pt', 'zh', 'ar', 'hi', 'ru'] (Core plugin).
+- Multiple alignment of views between modules (Multiple plugins).
+- Cloud tags (Tags plugin).
+- Auto-join button for PUB: SNH "La Plaza" (Invites plugin).
+- i18n languages array expanded: ['en', 'es', 'fr', 'eu', 'de', 'it', 'pt', 'zh', 'ar', 'hi', 'ru'] (Core plugin).
 
 ## v0.6.6 - 2026-02-23
 
 ### Added
 
- + SNH-Mobile Theme (Core plugin).
- + Added sub-tribes (Core plugin).
- + Max size control when uploading any file (Core plugin).
- + File uploading into every single commenting point (Core plugin).
- + Add an IP/Port for creating direct peering (Peers plugin).
- + Oasis new updates available notice banner (Core plugin).
- + Carbon footprint indicator in Statistics based on blobs/blockchain weight (Statistics plugin).
- + Canvas block visualization in Block Explorer showing last 50 blocks as colored bars (Blockexplorer plugin).
- + Inbox notification badge showing unread message count in topbar (Inbox plugin).
- + Feed published success confirmation message banner (Feed plugin).
- + Default SNH invite code loaded from snh-invite-code.json (Invites plugin).
- + Peer deduplication by host in Invites (Invites plugin).
- + Peer deduplication by key and table layout for peer listing (Peers plugin).
- + "Device source" field showing KIT/DESKTOP/MOBILE based on theme (Inhabitants/Profile plugin).
- + Module preset buttons for grouped configurations: Basic, Social, Economy, Full (Modules plugin).
- + Dominant opinion highlight next to Total Opinions (Trending/Opinions plugin).
- + German (de) translation (i18n).
- + Italian (it) translation (i18n).
- + Portuguese (pt) translation (i18n).
- + Shared state module for cross-module communication (Core plugin).
+- SNH-Mobile Theme (Core plugin).
+- Added sub-tribes (Core plugin).
+- Max size control when uploading any file (Core plugin).
+- File uploading into every single commenting point (Core plugin).
+- Add an IP/Port for creating direct peering (Peers plugin).
+- Oasis new updates available notice banner (Core plugin).
+- Carbon footprint indicator in Statistics based on blobs/blockchain weight (Statistics plugin).
+- Canvas block visualization in Block Explorer showing last 50 blocks as colored bars (Blockexplorer plugin).
+- Inbox notification badge showing unread message count in topbar (Inbox plugin).
+- Feed published success confirmation message banner (Feed plugin).
+- Default SNH invite code loaded from snh-invite-code.json (Invites plugin).
+- Peer deduplication by host in Invites (Invites plugin).
+- Peer deduplication by key and table layout for peer listing (Peers plugin).
+- "Device source" field showing KIT/DESKTOP/MOBILE based on theme (Inhabitants/Profile plugin).
+- Module preset buttons for grouped configurations: Basic, Social, Economy, Full (Modules plugin).
+- Dominant opinion highlight next to Total Opinions (Trending/Opinions plugin).
+- German (de) translation (i18n).
+- Italian (it) translation (i18n).
+- Portuguese (pt) translation (i18n).
+- Shared state module for cross-module communication (Core plugin).
 
 ### Fixed
 
- + MIME type error when uploading .mp4 (Videos plugin).
- + URL generation problems when containing "special characters" (Forum plugin).
- + Language selection between executing instances (Core plugin).
- + LAN broadcasting features (Core plugin).
- + Currently online peers discovering (Peers plugin).
- + Activity level shadowing (Inhabitants plugin).
- + Strip dangerous HTML tags from markdown output (Core plugin).
- + Plaintext injection vulnerability: comprehensive stripDangerousTags sanitization across all user text inputs in backend (Core Security).
- + Activity feed post truncation at 500 chars with "View details" link for long posts (Activity plugin).
- + Spread content now shows excerpt (300 chars) instead of just hashtag name (Activity plugin).
- + IN REPLY TO improved display with border-left styling, author name bold, and context preview (Activity plugin).
- + Activity level dot was empty, now displays colored dot (●) matching inhabitants view (Profile plugin).
- + Task description layout in Search: description now appears on new line below label (Search plugin).
- + Projects description label formatting with flex-direction column and word-break (Projects plugin).
- + Banking addresses from OASIS source now have delete action (Banking plugin).
- + Direct Connect form moved below networking action buttons for better UX (Peers plugin).
- + Peer validation and table layout for clearer peer listing (Peers plugin).
+- MIME type error when uploading .mp4 (Videos plugin).
+- URL generation problems when containing "special characters" (Forum plugin).
+- Language selection between executing instances (Core plugin).
+- LAN broadcasting features (Core plugin).
+- Currently online peers discovering (Peers plugin).
+- Activity level shadowing (Inhabitants plugin).
+- Strip dangerous HTML tags from markdown output (Core plugin).
+- Plaintext injection vulnerability: comprehensive stripDangerousTags sanitization across all user text inputs in backend (Core Security).
+- Activity feed post truncation at 500 chars with "View details" link for long posts (Activity plugin).
+- Spread content now shows excerpt (300 chars) instead of just hashtag name (Activity plugin).
+- IN REPLY TO improved display with border-left styling, author name bold, and context preview (Activity plugin).
+- Activity level dot was empty, now displays colored dot (●) matching inhabitants view (Profile plugin).
+- Task description layout in Search: description now appears on new line below label (Search plugin).
+- Projects description label formatting with flex-direction column and word-break (Projects plugin).
+- Banking addresses from OASIS source now have delete action (Banking plugin).
+- Direct Connect form moved below networking action buttons for better UX (Peers plugin).
+- Peer validation and table layout for clearer peer listing (Peers plugin).
  
 ### Changed
 
- + Tribes for adding a "fractal" of mods inside (Tribes plugin).
- + Removing metadata and added strong controls before uploading: PDF, video, audio, image... (Core plugin).
- + Packages.json (Core plugin).
- + i18n languages array expanded: ['en', 'es', 'fr', 'eu', 'de', 'it', 'pt'] (Core plugin).
- + Activity view now truncates long posts instead of expanding full content (Activity plugin).
- + Peers view uses table layout instead of nested lists (Peers plugin).
- + Block Explorer shows visual block canvas above the block list (Blockexplorer plugin).
+- Tribes for adding a "fractal" of mods inside (Tribes plugin).
+- Removing metadata and added strong controls before uploading: PDF, video, audio, image... (Core plugin).
+- Packages.json (Core plugin).
+- i18n languages array expanded: ['en', 'es', 'fr', 'eu', 'de', 'it', 'pt'] (Core plugin).
+- Activity view now truncates long posts instead of expanding full content (Activity plugin).
+- Peers view uses table layout instead of nested lists (Peers plugin).
+- Block Explorer shows visual block canvas above the block list (Blockexplorer plugin).
 
 ## v0.6.5 - 2026-01-16
 
 ### Added
 
- + Blockexplorer search engine (Blockexplorer plugin).
+- Blockexplorer search engine (Blockexplorer plugin).
 
 ### Fixed
 
- + Spreading threads + comments (Activity plugin).
- + Minor fixes (Activity plugin).
+- Spreading threads- comments (Activity plugin).
+- Minor fixes (Activity plugin).
 
 ## v0.6.4 - 2026-01-05
 
 ### Added
 
- + More information shared when spreading content at inhabitants activity (Activity plugin).
- + New PUB: pub.andromeda.oasis
+- More information shared when spreading content at inhabitants activity (Activity plugin).
+- New PUB: pub.andromeda.oasis
 
 ### Changed
 
- + Strong backend refactoring (4962L -> 2666L) (Core plugin).
+- Strong backend refactoring (4962L -> 2666L) (Core plugin).
 
 ### Fixed
 
- + Fixed parliament cycles (Parliament plugin).
- + Refeeding with hashtags (Feed plugin).
+- Fixed parliament cycles (Parliament plugin).
+- Refeeding with hashtags (Feed plugin).
 
 ## v0.6.3 - 2025-12-10
 
 ### Fixed
 
- + Fixed mentions (Core plugin).
- + Fixed feeds (Feed plugin).
- + Minor details at market view (Market plugin).
+- Fixed mentions (Core plugin).
+- Fixed feeds (Feed plugin).
+- Minor details at market view (Market plugin).
 
 ## v0.6.2 - 2025-12-05
 
 ### Added
 
- + Added a footer (Core plugin).
- + Added favorites to media related modules (Favorites plugin).
- + Added advanced search engine integration into modules (Search plugin).
+- Added a footer (Core plugin).
+- Added favorites to media related modules (Favorites plugin).
+- Added advanced search engine integration into modules (Search plugin).
  
 ### Changed
 
- * Added Oasis version at GUI (Core plugin).
- + Added templates for reporting standardization (Reports plugin).
- + Added market new functionalities (Market plugin).
- + Added bookmarks new functionalities (Bookmarks plugin).
- + Added jobs new filters (Jobs plugin).
+- Added Oasis version at GUI (Core plugin).
+- Added templates for reporting standardization (Reports plugin).
+- Added market new functionalities (Market plugin).
+- Added bookmarks new functionalities (Bookmarks plugin).
+- Added jobs new filters (Jobs plugin).
  
 ### Fixed
 
- + Security fixes (Core plugin).
- + Reports filters (Reports plugin).
- + Tasks minor changes (Tasks plugin).
- + Events minor changes (Events plugin).
- + Votations minor changes (Votations plugin).
- + Market minor changes (Market plugin).
- + Projects minor changes (Projects plugin).
- + Jobs minor changes (Jobs plugin).
- + Transfers minor changes (Transfers plugin).
+- Security fixes (Core plugin).
+- Reports filters (Reports plugin).
+- Tasks minor changes (Tasks plugin).
+- Events minor changes (Events plugin).
+- Votations minor changes (Votations plugin).
+- Market minor changes (Market plugin).
+- Projects minor changes (Projects plugin).
+- Jobs minor changes (Jobs plugin).
+- Transfers minor changes (Transfers plugin).
 
 ## v0.6.1 - 2025-12-01
 
 ### Changed
 
- + Added more notifications for tribes activity (Activity plugin).
- + Reordered filters (Opinions plugin).
+- Added more notifications for tribes activity (Activity plugin).
+- Reordered filters (Opinions plugin).
  
 ### Fixed
 
- + Feed minor changes (Feed plugin).
- + Tribes feed styles container (Tribes plugin).
+- Feed minor changes (Feed plugin).
+- Tribes feed styles container (Tribes plugin).
 
 ## v0.6.0 - 2025-11-29
 
 ### Changed
 
- + Added more opinion categories (Opinions plugin).
+- Added more opinion categories (Opinions plugin).
  
 ### Fixed
 
- + Tag counters (Tags plugin).
- + Duplicated content when searching (Search plugin).
- + Inhabitant-linked styles for Contact and PUB (Activity plugin).
- + Old posts retrieving at inhabitant profile (Core plugin).
- + Fixed threading comments (Core plugin).
+- Tag counters (Tags plugin).
+- Duplicated content when searching (Search plugin).
+- Inhabitant-linked styles for Contact and PUB (Activity plugin).
+- Old posts retrieving at inhabitant profile (Core plugin).
+- Fixed threading comments (Core plugin).
 
 ## v0.5.9 - 2025-11-28
 
 ### Added
 
- + Added fixed (also linked) threads into activity feed (Activity plugin).
+- Added fixed (also linked) threads into activity feed (Activity plugin).
  
 ### Fixed
 
- + Fixed laws stats (Parliament plugin).
+- Fixed laws stats (Parliament plugin).
 
 ## v0.5.8 - 2025-11-25
 
 ### Fixed
 
- + Fixed post preview from a pre-cached context (Core plugin).
- + Fixed tasks assignement to others different to the author (Core plugin).
- + Fixed comments context adding different to blog/post (Core plugin).
+- Fixed post preview from a pre-cached context (Core plugin).
+- Fixed tasks assignement to others different to the author (Core plugin).
+- Fixed comments context adding different to blog/post (Core plugin).
 
 ## v0.5.7 - 2025-11-24
 
 ### Added
 
- + Collapsible menu entries (Core plugin).
+- Collapsible menu entries (Core plugin).
 
 ### Fixed
 
- + Remote videos fail to load at Firefox/LibreWolf (Core plugin).
- + Fixed the comment query to return all posts whose root is the topic ID (Core plugin).
- + Fixed render-format for latest posts (Core plugin).
- + Fixed inhabitants listing for short-time activities (Activity plugin).
+- Remote videos fail to load at Firefox/LibreWolf (Core plugin).
+- Fixed the comment query to return all posts whose root is the topic ID (Core plugin).
+- Fixed render-format for latest posts (Core plugin).
+- Fixed inhabitants listing for short-time activities (Activity plugin).
 
 ## v0.5.6 - 2025-11-21
 
 ### Added
 
- + Extended post-commenting into various modules (bookmarks, images, audios, videos, documents, votations, events, tasks, reports, market, projects, jobs).
+- Extended post-commenting into various modules (bookmarks, images, audios, videos, documents, votations, events, tasks, reports, market, projects, jobs).
  
 ### Changed
 
- + Added details about current proposals at Courts (Courts plugin).
- + Parliament proposal listing when voting process has started (Parliament plugin).
+- Added details about current proposals at Courts (Courts plugin).
+- Parliament proposal listing when voting process has started (Parliament plugin).
  
 ### Fixed
 
- + Votations deduplication applied when directly voting from Parliament (Votes plugin).
+- Votations deduplication applied when directly voting from Parliament (Votes plugin).
  
 ## v0.5.5 - 2025-11-15
 
 ### Added
 
- + Conflicts resolution system (Courts plugin).
+- Conflicts resolution system (Courts plugin).
  
 ## v0.5.4 - 2025-10-30
 
 ### Fixed
 
- + Content stats (Stats plugin).
- + Non-avatar inhabitants listing (Inhabitants plugin).
- + Inhabitants suggestions (Inhabitants plugin).
- + Activity level (Inhabitants plugin).
- + Parliament duplication (Parliament plugin).
- + Added Parliament to blockexplorer (Blockexplorer plugin).
+- Content stats (Stats plugin).
+- Non-avatar inhabitants listing (Inhabitants plugin).
+- Inhabitants suggestions (Inhabitants plugin).
+- Activity level (Inhabitants plugin).
+- Parliament duplication (Parliament plugin).
+- Added Parliament to blockexplorer (Blockexplorer plugin).
 
 ## v0.5.3 - 2025-10-27
 
 ### Fixed
 
- + Tribes duplication (Tribes plugin + Activity plugin + Stats plugin).
+- Tribes duplication (Tribes plugin- Activity plugin- Stats plugin).
 
 ## v0.5.2 - 2025-10-22
 
 ### Added
 
- + Government system (Parliament plugin).
+- Government system (Parliament plugin).
  
 ### Fixed
 
- + Forum category translations (Forum plugin).
+- Forum category translations (Forum plugin).
 
 ## v0.5.1 - 2025-09-26
 
 ### Added
 
- + Activity level measurement (Inhabitants plugin).
- + Home page settings (Settings plugin).
+- Activity level measurement (Inhabitants plugin).
+- Home page settings (Settings plugin).
 
 ### Fixed
 
- + ECOIn wallet addresses (Banking plugin).
- + Tribes view (Tribes plugin).
- + Inhabitants view (Inhabitants plugin).
- + Avatar view (Main module).
- + Forum posts (Forums plugin).
- + Tribes info display (Search plugin).
+- ECOIn wallet addresses (Banking plugin).
+- Tribes view (Tribes plugin).
+- Inhabitants view (Inhabitants plugin).
+- Avatar view (Main module).
+- Forum posts (Forums plugin).
+- Tribes info display (Search plugin).
 
 ## v0.5.0 - 2025-09-20
 
 ### Added
 
- + Custom answer training (AI plugin).
+- Custom answer training (AI plugin).
 
 ### Fixed
 
- + Clean-SNH theme.
- + AI learning (AI plugin).
+- Clean-SNH theme.
+- AI learning (AI plugin).
 
 ## v0.4.9 - 2025-09-01
 
 ### Added
 
- + French translation.
+- French translation.
  
 ### Changed
 
@@ -308,8 +330,8 @@ All notable changes to this project will be documented in this file.
  
 ### Fixed
 
- + Fixed legacy codes (invites plugin).
- + Fixed SHS generator (script).
+- Fixed legacy codes (invites plugin).
+- Fixed SHS generator (script).
  
 ### Changed
 
@@ -323,13 +345,13 @@ All notable changes to this project will be documented in this file.
 
 ### Added
 
- + Online, discovered, unknown listing (peers plugin).
- + Federated, unfederated, unreachable networks (invites plugin).
+- Online, discovered, unknown listing (peers plugin).
+- Federated, unfederated, unreachable networks (invites plugin).
  
 ### Fixed
 
- + Fixed mentioning (mentions plugin).
- + Forum feed (activity plugin).
+- Fixed mentioning (mentions plugin).
+- Forum feed (activity plugin).
  
 ### Changed
 
@@ -343,8 +365,8 @@ All notable changes to this project will be documented in this file.
  
 ### Fixed
 
- + Follow/Unfollow and Pledges (projects plugin).
- + Karma SCORE (inhabitants plugin).
+- Follow/Unfollow and Pledges (projects plugin).
+- Karma SCORE (inhabitants plugin).
  
 ### Changed
 
@@ -356,14 +378,14 @@ All notable changes to this project will be documented in this file.
 
 ### Added
 
- + Exchange (ECOin current value) for all inhabitants (banking plugin).
- + Karma SCORE.
- + Upload a set of images/collections (images plugin).
+- Exchange (ECOin current value) for all inhabitants (banking plugin).
+- Karma SCORE.
+- Upload a set of images/collections (images plugin).
  
 ### Fixed
 
- + Add a new bounty (projects plugin).
- + Activity duplications.
+- Add a new bounty (projects plugin).
+- Activity duplications.
  
 ### Changed
 
@@ -376,8 +398,8 @@ All notable changes to this project will be documented in this file.
 
 ### Added
 
- + Projects: Module to explore, crowd-funding and manage projects.
- + Banking: Module to distribute a fair Universal Basic Income (UBI) using commons-treasury.
+- Projects: Module to explore, crowd-funding and manage projects.
+- Banking: Module to distribute a fair Universal Basic Income (UBI) using commons-treasury.
  
 ### Changed
 
@@ -393,14 +415,14 @@ All notable changes to this project will be documented in this file.
 
 - Limiter to blockchain logstream retrieval.
 
-  + Jobs: Module to discover and manage jobs.
-  + BlockExplorer: Module to navigate the blockchain.
+ - Jobs: Module to discover and manage jobs.
+ - BlockExplorer: Module to navigate the blockchain.
 
 ## v0.4.0 - 2025-07-29
 
 ### Added
 
-  + Forums: Module to discover and manage forums.
+ - Forums: Module to discover and manage forums.
 
 ## v0.3.8 - 2025-07-21
 
@@ -412,40 +434,40 @@ All notable changes to this project will be documented in this file.
 
 ### Changed
 
-- Hardcore "hacking" and refactoring for: models + backend + middleware + views.
+- Hardcore "hacking" and refactoring for: models- backend- middleware- views.
 
 ### Added
 
 - Some "core" modules:
 
-  + Agenda: Module to manage all your assigned items.
-  + Audios: Module to discover and manage audios.
-  + Bookmarks: Module to discover and manage bookmarks.
-  + Cipher: Module to encrypt and decrypt your text symmetrically (using a shared password).
-  + Documents: Module to discover and manage documents.
-  + Events: Module to discover and manage events.
-  + Feed: Module to discover and share short-texts (feeds).
-  + Governance: Module to discover and manage votes.
-  + Images: Module to discover and manage images.
-  + Invites: Module to manage and apply invite codes.
-  + Legacy: Module to manage your secret (private key) quickly and securely.
-  + Latest: Module to receive the most recent posts and discussions.
-  + Market: Module to exchange goods or services.
-  + Multiverse: Module to receive content from other federated peers.
-  + Opinions: Module to discover and vote on opinions.
-  + Pixelia: Module to draw on a collaborative grid.
-  + Popular: Module to receive posts that are trending, most viewed, or most commented on.
-  + Reports: Module to manage and track reports related to issues, bugs, abuses, and content warnings.
-  + Summaries: Module to receive summaries of long discussions or posts.
-  + Tags: Module to discover and explore taxonomy patterns (tags).
-  + Tasks: Module to discover and manage tasks.
-  + Threads: Module to receive conversations grouped by topic or question.
-  + Transfers: Module to discover and manage smart-contracts (transfers).
-  + Trending: Module to explore the most popular content.
-  + Tribes: Module to explore or create tribes (groups).
-  + Videos: Module to discover and manage videos.
-  + Wallet: Module to manage your digital assets (ECOin).
-  + Topics: Module to receive discussion categories based on shared interests.
+ - Agenda: Module to manage all your assigned items.
+ - Audios: Module to discover and manage audios.
+ - Bookmarks: Module to discover and manage bookmarks.
+ - Cipher: Module to encrypt and decrypt your text symmetrically (using a shared password).
+ - Documents: Module to discover and manage documents.
+ - Events: Module to discover and manage events.
+ - Feed: Module to discover and share short-texts (feeds).
+ - Governance: Module to discover and manage votes.
+ - Images: Module to discover and manage images.
+ - Invites: Module to manage and apply invite codes.
+ - Legacy: Module to manage your secret (private key) quickly and securely.
+ - Latest: Module to receive the most recent posts and discussions.
+ - Market: Module to exchange goods or services.
+ - Multiverse: Module to receive content from other federated peers.
+ - Opinions: Module to discover and vote on opinions.
+ - Pixelia: Module to draw on a collaborative grid.
+ - Popular: Module to receive posts that are trending, most viewed, or most commented on.
+ - Reports: Module to manage and track reports related to issues, bugs, abuses, and content warnings.
+ - Summaries: Module to receive summaries of long discussions or posts.
+ - Tags: Module to discover and explore taxonomy patterns (tags).
+ - Tasks: Module to discover and manage tasks.
+ - Threads: Module to receive conversations grouped by topic or question.
+ - Transfers: Module to discover and manage smart-contracts (transfers).
+ - Trending: Module to explore the most popular content.
+ - Tribes: Module to explore or create tribes (groups).
+ - Videos: Module to discover and manage videos.
+ - Wallet: Module to manage your digital assets (ECOin).
+ - Topics: Module to receive discussion categories based on shared interests.
 
 - New languages: Spanish, Euskara and French.
 

+ 180 - 0
scripts/build-deb.sh

@@ -0,0 +1,180 @@
+#!/bin/bash
+
+set -e
+
+VERSION="0.6.9"
+PKG_NAME="oasis"
+ARCH=$(dpkg --print-architecture)
+SRC_DIR="$(cd "$(dirname "$0")/.." && pwd)"
+BUILD_DIR="/tmp/oasis-deb-build"
+DEB_ROOT="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}"
+INSTALL_DIR="/opt/oasis"
+
+if [ "$1" = "--arm64" ]; then
+    ARCH="arm64"
+    DEB_ROOT="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}"
+fi
+
+echo "=== Building Oasis ${VERSION} .deb (${ARCH}) ==="
+
+rm -rf "${DEB_ROOT}"
+mkdir -p "${DEB_ROOT}/DEBIAN"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/server"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/backend"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/views"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/models"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/client"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/src/configs"
+mkdir -p "${DEB_ROOT}${INSTALL_DIR}/scripts"
+mkdir -p "${DEB_ROOT}/usr/bin"
+mkdir -p "${DEB_ROOT}/usr/share/applications"
+mkdir -p "${DEB_ROOT}/usr/share/doc/${PKG_NAME}"
+mkdir -p "${DEB_ROOT}/lib/systemd/system"
+
+echo "Copying application files..."
+
+cp -r "${SRC_DIR}/src/server/package.json" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
+cp -r "${SRC_DIR}/src/server/package-lock.json" "${DEB_ROOT}${INSTALL_DIR}/src/server/" 2>/dev/null || true
+cp "${SRC_DIR}/src/server/ssb_config.js" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
+cp "${SRC_DIR}/src/server/ssb_metadata.js" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
+cp "${SRC_DIR}/src/server/SSB_server.js" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
+
+if [ -d "${SRC_DIR}/src/server/packages" ]; then
+    cp -r "${SRC_DIR}/src/server/packages" "${DEB_ROOT}${INSTALL_DIR}/src/server/"
+    find "${DEB_ROOT}${INSTALL_DIR}/src/server/packages" -name "node_modules" -type d -exec rm -rf {} + 2>/dev/null || true
+fi
+
+cp "${SRC_DIR}/src/backend/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/backend/"
+cp -r "${SRC_DIR}/src/views/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/views/"
+cp -r "${SRC_DIR}/src/models/"*.js "${DEB_ROOT}${INSTALL_DIR}/src/models/"
+cp -r "${SRC_DIR}/src/client" "${DEB_ROOT}${INSTALL_DIR}/src/"
+find "${DEB_ROOT}${INSTALL_DIR}/src/client" -name "*.py" -delete 2>/dev/null
+find "${DEB_ROOT}${INSTALL_DIR}/src/client" -name ".ruff_cache" -type d -exec rm -rf {} + 2>/dev/null || true
+cp -r "${SRC_DIR}/src/configs/oasis-config.json" "${DEB_ROOT}${INSTALL_DIR}/src/configs/"
+cp -r "${SRC_DIR}/src/configs/shared-state.js" "${DEB_ROOT}${INSTALL_DIR}/src/configs/" 2>/dev/null || true
+cp -r "${SRC_DIR}/scripts" "${DEB_ROOT}${INSTALL_DIR}/"
+cp "${SRC_DIR}/oasis.sh" "${DEB_ROOT}${INSTALL_DIR}/"
+cp "${SRC_DIR}/LICENSE" "${DEB_ROOT}${INSTALL_DIR}/"
+
+cat > "${DEB_ROOT}/DEBIAN/control" << EOF
+Package: ${PKG_NAME}
+Version: ${VERSION}
+Architecture: ${ARCH}
+Maintainer: SolarNET.HuB <solarnethub@riseup.net>
+Depends: nodejs (>= 18)
+Recommends: npm
+Installed-Size: $(du -sk "${DEB_ROOT}${INSTALL_DIR}" | cut -f1)
+Section: net
+Priority: optional
+Homepage: https://solarnethub.com
+Description: Oasis P2P Social Network
+ Oasis is a P2P encrypted social network built on Secure Scuttlebutt (SSB).
+ Zero browser JavaScript — all rendering is server-side HTML+CSS.
+ Part of the SolarNET.HuB ecosystem.
+EOF
+
+cat > "${DEB_ROOT}/DEBIAN/postinst" << 'POSTINST'
+#!/bin/bash
+
+INSTALL_DIR="/opt/oasis"
+
+echo "Installing Node.js dependencies..."
+cd "${INSTALL_DIR}/src/server"
+npm install --production 2>&1 | tail -10
+node ../../scripts/patch-node-modules.js 2>/dev/null || true
+
+if ! id -u oasis >/dev/null 2>&1; then
+    useradd --system --home-dir /var/lib/oasis --create-home --shell /usr/sbin/nologin oasis
+fi
+
+mkdir -p /var/lib/oasis/.ssb
+chown -R oasis:oasis /var/lib/oasis
+chown -R oasis:oasis "${INSTALL_DIR}"
+
+systemctl daemon-reload 2>/dev/null || true
+echo ""
+echo "=== Oasis installed ==="
+echo "Start: systemctl start oasis"
+echo "Enable: systemctl enable oasis"
+echo "Open: http://localhost:3000"
+echo ""
+POSTINST
+chmod 755 "${DEB_ROOT}/DEBIAN/postinst"
+
+cat > "${DEB_ROOT}/DEBIAN/prerm" << 'PRERM'
+#!/bin/bash
+set -e
+systemctl stop oasis 2>/dev/null || true
+systemctl disable oasis 2>/dev/null || true
+PRERM
+chmod 755 "${DEB_ROOT}/DEBIAN/prerm"
+
+cat > "${DEB_ROOT}/DEBIAN/postrm" << 'POSTRM'
+#!/bin/bash
+set -e
+if [ "$1" = "purge" ]; then
+    rm -rf /opt/oasis
+    userdel oasis 2>/dev/null || true
+    rm -rf /var/lib/oasis
+fi
+systemctl daemon-reload 2>/dev/null || true
+POSTRM
+chmod 755 "${DEB_ROOT}/DEBIAN/postrm"
+
+cat > "${DEB_ROOT}/lib/systemd/system/oasis.service" << EOF
+[Unit]
+Description=Oasis P2P Social Network
+After=network.target
+
+[Service]
+Type=simple
+User=oasis
+Group=oasis
+WorkingDirectory=${INSTALL_DIR}
+ExecStart=/bin/sh ${INSTALL_DIR}/oasis.sh
+Restart=on-failure
+RestartSec=10
+Environment=HOME=/var/lib/oasis
+Environment=NODE_ENV=production
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+cat > "${DEB_ROOT}/usr/bin/oasis" << 'LAUNCHER'
+#!/bin/sh
+exec /bin/sh /opt/oasis/oasis.sh "$@"
+LAUNCHER
+chmod 755 "${DEB_ROOT}/usr/bin/oasis"
+
+cat > "${DEB_ROOT}/usr/share/applications/oasis.desktop" << EOF
+[Desktop Entry]
+Name=Oasis
+GenericName=P2P Social Network
+Exec=xdg-open http://localhost:3000
+Icon=applications-internet
+Terminal=false
+Type=Application
+Categories=Network;Chat;InstantMessaging;
+EOF
+
+cat > "${DEB_ROOT}/usr/share/doc/${PKG_NAME}/copyright" << EOF
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Oasis
+Source: https://code.03c8.net/KrakensLab/snh-oasis
+
+Files: *
+Copyright: 2022-2026 SolarNET.HuB / psy <epsylon@riseup.net>
+License: AGPL-3.0
+EOF
+
+echo "Building .deb package..."
+DEB_FILE="${BUILD_DIR}/${PKG_NAME}_${VERSION}_${ARCH}.deb"
+dpkg-deb --build "${DEB_ROOT}" "${DEB_FILE}"
+
+echo ""
+echo "=== Package built: ${DEB_FILE} ==="
+echo "Size: $(du -h "${DEB_FILE}" | cut -f1)"
+echo ""
+echo "Install: sudo dpkg -i ${DEB_FILE}"
+echo "         sudo apt-get install -f"

+ 1 - 3
scripts/patch-node-modules.js

@@ -7,8 +7,6 @@ const log = (msg) => console.log(`[OASIS] [PATCH] ${msg}`);
 const ssbRefPath = path.resolve(__dirname, '../src/server/node_modules/ssb-ref/index.js');
 if (fs.existsSync(ssbRefPath)) {
   const data = fs.readFileSync(ssbRefPath, 'utf8');
-
-  // Check if already in desired state (no deprecate wrapper on parseAddress)
   const alreadyClean = /exports\.parseAddress\s*=\s*parseAddress/.test(data);
   if (!alreadyClean) {
     const patched = data.replace(
@@ -34,7 +32,7 @@ if (fs.existsSync(ssbBlobsPath)) {
   const marker = 'want: function (id, cb)';
   const startIndex = data.indexOf(marker);
   if (startIndex !== -1) {
-    const endIndex = data.indexOf('},', startIndex); // end of function block
+    const endIndex = data.indexOf('},', startIndex);
     if (endIndex !== -1) {
       const before = data.slice(0, startIndex);
       const after = data.slice(endIndex + 2);

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 331 - 62
src/backend/backend.js


+ 2 - 0
src/backend/media-favorites.js

@@ -8,6 +8,8 @@ const DEFAULT = {
   bookmarks: [],
   documents: [],
   images: [],
+  maps: [],
+  shops: [],
   videos: []
 };
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 25 - 0
src/client/assets/images/world-map.svg


binární
src/client/assets/images/worldmap-z2.png


+ 619 - 3
src/client/assets/styles/style.css

@@ -870,7 +870,9 @@ article img {
 
 /* Filters */
 button.filter-btn,
+a.filter-btn,
 button.create-button {
+  font: inherit;
   padding: 10px 20px;
   margin-right: 10px;
   border-radius: 5px;
@@ -879,14 +881,19 @@ button.create-button {
   transition: background-color .2s, border-color .2s;
 }
 
-button.filter-btn {
+button.filter-btn,
+a.filter-btn {
   background-color: #ff6600;
   border-color:     #ff3300;
   color:            #fff;
+  text-decoration: none;
+  display: inline-block;
 }
 
 button.filter-btn:hover,
-button.filter-btn.active {
+button.filter-btn.active,
+a.filter-btn:hover,
+a.filter-btn.active {
   background-color: #ff9900;
   border-color:     #e68a00;
   color:            #000;
@@ -910,7 +917,7 @@ button.create-button:hover {
     width: 100%;
     padding: 6px;
     font-size: 14px;
-    border: 1px solid #ccc;
+    border: 1px solid #333;
     border-radius: 4px;
     box-sizing: border-box;
 }
@@ -3202,6 +3209,11 @@ width:100%; max-width:200px; max-height:300px; object-fit:cover; margin:16px 0;
   font-weight: bold;
 }
 
+.bank-claim-ubi{margin:16px 0}
+.bank-claim-card{border:1px solid #FFA500;border-radius:8px;padding:16px;margin:8px 0}
+.bank-claim-card p{margin:4px 0}
+.bank-claim-btn{font-size:16px;padding:10px 24px;margin-top:8px}
+
 .pub-item{border:1;border-radius:10px;background:none}
 .snh-invite-box{border:1px solid currentColor;border-radius:8px;padding:16px;margin:12px 0;font-family:monospace}
 .snh-invite-name{margin:0 0 8px 0}
@@ -3211,6 +3223,50 @@ width:100%; max-width:200px; max-height:300px; object-fit:cover; margin:16px 0;
 .error-title{margin:0 0 6px 0;font-weight:600}
 .error-pre{margin:0;white-space:pre-wrap;font-family:monospace}
 
+.shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;margin:8px 0}
+.shop-card{border:1px solid #333;border-radius:8px;overflow:hidden;display:flex;flex-direction:column}
+.shop-card-media{max-height:200px;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#111}
+.shop-card-media img,.shop-media{width:100%;max-height:200px;object-fit:cover}
+.shop-card-body{padding:12px;display:flex;flex-direction:column;gap:6px}
+.shop-title-link{color:#FFA500;text-decoration:none}
+.shop-card-desc{font-size:14px;opacity:0.85;margin:0}
+.shop-card-meta{display:flex;flex-wrap:wrap;gap:8px;font-size:13px}
+.shop-open{color:#0f0;font-weight:bold}
+.shop-closed{color:#f44;font-weight:bold}
+.shop-product-count{color:#FFA500}
+.shop-location{opacity:0.7}
+.shop-card-tags{display:flex;flex-wrap:wrap;gap:4px}
+.shop-card-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}
+.shop-detail{padding:8px 0}
+.shop-detail-media{max-width:100%;margin:12px 0}
+.shop-detail-media img{max-width:100%;border-radius:6px}
+.shop-detail-desc{margin:8px 0}
+.shop-share{margin:8px 0;display:flex;align-items:center;gap:8px}
+.shop-share-input{flex:1;background:#222;border:1px solid #555;color:#ccc;padding:6px 10px;border-radius:4px;font-family:monospace;font-size:13px}
+.shop-products-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;margin:12px 0}
+.shop-product-card{border:1px solid #333;border-radius:8px;overflow:hidden;display:flex;flex-direction:column}
+.shop-product-media{max-height:180px;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#111}
+.shop-product-media img{width:100%;max-height:180px;object-fit:cover}
+.shop-product-body{padding:10px;display:flex;flex-direction:column;gap:6px}
+.shop-product-price{font-size:18px;font-weight:bold;color:#FFA500}
+.shop-product-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}
+.shop-add-product{border:1px dashed #555;border-radius:8px;padding:16px;margin:12px 0}
+.shop-featured-products{display:flex;gap:8px;flex-wrap:wrap;margin:8px 0}
+.shop-featured-item{display:flex;flex-direction:column;align-items:center;width:72px;text-decoration:none;border:1px solid #333;border-radius:6px;overflow:hidden;background:#111}
+.shop-featured-item:hover{border-color:#FFA500}
+.shop-featured-thumb{width:72px;height:72px;object-fit:cover}
+.shop-featured-price{font-size:11px;color:#FFA500;padding:4px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%}
+.shop-product-stars{color:#FFA500;font-size:14px;letter-spacing:1px}
+.card-field-mb{margin-bottom:10px}
+.card-field-mt{margin-top:10px}
+.card-tags-mt{margin-top:10px}
+.card-label-bold{font-weight:800}
+.card-value-bold{margin-left:10px;font-weight:800}
+.contact-bold{font-weight:bold}
+.textarea-full{width:100%}
+.shop-form{padding:8px 0}
+.shop-form input[type="text"],.shop-form input[type="number"],.shop-form textarea,.shop-form select{width:100%;max-width:480px;padding:8px;margin:4px 0 8px 0;background:#222;border:1px solid #555;color:#ccc;border-radius:4px;font-family:monospace}
+
 /* carbon footprint chart */
 .carbon-section-title{font-size:18px;color:#555;margin:8px 0;font-weight:600}
 .carbon-chart{margin:12px 0;background:transparent !important;border:none !important}
@@ -3301,11 +3357,14 @@ width:100%; max-width:200px; max-height:300px; object-fit:cover; margin:16px 0;
   border-top: 1px solid rgba(255,255,255,0.08);
   line-height: 1;
   min-height: 0;
+  margin-top: 5px;
 }
 
 .oasis-footer-center {
+  margin-top: 15px;
   text-align: center;
   line-height: 1.6;
+  box-shadow: 0 2px 20px 0 #FFD60024;
 }
 
 .oasis-footer-logo-link {
@@ -4260,3 +4319,560 @@ width:100%; max-width:200px; max-height:300px; object-fit:cover; margin:16px 0;
   padding: 16px;
   margin-bottom: 16px;
 }
+
+.map-card {
+  background-color: #222;
+  border: 1px solid #444;
+  border-radius: 8px;
+  padding: 16px;
+  margin-bottom: 16px;
+}
+
+.map-card-info {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  flex-wrap: wrap;
+  margin-bottom: 8px;
+  border: 0px;
+}
+
+.map-type-badge {
+  display: inline-block;
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 0.75rem;
+  font-weight: 700;
+  letter-spacing: 0.5px;
+  background-color: #FFA500;
+  color: #000;
+}
+
+.map-coords {
+  font-family: monospace;
+  font-size: 0.85rem;
+  color: white;
+}
+
+.map-coords-detail {
+  font-family: monospace;
+  font-size: 0.9rem;
+  color: white;
+}
+
+.map-marker-count {
+  font-size: 0.8rem;
+  color: #888;
+}
+
+.map-description {
+  margin: 20px 0;
+}
+
+.map-url-container {
+  margin: 8px 0;
+}
+
+.map-url-link {
+  color: #FFA500;
+  word-break: break-all;
+}
+
+.map-detail-info {
+  display: flex;
+  gap: 16px;
+  align-items: center;
+  flex-wrap: wrap;
+  margin-bottom: 12px;
+}
+
+.map-wrap {
+  margin-right: 30px;
+  position: relative;
+  border: 0px;
+}
+
+.map-img {
+  display: block;
+  width: 100% !important;
+  height: 100% !important;
+  max-width: none !important;
+  border: none !important;
+}
+
+.map-bar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 4px 12px;
+  background-color: #1a1a1a;
+  border-top: 1px solid #333;
+}
+
+.map-bar-info {
+  font-size: 0.75rem;
+  color: #aaa;
+}
+
+.map-attr {
+  font-size: 0.6rem;
+  color: #555;
+}
+
+.map-coord-preview {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 8px 12px;
+  background-color: #222;
+  border-radius: 6px;
+  margin: 8px 0;
+  font-family: monospace;
+  font-size: 0.9rem;
+  color: #FFA500;
+}
+
+.map-coord-inline {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  font-family: monospace;
+  font-size: 0.9rem;
+  color: #FFA500;
+  margin: 4px 0;
+}
+
+.map-coord-pin {
+  font-size: 1.2rem;
+}
+
+.map-create-layout {
+  width: 100%;
+}
+
+.map-form {
+  margin-top: 12px;
+}
+
+.map-form label {
+  display: block;
+  font-weight: 600;
+  color: #FFA500;
+  margin-top: 8px;
+  margin-bottom: 4px;
+}
+
+.map-form input[type="text"],
+.map-form textarea,
+.map-form select {
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.map-form-row {
+  display: flex;
+  border: 0px;
+  gap: 12px;
+}
+
+.map-form-field {
+  flex: 1;
+}
+
+.map-card {
+  background-color: #222;
+  border: 1px solid #444;
+  border-radius: 8px;
+  padding: 12px 16px;
+  margin-bottom: 12px;
+  display: flex;
+  gap: 16px;
+  align-items: flex-start;
+}
+
+.map-card-thumb-link {
+  flex-shrink: 0;
+}
+
+.map-card-thumb {
+  width: 120px !important;
+  height: 120px !important;
+  max-width: none !important;
+  border-radius: 6px !important;
+  border: 1px solid #555 !important;
+  object-fit: cover;
+}
+
+.map-card-body {
+  flex: 1;
+  min-width: 0;
+}
+
+.map-card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 8px;
+  flex-wrap: wrap;
+  margin-bottom: 8px;
+}
+
+.map-card-actions {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+  align-items: center;
+}
+
+.map-click-hint {
+  color: #FFA500;
+  font-size: 0.85rem;
+  padding: 6px 0;
+}
+
+.map-detail {
+  background-color: #222;
+  border: 1px solid #444;
+  border-radius: 8px;
+  padding: 16px;
+}
+
+.map-detail-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: wrap;
+  margin-bottom: 12px;
+}
+
+.map-detail-actions {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+  align-items: center;
+}
+
+.map-marker-form {
+  margin-top: 20px;
+  padding-top: 16px;
+  border-top: 1px solid #444;
+}
+
+.map-markers-list {
+  margin-top: 16px;
+}
+
+.map-marker-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 10px;
+  padding: 10px 0;
+  border-bottom: 1px solid #333;
+}
+
+.map-marker-dot {
+  font-size: 1.2rem;
+  line-height: 1;
+}
+
+.map-marker-info {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  border: 0px;
+}
+
+.map-marker-label {
+  font-weight: 600;
+  color: #ffa300;
+}
+
+.map-marker-coords {
+  font-family: monospace;
+  font-size: 0.8rem;
+  color: white;
+}
+
+.map-marker-meta {
+  font-size: 0.75rem;
+}
+
+.map-empty {
+  padding: 24px;
+  text-align: center;
+  color: #666;
+  font-size: 0.9rem;
+}
+
+.map-form {
+  width: 100%;
+}
+
+.map-form label {
+  font-weight: 600;
+  color: #FFA500;
+}
+
+.map-form input[type="text"],
+.map-form textarea,
+.map-form select {
+  width: 100%;
+  max-width: 100%;
+  box-sizing: border-box;
+}
+
+.map-marker-form {
+  margin-top: 24px;
+  padding-top: 16px;
+  border-top: 1px solid #444;
+}
+
+.map-marker-form label {
+  font-weight: 600;
+  color: #FFA500;
+}
+
+.map-marker-form input[type="text"] {
+  width: 100%;
+  max-width: 100%;
+  box-sizing: border-box;
+}
+
+.map-form-map-slot {
+  margin: 12px 0;
+  border: none;
+  overflow: hidden;
+}
+
+.map-markers-list {
+  margin-top: 16px;
+  text-align: left;
+}
+
+.map-marker-item {
+  padding: 8px 0;
+  border-bottom: 1px solid #333;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: wrap;
+}
+
+.map-marker-dot {
+  color: #4a9eff;
+  font-size: 0.9rem;
+}
+
+.search-result-image {
+  max-width: 100%;
+  max-height: 300px;
+  border-radius: 4px;
+  display: block;
+  margin: 6px 0;
+}
+
+.map-embed-section {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  margin: 8px 0;
+}
+
+.map-location-inline {
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.map-location-icon {
+  color: #FFA500;
+}
+
+.map-location-link {
+  color: #FFA500;
+  font-size: 0.85rem;
+}
+
+.map-location-embed {
+  margin: 16px 0;
+}
+
+.maps-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.maps-search {
+  margin-bottom: 16px;
+}
+
+.inhabitant-karma-ubi {
+  margin-top: 8px;
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+.inhabitant-karma-ubi .karma-line,
+.inhabitant-karma-ubi .ubi-line {
+  display: block;
+  font-size: 0.9em;
+}
+
+.shop-featured-item,
+.shop-featured-item:hover,
+.shop-featured-item:visited,
+.shop-featured-item:active {
+  text-decoration: none;
+}
+
+.shop-detail {
+  border: 1px solid #333;
+  border-radius: 8px;
+  padding: 16px;
+}
+
+.map-form-map-slot {
+  margin: 12px 0 0 0;
+  border: none;
+  outline: none;
+  box-shadow: none;
+  overflow: hidden;
+}
+
+.map-marker-item {
+  border: none;
+  padding: 4px 0;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: nowrap;
+  text-align: left;
+}
+.map-marker-info {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: nowrap;
+  padding: 4px 0;
+  width: 100%;
+  overflow: hidden;
+  text-align: left;
+  justify-content: flex-start;
+}
+.map-markers-list h3 {
+  margin-bottom: 4px;
+}
+.map-form-full {
+  width: 100%;
+}
+.map-form-full form {
+  width: 100%;
+}
+.map-osm-embed {
+  width: 100%;
+  height: 450px;
+  border: 1px solid #444;
+  border-radius: 8px;
+  margin: 12px 0;
+}
+.map-viewer {
+  width: 100%;
+  overflow-x: auto;
+  overflow-y: hidden;
+  border: 0px;
+}
+.map-form-full, .map-form-full form {
+  box-sizing: border-box;
+  overflow: hidden;
+}
+.map-popup {
+  display: none;
+  position: fixed;
+  top: 0; left: 0; right: 0; bottom: 0;
+  background: rgba(0,0,0,0.65);
+  z-index: 1000;
+  align-items: center;
+  justify-content: center;
+}
+.map-popup:target {
+  display: flex;
+}
+.map-popup-box {
+  background: #222;
+  border: 1px solid #555;
+  border-radius: 10px;
+  padding: 20px 24px;
+  max-width: 360px;
+  width: 90%;
+  color: #ddd;
+  position: relative;
+}
+.map-popup-close {
+  position: absolute;
+  top: 10px; right: 14px;
+  color: #FFA500;
+  text-decoration: none;
+  font-size: 1.2rem;
+  line-height: 1;
+}
+.map-popup-label {
+  font-weight: 600;
+  font-size: 1rem;
+  color: #eee;
+  margin-bottom: 6px;
+}
+.map-popup-coords {
+  font-family: monospace;
+  font-size: 0.85rem;
+  color: white;
+}
+.map-popup-link {
+  color: #FFA500;
+  text-decoration: underline;
+  word-break: break-all;
+}
+.map-popup-img {
+  display: block;
+  max-width: 180px;
+  max-height: 120px;
+  object-fit: cover;
+  border-radius: 4px;
+  margin: 6px auto;
+}
+.map-marker-img {
+  display: block;
+  max-width: 120px;
+  max-height: 80px;
+  object-fit: cover;
+  border-radius: 4px;
+  margin: 4px 0;
+}
+.map-zoom-info {
+  display: block;
+  font-size: 0.8rem;
+  color: #888;
+  margin-bottom: 4px;
+}
+.map-embed-url {
+  margin-top: 6px;
+}
+.map-embed-url .map-location-link {
+  font-size: 0.8rem;
+}
+.map-zone {
+  display: block;
+}
+.map-popup-container { height: 0; overflow: hidden; border: 0px; }
+.project-maploc {
+  margin: 6px 0;
+}
+.project-maploc .map-location-link {
+  font-size: 0.85rem;
+  word-break: break-all;
+}
+.shop-product-actions:empty {
+  display: none;
+}

+ 153 - 16
src/client/assets/translations/oasis_ar.js

@@ -373,6 +373,17 @@ module.exports = {
     bookmarkLabel: "العلامات المرجعية",
     tribeLabel: "القبائل",
     marketLabel: "السوق",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "المحافظ",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "السير الذاتية",
     submit: "إرسال",
     subjectLabel: "الموضوع",
@@ -428,7 +439,7 @@ module.exports = {
     walletTitle: "المحفظة",
     walletTotalCostLine: ({ totalCost }) => `التكلفة الإجمالية: ECO ${totalCost}`,
     walletTransactionId: "معرّف المعاملة",
-    walletTxId: "معرّف المعاملة",
+    walletTxId: "معرف المعاملة",
     walletType: "النوع",
     walletUser: "اسم المستخدم",
     walletPass: "كلمة المرور",
@@ -627,6 +638,7 @@ module.exports = {
     favoritesFilterBookmarks: "العلامات المرجعية",
     favoritesFilterDocuments: "المستندات",
     favoritesFilterImages: "الصور",
+    favoritesFilterMaps: "خرائط",
     favoritesFilterVideos: "الفيديوهات",
     favoritesRemoveButton: "إزالة من المفضلة",
     favoritesNoItems: "لا توجد مفضلات بعد.",
@@ -654,7 +666,7 @@ module.exports = {
     viewAvatar: "عرض الصورة الرمزية",
     viewCV: "عرض السيرة الذاتية",
     suggestedSectionTitle: "المقترحون",
-    topkarmaSectionTitle: "أعلى كارما",
+    topkarmaSectionTitle: "أفضل كارما",
     topactivitySectionTitle: "الأكثر نشاطًا",
     blockedSectionTitle: "المحظورون",
     gallerySectionTitle: "المعرض",
@@ -666,6 +678,7 @@ module.exports = {
     oasisId: "المعرّف",
     noInhabitantsFound: "لم يتم العثور على سكان بعد.",
     inhabitantActivityLevel: "مستوى النشاط",
+    deviceLabel: "الجهاز",
     parliamentTitle: "البرلمان",
     parliamentDescription: "استكشف أشكال الحكم وقوانين الإدارة الجماعية.",
     parliamentFilterGovernment: "الحكومة",
@@ -725,7 +738,7 @@ module.exports = {
     parliamentThDate: "تاريخ الاقتراح",
     parliamentThSlogan: "الشعار",
     parliamentThMethod: "الطريقة",
-    parliamentThKarma: "الكارما",
+    parliamentThKarma: "كارما",
     parliamentThSince: "تاريخ الملف الشخصي",
     parliamentThVotes: "الأصوات المُستلَمة",
     parliamentThVoteAction: "تصويت",
@@ -1105,6 +1118,7 @@ module.exports = {
     transfersTo: "إلى",
     transfersFilterAll: "الكل",
     transfersFilterMine: "خاصتي",
+    transfersFilterUBI: "UBI",
     transfersFilterMarket: "السوق",
     transfersFilterTop: "الأفضل",
     transfersFilterPending: "معلّق",
@@ -1114,7 +1128,7 @@ module.exports = {
     transfersCreateButton: "إنشاء تحويل",
     transfersUpdateButton: "تحديث",
     transfersDeleteButton: "حذف",
-    transfersToUser: "معرّف Oasis",
+    transfersToUser: "معرف Oasis",
     transfersToUserValidation: "معرّف Oasis صالح، مثال: @…=.ed25519",
     transfersConcept: "المفهوم",
     transfersAmount: "المبلغ",
@@ -1129,6 +1143,7 @@ module.exports = {
     transfersConfirmButton: "تأكيد التحويل",
     transfersNoItems: "لم يتم العثور على تحويلات.",
     transfersMineSectionTitle: "تحويلاتك",
+    transfersUBISectionTitle: "تحويلات UBI",
     transfersMarketSectionTitle: "تحويلات السوق",
     transfersTopSectionTitle: "أفضل التحويلات",
     transfersPendingSectionTitle: "التحويلات المعلّقة",
@@ -1306,14 +1321,14 @@ module.exports = {
     imageTopSectionTitle: "أفضل الصور",
     imageFavoritesSectionTitle: "المفضلة",
     imageGallerySectionTitle: "المعرض",
-    imageMemeSectionTitle: "الميمات",
+    imageMemeSectionTitle: "ميمز",
     imageFilterAll: "الكل",
     imageFilterMine: "خاصتي",
     imageFilterRecent: "الأخيرة",
     imageFilterTop: "الأفضل",
     imageFilterFavorites: "المفضلة",
     imageFilterGallery: "المعرض",
-    imageFilterMeme: "الميمات",
+    imageFilterMeme: "ميمز",
     imageCreateButton: "رفع صورة",
     imageUpdateButton: "تحديث",
     imageDeleteButton: "حذف",
@@ -1395,7 +1410,7 @@ module.exports = {
     typeEvent:            "الأحداث",
     typeTransfer:         "تحويل",
     typeTask:             "المهام",
-    typePixelia:          "بكسيليا",
+    typePixelia:          "بيكسيليا",
     typeForum:            "المنتدى",
     typeReport:           "التقارير",
     typeFeed:             "التغذية",
@@ -1405,7 +1420,7 @@ module.exports = {
     typeBanking:          "المصرفية",
     typeBankWallet:       "المصرفية/المحفظة",
     typeBankClaim:        "المصرفية/الدخل الأساسي",
-    typeKarmaScore:       "الكارما",
+    typeKarmaScore:       "كارما",
     typeParliament:       "البرلمان",
     typeSpread:           "الانتشارات",
     typeParliamentCandidature: "البرلمان · ترشيح",
@@ -2071,6 +2086,8 @@ module.exports = {
     bankBack: 'العودة إلى المصرفية',
     bankViewTx: 'عرض المعاملة',
     bankClaimNow: 'طالب الآن',
+    bankClaimUBI: 'طالب بـ UBI!',
+    bankNoPendingUBI: 'لا توجد تخصيصات UBI معلقة لهذه الحقبة.',
     bankPubBalance: 'رصيد PUB',
     bankEpoch: 'الحقبة',
     bankPool: 'المجمع (هذه الحقبة)',
@@ -2119,7 +2136,71 @@ module.exports = {
     bankMyAddress: 'عنوانك',
     bankRemoveMyAddress: 'إزالة عنواني',
     bankNotRemovableOasis: 'لا يمكن إزالة العناوين محليًا',
-    bankingFutureUBI: "تخصيص الدخل الأساسي المقدّر",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "متاجر",
+    shopDescription: "اكتشف وأدر المتاجر في الشبكة.",
+    shopTitle: "متجر",
+    shopFilterAll: "الكل",
+    shopFilterMine: "متاجري",
+    shopFilterRecent: "الأحدث",
+    shopFilterTop: "الأفضل",
+    shopFilterProducts: "المنتجات",
+    shopFilterPrices: "الأسعار",
+    shopFilterFavorites: "المفضلة",
+    shopUpload: "إنشاء متجر",
+    shopAllSectionTitle: "جميع المتاجر",
+    shopMineSectionTitle: "متاجري",
+    shopRecentSectionTitle: "متاجر حديثة",
+    shopTopSectionTitle: "أفضل المتاجر",
+    shopProductsSectionTitle: "أفضل المنتجات",
+    shopPricesSectionTitle: "المنتجات حسب السعر",
+    shopFavoritesSectionTitle: "المفضلة",
+    shopCreateSectionTitle: "إنشاء متجر",
+    shopUpdateSectionTitle: "تحديث متجر",
+    shopCreate: "إنشاء",
+    shopUpdate: "تحديث",
+    shopDelete: "حذف",
+    shopAddFavorite: "إضافة مفضلة",
+    shopRemoveFavorite: "إزالة مفضلة",
+    shopOpen: "مفتوح",
+    shopClosed: "مغلق",
+    shopOpenShop: "فتح المتجر",
+    shopCloseShop: "إغلاق المتجر",
+    shopProducts: "المنتجات",
+    shopProductAdd: "إضافة منتج",
+    shopProductTitle: "منتج",
+    shopProductUpdate: "تحديث منتج",
+    shopProductPrice: "السعر (ECO)",
+    shopProductStock: "المخزون",
+    shopProductUntitled: "منتج بلا عنوان",
+    shopUntitled: "متجر بلا عنوان",
+    shopNoItems: "لم يتم العثور على متاجر.",
+    shopNoProducts: "لا توجد منتجات بعد.",
+    shopOutOfStock: "نفد المخزون",
+    shopBuy: "شراء",
+    shopBackToShop: "العودة إلى المتجر",
+    shopShareUrl: "رابط المشاركة",
+    shopVisitShop: "زيارة المتجر",
+    shopStatus: "الحالة",
+    shopCreatedAt: "تاريخ الإنشاء",
+    shopSearchPlaceholder: "البحث عن متاجر...",
+    shopUrl: "الرابط",
+    shopLocation: "الموقع",
+    shopTags: "الوسوم",
+    shopVisibility: "الرؤية",
+    shopImage: "صورة",
+    shopShortDescription: "وصف قصير",
+    shopShortDescriptionPlaceholder: "وصف مختصر لبطاقات المتجر (160 حرف كحد أقصى)",
+    shopTitlePlaceholder: "اسم متجرك",
+    shopDescriptionPlaceholder: "وصف تفصيلي لمتجرك",
+    shopUrlPlaceholder: "https://رابط-متجرك.com",
+    shopLocationPlaceholder: "المدينة، البلد",
+    shopTagsPlaceholder: "وسم1، وسم2، وسم3",
+    mapTitlePlaceholder: "عنوان الخريطة",
+    shopProductFeatured: "منتج مميز",
+    shopSendToMarket: "Send to Market",
+    typeShop: "متجر",
+    typeShopProduct: "منتج متجر",
     bankExchange: 'الصرف',
     bankExchangeCurrentValue: 'قيمة ECOin (1 ساعة)',
     bankTotalSupply: 'إجمالي معروض ECOin',
@@ -2181,6 +2262,9 @@ module.exports = {
     statsAudio: "الملفات الصوتية",
     statsVideo: "الفيديوهات",
     statsDocument: "المستندات",
+    statsMap: "الخرائط",
+    statsShop: "المتاجر",
+    statsShopProduct: "منتجات المتجر",
     statsTransfer: "التحويلات",
     statsAiExchange: "الذكاء الاصطناعي",
     statsPUBs: 'PUBs',
@@ -2304,6 +2388,7 @@ module.exports = {
     marketItemDescription: "الوصف",
     marketItemDescriptionPlaceholder: "صف العنصر الذي تبيعه",
     marketItemStatus: "الحالة",
+    marketShopLabel: "متجر",
     marketItemCondition: "الحالة",
     marketItemPrice: "السعر",
     marketItemTags: "الوسوم",
@@ -2584,6 +2669,8 @@ module.exports = {
     modulesTasksDescription: "وحدة لاكتشاف وإدارة المهام.",
     modulesMarketLabel: "السوق",
     modulesMarketDescription: "وحدة لتبادل السلع أو الخدمات.",
+    modulesShopsLabel: "متاجر",
+    modulesShopsDescription: "وحدة لإدارة واكتشاف المتاجر.",
     modulesTribesLabel: "القبائل",
     modulesTribesDescription: "وحدة لاستكشاف أو إنشاء قبائل (مجموعات).",
     modulesVotationsLabel: "التصويتات",
@@ -2639,16 +2726,66 @@ module.exports = {
     feedSuccessMsg: "تم نشر التغذية بنجاح!",
     dominantOpinionLabel: "الرأي السائد",
     uploadMedia: "رفع وسائط (الحد الأقصى: 50 ميغابايت)",
-    feedViewDetails: "عرض التفاصيل",
     feedOpenDiscussion: "فتح النقاش",
     feedDetailTitle: "التغذية",
     feedPostComment: "نشر تعليق",
-    eventFileLabel: "إرفاق صورة",
-    taskFileLabel: "إرفاق صورة",
-    courtsRespondentRequired: "معرّف المدعى عليه مطلوب",
     courtsRespondentInvalid: "يجب أن يكون معرّف SSB صالحًا (@...ed25519)",
-    noComments: "لا توجد تعليقات بعد"
-
-     //END
+    noComments: "لا توجد تعليقات بعد",
+    mapsLabel: "خرائط",
+    mapTitle: "خرائط",
+    mapDescription: "استكشف وأدر الخرائط غير المتصلة في شبكتك.",
+    mapMineSectionTitle: "خرائطك",
+    mapCreateSectionTitle: "إنشاء خريطة",
+    mapUpdateSectionTitle: "تحديث الخريطة",
+    mapAllSectionTitle: "خرائط",
+    mapRecentSectionTitle: "خرائط حديثة",
+    mapFavoritesSectionTitle: "المفضلة",
+    mapFilterAll: "الكل",
+    mapFilterMine: "خرائطي",
+    mapFilterRecent: "حديثة",
+    mapFilterFavorites: "المفضلة",
+    mapUploadButton: "إنشاء خريطة",
+    mapCreateButton: "إنشاء خريطة",
+    mapUpdateButton: "تحديث",
+    mapDeleteButton: "حذف",
+    mapAddFavoriteButton: "إضافة للمفضلة",
+    mapRemoveFavoriteButton: "إزالة من المفضلة",
+    mapLatLabel: "خط العرض",
+    mapLatPlaceholder: "مثال 24.7136",
+    mapLngLabel: "خط الطول",
+    mapLngPlaceholder: "مثال 46.6753",
+    mapDescriptionLabel: "الوصف",
+    mapDescriptionPlaceholder: "صف الخريطة أو الموقع...",
+    mapTypeLabel: "نوع الخريطة",
+    mapTypeSingle: "فردي (الموقع الأولي فقط)",
+    mapTypeOpen: "مفتوح (يمكن لأي شخص إضافة علامات)",
+    mapTypeClosed: "مغلق (المنشئ فقط يضيف علامات)",
+    mapTagsLabel: "الوسوم",
+    mapTagsPlaceholder: "أدخل الوسوم مفصولة بفواصل",
+    mapUrlLabel: "رابط الخريطة",
+    mapPickCoordLabel: "أو اختر موقعاً على الشبكة:",
+    mapMarkersLabel: "علامات",
+    mapMarkersTitle: "العلامات",
+    mapMarkerDefault: "علامة",
+    mapMarkerLatLabel: "خط عرض العلامة",
+    mapMarkerLngLabel: "خط طول العلامة",
+    mapMarkerLabelField: "تسمية العلامة",
+    mapMarkerLabelPlaceholder: "صف هذه العلامة...",
+    markerImageLabel: "صورة العلامة",
+    mapAddMarkerTitle: "إضافة علامة",
+    mapAddMarkerButton: "إضافة علامة",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "تطبيق التكبير",
+    mapSearchPlaceholder: "البحث في الوصف، الوسوم، المؤلف...",
+    mapSearchButton: "بحث",
+    mapUpdatedAt: "محدّث",
+    mapNoMatch: "لا توجد خرائط مطابقة لبحثك.",
+    noMaps: "لا توجد خرائط متاحة.",
+    mapLocationTitle: "الموقع",
+    mapVisitLabel: "زيارة الخريطة",
+    typeMap: "خرائط",
+    typeMapMarker: "علامة خريطة",
+    modulesMapLabel: "خرائط",
+    modulesMapDescription: "وحدة لإدارة ومشاركة الخرائط غير المتصلة."
     }
 };

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1916 - 1831
src/client/assets/translations/oasis_de.js


+ 156 - 53
src/client/assets/translations/oasis_en.js

@@ -74,7 +74,6 @@ module.exports = {
     continueReading: "Continue reading",
     moreComments: "more comment",
     readThread: "read the rest of the thread",    
-    // pixelia
     pixeliaTitle: 'Pixelia',
     pixeliaDescription: 'Draw pixels on the grid and collaborARTe with others in your network.',
     coordLabel: 'Coordinate (e.g., A3)',
@@ -86,7 +85,6 @@ module.exports = {
     invalidCoordinate: 'Incorrect coordinate',
     goToMuralButton: "View Mural",
     totalPixels: 'Total Pixels',
-    // modules
     modules: "Modules",
     modulesViewTitle: "Modules",
     modulesViewDescription: "Set your environment by enabling or disabling modules.",
@@ -119,7 +117,6 @@ module.exports = {
     opinionsTitle: "Opinions",
     saveSettings: "Save configuration",
     apply: "Apply",
-    // menu categories
     menuPersonal: "Personal",
     menuContent: "Content",
     menuGovernance: "Governance",
@@ -130,13 +127,11 @@ module.exports = {
     menuEconomy: "Economy",
     menuMedia: "Media",
     menuTools: "Tools",
-    // post actions
     comment: "Comment",
     subtopic: "Subtopic",
     json: "JSON",
     createdAt: "Created At",
     createdBy: "by",
-    // relationships
     unfollow: "Unsupport",
     follow: "Support",
     block: "Block",
@@ -159,11 +154,9 @@ module.exports = {
     relationshipNone: "You are not supporting",
     relationshipConflict: "Conflict",
     relationshipBlockingPost: "Blocked post",
-    // spreads view
     viewLikes: "View spreads",
     spreadedDescription: "List of posts spread by the inhabitant.",
     totalspreads: "Total spreads",
-    // composer
     attachFiles: "Attach files",
     preview: "Preview",
     publish: "Write",
@@ -198,12 +191,10 @@ module.exports = {
     ],
     publishCustom: "Write advanced post",
     subtopicLabel: "Create a subtopic of this post",
-    //mentions
     messagePreview: "Post Preview",
     mentionsMatching: "Matching Mentions",
     mentionsName: "Name",
     mentionsRelationship: "Relationship",
-    //settings
     updateit: "GET UPDATES!",
     updateBannerText: "A new version of Oasis is available.",
     updateBannerAction: "Update now →",
@@ -273,7 +264,6 @@ module.exports = {
     homePageTitle: "Home",
     homePageDescription: "Select which module you want as your home page.",
     saveHomePage: "Set home",
-    //invites
     invites: "Invites",
     invitesTitle: "Invites",
     invitesInvites: "Invitations",
@@ -296,9 +286,7 @@ module.exports = {
     currentlyUnreachable: "ERROR!",
     errorDetails: "Error Details",
     genericError: "An error occurred.",
-    //panic  
     panicMode: "Panic Mode!",
-    //cipher
     encryptData: "Set password (min 32 characters long) to encrypt your blockchain",
     decryptData: "Enter password to decrypt your blockchain",
     panicModeDescription: "Encrypt/Decrypt or DELETE your blockchain",
@@ -306,7 +294,6 @@ module.exports = {
     encryptPanicButton: "Encrypt blockchain",  
     decryptPanicButton: "Decrypt blockchain",      
     removePanicButton: "DELETE ALL YOUR DATA!",    
-    //search
     searchTitle: "Search",
     searchDescriptionLabel: "Search for content in your network.",
     searchLanguagesLabel: "Languages",
@@ -354,9 +341,7 @@ module.exports = {
     votesCreatedByLabel:"Created by",
     voteStatusOpen:"OPEN",
     voteStatusClosed:"CLOSED",
-    //image search page
     imageSearchLabel: "Enter words to search for images labelled with them.",
-    //posts and comments
     commentDescription: ({ parentUrl }) => [
       " commented on ",
       a({ href: parentUrl }, " thread"),
@@ -368,7 +353,6 @@ module.exports = {
     ],
     subtopicTitle: ({ authorName }) => [`Subtopic on @${authorName}'s post`],
     mysteryDescription: "posted a mysterious post",
-    // misc + search
     oasisDescription: "OASIS Project Network",
     searchSubmit: "Let's Search...",
     hashtagDescription: "Posts tagged with this hashtag.",
@@ -389,6 +373,17 @@ module.exports = {
     bookmarkLabel: "BOOKMARKS",
     tribeLabel: "TRIBES",
     marketLabel: "MARKET",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "WALLETS",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "CVs",
     submit: "Submit",
     subjectLabel: "Subject",
@@ -449,7 +444,6 @@ module.exports = {
     walletUser: "Username",
     walletPass: "Password",
     walletConfiguration: "Set wallet",
-    //cipher
     cipher: "Crypter",
     cipherTitle: "Crypter",
     cipherDescription: "Encrypt and decrypt your text symmetrically (using a shared password).",
@@ -459,6 +453,7 @@ module.exports = {
     cipherTextLabel: "Text to Encrypt",
     cipherTextPlaceholder: "Enter text to encrypt...",
     cipherPasswordLabel: "Set password (min 32 characters long) to encrypt your text",
+    cipherPasswordDecryptLabel: "Set password (min 32 characters long) to decrypt your text",
     cipherPasswordPlaceholder: "Enter a password...",
     cipherEncryptButton: "Encrypt",
     cipherDecryptTitle: "Decrypt Text",
@@ -484,7 +479,6 @@ module.exports = {
     missingFieldsError: "Text, password or IV not provided.",
     encryptionError: "Error encrypting text.",
     decryptionError: "Error decrypting text.",
-    //bookmarking
     bookmarkTitle: "Bookmarks",
     bookmarkDescription: "Discover and manage bookmarks in your network.",
     bookmarkAllSectionTitle: "Bookmarks",
@@ -524,7 +518,6 @@ module.exports = {
     noUrl: "No link",
     noCategory: "No category",
     noLastVisit: "No last visit",
-    //videos
     videoTitle: "Videos",
     videoDescription: "Explore and manage video content in your network.",
     videoPluginTitle: "Title",
@@ -563,7 +556,6 @@ module.exports = {
     videoMessageAuthorButton: "PM",
     videoUpdatedAt: "Updated",
     videoNoMatch: "No videos match your search.",
-    //documents
     documentTitle: "Documents",
     documentDescription: "Discover and manage documents in your network.",
     documentAllSectionTitle: "Documents",
@@ -600,7 +592,6 @@ module.exports = {
     documentSearchButton: "Search",
     documentNoMatch: "No documents match your search.",
     documentUpdatedAt: "Updated",
-    //audios
     audioTitle: "Audios",
     audioDescription: "Explore and manage audio content in your network.",
     audioPluginTitle: "Title",
@@ -639,7 +630,6 @@ module.exports = {
     audioNoMatch: "No audios match your search.",
     audioFavoritesSectionTitle: "Favorites",
     audioFilterFavorites: "FAVORITES",
-    //favorites
     favoritesTitle: "Favorites",
     favoritesDescription: "All your favorited media in one place.",
     favoritesFilterAll: "ALL",
@@ -648,10 +638,10 @@ module.exports = {
     favoritesFilterBookmarks: "BOOKMARKS",
     favoritesFilterDocuments: "DOCUMENTS",
     favoritesFilterImages: "IMAGES",
+    favoritesFilterMaps: "MAPS",
     favoritesFilterVideos: "VIDEOS",
     favoritesRemoveButton: "Remove from favorites",
     favoritesNoItems: "No favorites yet.",
-    //inhabitants
     yourContacts: "Your Contacts",
     allInhabitants: "Inhabitants",
     allCVs: "All CVs",
@@ -688,7 +678,7 @@ module.exports = {
     oasisId: "ID",
     noInhabitantsFound: "No inhabitants found, yet.",
     inhabitantActivityLevel: "Activity Level",
-    //parliament
+    deviceLabel: "Device",
     parliamentTitle: "Parliament",
     parliamentDescription: "Explore forms of government and collective management laws.",
     parliamentFilterGovernment: "GOVERMENT",
@@ -830,7 +820,6 @@ module.exports = {
     parliamentProposalVoteStatusLabel: "Vote status",
     parliamentProposalOnTrackYes: "Threshold reached",
     parliamentProposalOnTrackNo: "Below threshold",
-    //courts
     courtsTitle: "Courts",
     courtsDescription: "Explore forms of conflict resolution and collective justice management.",
     courtsFilterCases: "CASES",
@@ -881,6 +870,7 @@ module.exports = {
     courtsNoNominations: "No nominations yet.",
     courtsAccuser: "Accuser",
     courtsRespondent: "Respondent",
+    courtsRespondentInvalid: "Must be a valid SSB ID (@...ed25519)",
     courtsThStatus: "Status",
     courtsThAnswerBy: "Answer by",
     courtsThEvidenceBy: "Evidence by",
@@ -945,7 +935,6 @@ module.exports = {
     courtsRoleDictator: "Dictator",
     courtsAssignJudgeTitle: "Choose judge",
     courtsAssignJudgeBtn: "Choose judge",
-    //trending
     trendingTitle: "Trending",
     exploreTrending: "Explore the most popular content in your network.",
     ALLButton: "ALL",
@@ -991,7 +980,6 @@ module.exports = {
     trendingAuthor: "By",
     trendingCreatedAtLabel: "Created At",
     trendingTotalCount: "Total Count",
-    //tasks
     tasksTitle: "Tasks",
     tasksDescription: "Discover and manage tasks in your network.",
     taskTitleLabel: "Title",
@@ -1047,7 +1035,6 @@ module.exports = {
     notasks: "No tasks available.",
     noLocation: "No location specified",
     taskSetStatus: "Set Status",
-    //events
     eventTitle: "Events",
     eventDateLabel: "Date",
     eventsTitle: "Events",
@@ -1115,7 +1102,6 @@ module.exports = {
     eventUnattended: "Unattended",
     eventStatusOpen: "Open",
     eventStatusClosed: "Closed",
-    //tags 
     tagsTitle: "Tags",
     tagsDescription: "Discover and explore taxonomy patterns in your network.",
     tagsAllSectionTitle: "Tags",
@@ -1127,13 +1113,13 @@ module.exports = {
     tagsNoItems: "No tags available.",
     tagsTableHeaderTag: "TAG/LINK:",
     tagsTableHeaderCount: "COUNTER:",
-    //transfers
     transfersTitle: "Transfers",
     transfersDescription: "Discover and manage transfers in your network.",
     transfersFrom: "From",
     transfersTo: "To",
     transfersFilterAll: "ALL",
     transfersFilterMine: "MINE",
+    transfersFilterUBI: "UBI",
     transfersFilterMarket: "MARKET",
     transfersFilterTop: "TOP",
     transfersFilterPending: "PENDING",
@@ -1158,6 +1144,7 @@ module.exports = {
     transfersConfirmButton: "Confirm Transfer",
     transfersNoItems: "No transfers found.",
     transfersMineSectionTitle: "Your Transfers",
+    transfersUBISectionTitle: "UBI Transfers",
     transfersMarketSectionTitle: "Market Transfers",
     transfersTopSectionTitle: "Top Transfers",
     transfersPendingSectionTitle: "Pending Transfers",
@@ -1184,7 +1171,6 @@ module.exports = {
     transfersExpiredBadge: "EXPIRED",
     transfersUpdatedAt: "Updated",
     transfersNoMatch: "No transfers match your search.",
-    //votations (voting/polls)
     votationsTitle: "Votations",
     votationsDescription: "Discover and manage votations in your network.",
     voteMineSectionTitle: "Your Votations",
@@ -1241,7 +1227,6 @@ module.exports = {
     voteNewCommentPlaceholder: 'Write your comment here…',
     voteNewCommentButton: 'Post comment',
     voteNewCommentLabel: 'Add a comment',
-    //CV
     cvTitle: "CV",
     cvLabel: "Curriculum Vitae (CV)",
     cvEditSectionTitle: "Edit CV",
@@ -1280,13 +1265,11 @@ module.exports = {
     cvEditButton: "Update",
     cvDeleteButton: "Delete",
     cvNoCV: "No CV found.",
-    // blog/post,
     blogSubject: "Subject",
     blogMessage: "Message",
     blogImage: "Upload media (max-size: 50MB)",
     blogPublish: "Preview",
     noPopularMessages: "No popular messages published, yet",
-    //forum
     forumTitle: "Forums",
     forumCategoryLabel: "Category",
     forumTitleLabel: "Title",
@@ -1327,7 +1310,6 @@ module.exports = {
     forumCatPRIVACY: "Privacy",
     forumCatCYBERWARFARE: "Cyberwarfare",
     forumCatSURVIVALISM: "Survivalism",
-    //images
     imageTitle: "Images",
     imageDescription: "Explore and manage image content in your network.",
     imagePluginTitle: "Title",
@@ -1371,8 +1353,11 @@ module.exports = {
     imageMessageAuthorButton: "Message",
     imageUpdatedAt: "Updated",
     imageNoMatch: "No images match your search.",
-    //feed
     feedTitle:        "Feed",
+    feedDetailTitle:  "Feed",
+    feedOpenDiscussion: "Open discussion",
+    feedPostComment:  "Post comment",
+    noComments:       "No comments yet",
     createFeedTitle:  "Create Feed",
     createFeedButton: "Send Feed!",
     feedPlaceholder:  "What's happening? (max 280 characters)",
@@ -1388,10 +1373,8 @@ module.exports = {
     author:           "By",
     createdAtLabel:   "Created at",
     FeedshareYourOpinions: "Discover and share short-texts in your network.",
-    //feed
     refeedButton:     "Refeed",
     alreadyRefeeded: "You already refeeded this.",
-    //activity
     activityTitle:        "Activity",
     yourActivity:         "Your Activity",
     globalActivity:       "Global Activity",
@@ -1566,7 +1549,6 @@ module.exports = {
     courtsPublicPrefSubmit: 'Save visibility preference',
     courtsMethodMEDIATION: 'Mediation',
     courtsNoCases: 'No cases.',
-    //reports
     reportsTitle: "Reports",
     reportsDescription: "Manage and track reports related to issues, bugs, abuses and content warnings in your network.",
     reportsFilterAll: "ALL",
@@ -1952,7 +1934,6 @@ module.exports = {
     agendaTransferConcept: "Concept",
     agendaTransferAmount: "Amount",
     agendaTransferDeadline: "Deadline",  
-    //opinions
     opinionsTitle:        "Opinions",
     shareYourOpinions:    "Discover and vote for opinions in your network.",
     author:               "By",
@@ -2040,7 +2021,6 @@ module.exports = {
     voteCreative: "Creative",
     voteSpam: "Spam",
     voteAdultOnly: "Adult Only",
-    //inbox
     publishBlog: "Publish Blog",
     privateMessage: "PM",
     pmSendTitle: "Private Messages",
@@ -2080,7 +2060,6 @@ module.exports = {
     inboxProjectPledgedTitle: "New pledge to your project",
     pmHasPledged: "has pledged",
     pmToYourProject: "to your project",
-    //blockexplorer
     blockchain: 'BlockExplorer',
     blockchainTitle: 'BlockExplorer',
     blockchainDescription: 'Explore and visualize the blocks in the blockchain.',
@@ -2101,7 +2080,6 @@ module.exports = {
     blockchainBack: 'Back to Blockexplorer',
     blockchainContentDeleted: "This content has been tombstoned",
     visitContent: "Visit Content",
-    //banking
     banking: 'Banking',
     bankingTitle: 'Banking',
     bankingDescription: 'Explore the current value of ECOin and the corresponding UBI allocation, distributed per epoch based on participation and trust.',
@@ -2113,6 +2091,8 @@ module.exports = {
     bankBack: 'Back to Banking',
     bankViewTx: 'View Tx',
     bankClaimNow: 'Claim now',
+    bankClaimUBI: 'Claim UBI!',
+    bankNoPendingUBI: 'No pending UBI allocations for this epoch.',
     bankPubBalance: 'PUB Balance',
     bankEpoch: 'Epoch',
     bankPool: 'Pool (this epoch)',
@@ -2161,7 +2141,71 @@ module.exports = {
     bankMyAddress: 'Your address',
     bankRemoveMyAddress: 'Remove my address',
     bankNotRemovableOasis: 'Addresses cannot be removed locally',
-    bankingFutureUBI: "Estimated UBI Allocation",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "Shops",
+    shopDescription: "Discover and manage shops in the network.",
+    shopTitle: "Shop",
+    shopFilterAll: "ALL",
+    shopFilterMine: "MINE",
+    shopFilterRecent: "RECENT",
+    shopFilterTop: "TOP",
+    shopFilterProducts: "PRODUCTS",
+    shopFilterPrices: "PRICES",
+    shopFilterFavorites: "FAVORITES",
+    shopUpload: "Create Shop",
+    shopAllSectionTitle: "Shops",
+    shopMineSectionTitle: "Your Shops",
+    shopRecentSectionTitle: "Recent Shops",
+    shopTopSectionTitle: "Top Shops",
+    shopProductsSectionTitle: "Top Products",
+    shopPricesSectionTitle: "Products by Price",
+    shopFavoritesSectionTitle: "Favorites",
+    shopCreateSectionTitle: "Create Shop",
+    shopUpdateSectionTitle: "Update Shop",
+    shopCreate: "Create",
+    shopUpdate: "Update",
+    shopDelete: "Delete",
+    shopAddFavorite: "Add Favorite",
+    shopRemoveFavorite: "Remove Favorite",
+    shopOpen: "OPEN",
+    shopClosed: "CLOSED",
+    shopOpenShop: "Open Shop",
+    shopCloseShop: "Close Shop",
+    shopProducts: "Products",
+    shopProductAdd: "Add Product",
+    shopProductTitle: "Product",
+    shopProductUpdate: "Update Product",
+    shopProductPrice: "Price (ECO)",
+    shopProductStock: "Stock",
+    shopProductUntitled: "Untitled Product",
+    shopUntitled: "Untitled Shop",
+    shopNoItems: "No shops found.",
+    shopNoProducts: "No products yet.",
+    shopOutOfStock: "Out of stock",
+    shopBuy: "Buy",
+    shopBackToShop: "Back to Shop",
+    shopShareUrl: "Share URL",
+    shopVisitShop: "VISIT SHOP",
+    shopStatus: "STATUS",
+    shopCreatedAt: "CREATED",
+    shopSearchPlaceholder: "Search shops...",
+    shopUrl: "URL",
+    shopLocation: "Location",
+    shopTags: "Tags",
+    shopVisibility: "Visibility",
+    shopImage: "Image",
+    shopShortDescription: "Short Description",
+    shopShortDescriptionPlaceholder: "Brief description of your shop",
+    shopTitlePlaceholder: "Name of your shop",
+    shopDescriptionPlaceholder: "Detailed description of your shop",
+    shopUrlPlaceholder: "https://your-shop-url.com",
+    shopLocationPlaceholder: "City, Country",
+    shopTagsPlaceholder: "tag1, tag2, tag3",
+    mapTitlePlaceholder: "Map title",
+    shopProductFeatured: "Featured Product",
+    shopSendToMarket: "Send to Market",
+    typeShop: "SHOP",
+    typeShopProduct: "SHOP PRODUCT",
     bankExchange: 'Exchange',
     bankExchangeCurrentValue: 'ECOin Value (1h)',
     bankTotalSupply: 'ECOin Total Supply',
@@ -2174,7 +2218,6 @@ module.exports = {
     bankingSyncStatus: 'ECOin Status',
     bankingSyncStatusSynced: 'Synced',
     bankingSyncStatusOutdated: 'Outdated',
-    //stats
     statsTitle: 'Statistics',
     statistics: "Statistics",
     statsInhabitant: "Inhabitant Stats",
@@ -2224,6 +2267,9 @@ module.exports = {
     statsAudio: "Audios",
     statsVideo: "Videos",
     statsDocument: "Documents",
+    statsMap: "Maps",
+    statsShop: "Shops",
+    statsShopProduct: "Shop products",
     statsTransfer: "Transfers",
     statsAiExchange: "AI",
     statsPUBs: 'PUBs',
@@ -2290,7 +2336,6 @@ module.exports = {
     statsCourtsSettlementAccepted: "Settlements accepted",
     statsCourtsNomination: "Judge nominations",
     statsCourtsNominationVote: "Nomination votes",
-    //AI
     ai: "AI",
     aiTitle: "AI",
     aiDescription: "A Collective Artificial Intelligence (CAI) called '42' that learns from your network.",
@@ -2322,7 +2367,6 @@ module.exports = {
     aiApproveCustomTrain: "Train using this custom answer",
     aiCustomAnswerPlaceholder: "Write your custom answer…",
     statsAIExchanges: "Model Exchanges",
-    //market
     marketMineSectionTitle: "Your Items",
     marketCreateSectionTitle: "Create Item",
     marketUpdateSectionTitle: "Update",
@@ -2349,6 +2393,7 @@ module.exports = {
     marketItemDescription: "Description",
     marketItemDescriptionPlaceholder: "Describe the item you're selling",
     marketItemStatus: "Status",
+    marketShopLabel: "Shop",
     marketItemCondition: "Condition",
     marketItemPrice: "Price",
     marketItemTags: "Tags",
@@ -2385,7 +2430,6 @@ module.exports = {
     marketAuctionEnded: "Ended",
     marketMyBidBadge: "You bid",
     marketNoItemsMatch: "No items match your search.",
-    //jobs
     jobsTitle: "Jobs",
     jobsDescription: "Discover and manage jobs in your network.",
     jobsFilterRecent: "RECENT",
@@ -2476,7 +2520,6 @@ module.exports = {
     jobsTagsLabel: "Tags",
     jobsTagsPlaceholder: "tag1, tag2, tag3",
     noJobsMatch: "No jobs match your search.",
-    //projects
     projectsTitle: "Projects",
     projectsDescription: "Create, fund, and follow community-driven projects in your network.",
     projectCreateProject: "Create Project",
@@ -2582,11 +2625,9 @@ module.exports = {
     projectPledgeAmount: "Amount",
     projectSelectMilestoneOrBounty: "Select Milestone or Bounty",
     projectPledgeButton: "Pledge",
-    //footer
     footerLicense: "GPLv3",
     footerPackage: "Package",
     footerVersion: "Version",
-    //modules
     modulesModuleName: "Name",
     modulesModuleDescription: "Description",
     modulesModuleStatus: "Status",
@@ -2633,6 +2674,8 @@ module.exports = {
     modulesTasksDescription: "Module to discover and manage tasks.",
     modulesMarketLabel: "Market",
     modulesMarketDescription: "Module to exchange goods or services.",
+    modulesShopsLabel: "Shops",
+    modulesShopsDescription: "Module to manage and discover shops.",
     modulesTribesLabel: "Tribes",
     modulesTribesDescription: "Module to explore or create tribes (groups).",
     modulesVotationsLabel: "Votations",
@@ -2687,8 +2730,68 @@ module.exports = {
     statsCarbonTombstone: "Tombstoning footprint",
     feedSuccessMsg: "Feed published successfully!",
     dominantOpinionLabel: "Dominant opinion",
-    uploadMedia: "Upload media (max-size: 50MB)"
+    uploadMedia: "Upload media (max-size: 50MB)",
+    mapsLabel: "Maps",
+    mapTitle: "Maps",
+    mapDescription: "Explore and manage offline maps in your network.",
+    mapMineSectionTitle: "Your Maps",
+    mapCreateSectionTitle: "Create Map",
+    mapUpdateSectionTitle: "Update Map",
+    mapAllSectionTitle: "Maps",
+    mapRecentSectionTitle: "Recent Maps",
+    mapFavoritesSectionTitle: "Favorites",
+    mapFilterAll: "ALL",
+    mapFilterMine: "MINE",
+    mapFilterRecent: "RECENT",
+    mapFilterFavorites: "FAVORITES",
+    mapUploadButton: "Upload Map",
+    mapCreateButton: "Create Map",
+    mapUpdateButton: "Update",
+    mapDeleteButton: "Delete",
+    mapAddFavoriteButton: "Add favorite",
+    mapRemoveFavoriteButton: "Remove favorite",
+    mapLatLabel: "Latitude",
+    mapLatPlaceholder: "e.g. 40.4168",
+    mapLngLabel: "Longitude",
+    mapLngPlaceholder: "e.g. -3.7038",
+    mapDescriptionLabel: "Description",
+    mapDescriptionPlaceholder: "Describe the map or location...",
+    mapTypeLabel: "Map type",
+    mapTypeSingle: "SINGLE (only initial location)",
+    mapTypeOpen: "OPEN (anyone can add markers)",
+    mapTypeClosed: "CLOSED (only creator adds markers)",
+    mapTagsLabel: "Tags",
+    mapTagsPlaceholder: "Enter tags separated by commas",
+    mapUrlLabel: "Map URL",
+    mapPickCoordLabel: "Or select a location on the grid:",
+    mapMarkersLabel: "markers",
+    mapMarkersTitle: "Markers",
+    mapMarkerDefault: "Marker",
+    mapMarkerLatLabel: "Marker latitude",
+    mapMarkerLngLabel: "Marker longitude",
+    mapMarkerLabelField: "Marker label",
+    mapMarkerLabelPlaceholder: "Describe this marker...",
+    markerImageLabel: "Marker Image",
+    mapAddMarkerTitle: "Add Marker",
+    mapAddMarkerButton: "Add Marker",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "Apply Zoom",
+    mapSearchPlaceholder: "Search description, tags, author...",
+    mapSearchButton: "Search",
+    mapZoomIn: "Zoom +",
+    mapZoomOut: "Zoom −",
+    mapClickToCreate: "Click on the map to select a location",
+    mapClickToAddMarker: "Click on the map to add a marker",
+    mapUpdatedAt: "Updated",
+    mapNoMatch: "No maps match your search.",
+    noMaps: "No maps available.",
+    mapLocationTitle: "Map Location",
+    mapVisitLabel: "Visit map",
+    mapUrlPlaceholder: "/maps/MAP_ID",
+    typeMap: "MAPS",
+    typeMapMarker: "MAP MARKER",
+    modulesMapLabel: "Maps",
+    modulesMapDescription: "Module to manage and share offline maps."
 
-     //END
     }
 };

+ 200 - 93
src/client/assets/translations/oasis_es.js

@@ -67,7 +67,6 @@ module.exports = {
     continueReading: "Continuar leyendo",
     moreComments: "más comentarios",
     readThread: "leer el resto del hilo",    
-    // pixelia
     pixeliaTitle: 'Pixelia',
     pixeliaDescription: 'Dibuja pixeles en un grid y colabor-ART con habitantes en tu red.',
     coordLabel: 'Coordenadas (e.g., A3)',
@@ -79,7 +78,6 @@ module.exports = {
     invalidCoordinate: 'Coordenada incorrecta',
     goToMuralButton: "Ver Mural",
     totalPixels: 'Total Pixeles',
-    // modules
     modules: "Módulos",
     modulesViewTitle: "Módulos",
     modulesViewDescription: "Define tu entorno ideal activando y desactivando módulos.",
@@ -112,7 +110,6 @@ module.exports = {
     opinionsTitle: "Opiniones",
     saveSettings: "Guardar configuración",
     apply: "Aplicar",
-    // menu categories
     menuPersonal: "Personal",
     menuContent: "Contenido",
     menuGovernance: "Gobernanza",
@@ -123,13 +120,11 @@ module.exports = {
     menuEconomy: "Economía",
     menuMedia: "Multimedia",
     menuTools: "Herramientas",
-    // post actions
     comment: "Comentar",
-    subtopic: "Subtopic",
+    subtopic: "Subtema",
     json: "JSON",
     createdAt: "Creado el",
     createdBy: "por",
-    // relationships
     unfollow: "Dejar de soportar",
     follow: "Soportar",
     block: "Bloquear",
@@ -152,11 +147,9 @@ module.exports = {
     relationshipNone: "No estás dando soporte",
     relationshipConflict: "Conflicto",
     relationshipBlockingPost: "Post bloqueado",
-    // spreads view
     viewLikes: "Ver replicas",
     spreadedDescription: "Lista de posts replicados por habitante.",
     totalspreads: "Replicas totales",
-    // composer
     attachFiles: "Adjuntar ficheros",
     preview: "Previsualizar",
     publish: "Publicar",
@@ -191,12 +184,10 @@ module.exports = {
     ],
     publishCustom: "Escribir un post avanzado",
     subtopicLabel: "Crea un subtopic para éste post",
-    // mentions
     messagePreview: "Previsualización",
     mentionsMatching: "Menciones",
     mentionsName: "Nombre",
     mentionsRelationship: "Relación",
-    // settings
     updateit: "OBTENER ACTUALIZACIONES!",
     updateBannerText: "Hay una nueva versión de Oasis disponible.",
     updateBannerAction: "Actualizar ahora →",
@@ -204,7 +195,7 @@ module.exports = {
     settingsIntro: ({ version }) => [
       `[SNH] ꖒ OASIS [ v.${version} ]`,
     ],
-    timeAgo: "",
+    timeAgo: "hace",
     sendTime: "escrito ",
     theme: "Tema",
     legacy: "Llaves",
@@ -217,7 +208,7 @@ module.exports = {
     saveSettings: "Guardar configuración",
     exportTitle: "Exportar datos",
     exportDescription: "Establece una contraseña (min 32 caracteres de longitud) para cifrar tu secreto",
-    exportDataTitle: "Backup",
+    exportDataTitle: "Copia de Seguridad",
     exportDataDescription: "Descargar tu blockchain (llave privada excluida!)",
     exportDataButton: "Descargar blockchain",
     pubWallet: "Cartera del PUB",
@@ -241,8 +232,8 @@ module.exports = {
     status: "Estado",
     peerConnections: "Nodos",
     peerConnectionsIntro: "Maneja todas tus conexiones con otros nodos.",
-    online: "Online",
-    offline: "Offline",
+    online: "Conectado",
+    offline: "Desconectado",
     discovered: 'Descubiertos',
     unknown: 'Desconocidos',
     pub: 'PUB',
@@ -266,7 +257,6 @@ module.exports = {
     homePageTitle: "Página de inicio",
     homePageDescription: "Selecciona qué módulo quieres como tu página de inicio.",
     saveHomePage: "Establecer página de inicio",
-    //invites
     invites: "Invitaciones",
     invitesTitle: "Invitaciones",
     invitesInvites: "Invitaciones",
@@ -281,17 +271,15 @@ module.exports = {
     invitesNoInvites: "No hay invitaciones aceptadas, aún.",
     invitesUnfollow: 'Dejar de seguir',
     invitesFollow: 'Seguir',
-    invitesUnfollowedInvites: 'Redes No Federadas',
+    invitesUnfollowedInvites: 'Invitaciones no seguidas',
     invitesNoFederatedPubs: "No hay redes federadas.",
-    invitesNoUnfollowed: 'No hay redes no federadas.',
+    invitesNoUnfollowed: 'Sin invitaciones no seguidas.',
     invitesUnreachablePubs: "Redes Inalcanzables",
     invitesNoUnreachablePubs: "No hay redes inalcanzables.",
-    currentlyUnreachable: "ERROR!",
+    currentlyUnreachable: "¡ERROR!",
     errorDetails: "Detalles del error",
     genericError: "A ocurrido un error.",
-    //panic  
     panicMode: "Modo Pánico!",
-    //cipher
     encryptData: "Establece una contraseña (min 32 caracteres de longitud) para cifrar tu blockchain",
     decryptData: "Introduce contraseña para descifrar tu blockchain",
     panicModeDescription: "Cifrar/Descifrar o BORRAR tu blockchain",
@@ -299,7 +287,6 @@ module.exports = {
     encryptPanicButton: "Cifrar blockchain",  
     decryptPanicButton: "Descifrar blockchain",      
     removePanicButton: "BORRAR TODOS TUS DATOS!",    
-    //search
     searchTitle: "Buscar",
     searchDescriptionLabel: "Buscar contenido en tu red.",
     searchLanguagesLabel: "Lenguajes",
@@ -346,9 +333,7 @@ module.exports = {
     votesCreatedByLabel:"Creado el",
     voteStatusOpen:"ABIERTO",
     voteStatusClosed:"CERRADO",
-    //image search page
     imageSearchLabel: "Introduce palabras para buscar las imagenes que hayan sido etiquetadas con ellas.",
-    //posts and comments
     commentDescription: ({ parentUrl }) => [
       " ha comentado en ",
       a({ href: parentUrl }, " hilo"),
@@ -360,7 +345,6 @@ module.exports = {
     ],
     subtopicTitle: ({ authorName }) => [`Ha creado un subtopic en el post de @${authorName}`],
     mysteryDescription: "publicado un post misterioso",
-    // misc + search
     oasisDescription: "OASIS Red de Proyectos",
     searchSubmit: "Buscar...",
     hashtagDescription: "Posts etiquetados con éste hashtag.",
@@ -377,10 +361,21 @@ module.exports = {
     eventLabel: "EVENTOS",
     taskLabel: "TAREAS",
     transferLabel: "TRANSFERENCIAS",
-    curriculumLabel: "CURRICULUM",
+    curriculumLabel: "CURRÍCULUM",
     bookmarkLabel: "ENLACES",
     tribeLabel: "TRIBUS",
     marketLabel: "MERCADO",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "CARTERAS",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "CVs",
     submit: "Aplicar",
     subjectLabel: "Asunto",
@@ -399,12 +394,12 @@ module.exports = {
     walletAddressLine: ({ address }) => `Dirección: ${address}`,
     walletAmountLine: ({ amount }) => `Cantidad: ${amount} ECO`,
     walletBack: "Volver",
-    walletBalanceTitle: "Balance",
+    walletBalanceTitle: "Saldo",
     walletWalletSendTitle: "Enviar",
     walletReceiveTitle: "Recibir",
     walletHistoryTitle: "Historial",
     walletBalanceLine: ({ balance }) => `${balance} ECO`,
-    walletCnfrs: "Cnfrs",
+    walletCnfrs: "Conf.",
     walletConfirm: "Confirmar",
     walletDescription: "Administra tus activos digitales, incluido el envío y la recepción de ECOin, la visualización del saldo y el acceso al historial de transacciones.",
     walletDate: "Fecha",
@@ -436,12 +431,11 @@ module.exports = {
     walletTitle: "Cartera",
     walletTotalCostLine: ({ totalCost }) => `Coste total: ECO ${totalCost}`,
     walletTransactionId: "ID de transacción",
-    walletTxId: "Tx ID",
+    walletTxId: "ID Tx",
     walletType: "Tipo",
     walletUser: "Usuario",
     walletPass: "Contraseña",
     walletConfiguration: "Configurar cartera",
-    //cipher
     cipher: "Cifrador",
     cipherTitle: "Cifrador",
     cipherDescription: "Encripta y desencripta tu texto de forma simétrica (usando una contraseña compartida).",
@@ -451,6 +445,7 @@ module.exports = {
     cipherTextLabel: "Texto para Encriptar",
     cipherTextPlaceholder: "Introduce el texto para encriptar...",
     cipherPasswordLabel: "Establece una contraseña (mínimo 32 caracteres) para encriptar tu texto",
+    cipherPasswordDecryptLabel: "Establece una contraseña (mínimo 32 caracteres) para desencriptar tu texto",
     cipherPasswordPlaceholder: "Introduce una contraseña...",
     cipherEncryptButton: "Encriptar",
     cipherDecryptTitle: "Desencriptar Texto",
@@ -476,7 +471,6 @@ module.exports = {
     missingFieldsError: "Texto, contraseña o IV no proporcionados.",
     encryptionError: "Error al encriptar el texto.",
     decryptionError: "Error al desencriptar el texto.",
-    //bookmarking
     bookmarkTitle: "Marcadores",
     bookmarkDescription: "Descubre y gestiona marcadores en tu red.",
     bookmarkAllSectionTitle: "Marcadores",
@@ -500,7 +494,7 @@ module.exports = {
     bookmarkUrlPlaceholder: "https://ejemplo.com",
     bookmarkDescriptionLabel: "Descripción",
     bookmarkDescriptionPlaceholder: "Opcional",
-    bookmarkTagsLabel: "Tags",
+    bookmarkTagsLabel: "Etiquetas",
     bookmarkTagsPlaceholder: "Introduce tags separadas por comas",
     bookmarkCategoryLabel: "Categoría",
     bookmarkCategoryPlaceholder: "Opcional",
@@ -516,7 +510,6 @@ module.exports = {
     noUrl: "Sin enlace",
     noCategory: "Sin categoría",
     noLastVisit: "Sin última visita",
-    //videos
     videoTitle: "Vídeos",
     videoDescription: "Explora y gestiona contenido de vídeo en tu red.",
     videoPluginTitle: "Título",
@@ -555,7 +548,6 @@ module.exports = {
     videoMessageAuthorButton: "MP",
     videoUpdatedAt: "Actualizado",
     videoNoMatch: "Ningún vídeo coincide con tu búsqueda.",
-    //documents
     documentTitle: "Documentos",
     documentDescription: "Descubre y gestiona documentos en tu red.",
     documentAllSectionTitle: "Documentos",
@@ -592,7 +584,6 @@ module.exports = {
     documentSearchButton: "Buscar",
     documentNoMatch: "No hay documentos que coincidan con tu búsqueda.",
     documentUpdatedAt: "Actualizado",
-    //audios
     audioTitle: "Audios",
     audioDescription: "Explora y gestiona contenido de audio en tu red.",
     audioPluginTitle: "Título",
@@ -631,7 +622,6 @@ module.exports = {
     audioNoMatch: "Ningún audio coincide con tu búsqueda.",
     audioFavoritesSectionTitle: "Favoritos",
     audioFilterFavorites: "FAVORITOS",
-    //favorites
     favoritesTitle: "Favoritos",
     favoritesDescription: "Todos tus elementos marcados como favoritos en un solo lugar.",
     favoritesFilterAll: "TODOS",
@@ -640,10 +630,10 @@ module.exports = {
     favoritesFilterBookmarks: "MARCADORES",
     favoritesFilterDocuments: "DOCUMENTOS",
     favoritesFilterImages: "IMÁGENES",
+    favoritesFilterMaps: "MAPAS",
     favoritesFilterVideos: "VÍDEOS",
     favoritesRemoveButton: "Quitar de favoritos",
     favoritesNoItems: "Todavía no hay favoritos.",
-    //inhabitants
     yourContacts:       "Tus Contactos",
     allInhabitants:     "Habitantes",
     allCVs:             "Todos los CVs",
@@ -668,7 +658,7 @@ module.exports = {
     viewAvatar:        "Ver Avatar",
     viewCV:            "Ver CV",
     suggestedSectionTitle: "Sugeridos",
-    topkarmaSectionTitle: "Top Karma",
+    topkarmaSectionTitle: "Mejor Karma",
     topactivitySectionTitle: "Top Actividad",
     blockedSectionTitle: "Bloqueados",
     gallerySectionTitle: "GALERÍA",
@@ -680,7 +670,7 @@ module.exports = {
     oasisId: "ID",
     noInhabitantsFound:    "No se encontraron habitantes, aún.",
     inhabitantActivityLevel: "Nivel Actividad",
-    //parliament
+    deviceLabel: "Dispositivo",
     parliamentTitle: "Parlamento",
     parliamentDescription: "Explora formas de gobierno y políticas de gestión colectiva.",
     parliamentFilterGovernment: "GOBIERNO",
@@ -821,7 +811,6 @@ module.exports = {
     parliamentProposalVoteStatusLabel: "Estado de la votación",
     parliamentProposalOnTrackYes: "Umbral alcanzado",
     parliamentProposalOnTrackNo: "Por debajo del umbral",
-     //courts
     courtsTitle: "Tribunales",
     courtsDescription: "Explora formas de resolución de conflictos y de gestión colectiva de la justicia.",
     courtsFilterCases: "CASOS",
@@ -872,6 +861,7 @@ module.exports = {
     courtsNoNominations: "Todavía no hay nominaciones.",
     courtsAccuser: "Acusación",
     courtsRespondent: "Defensa",
+    courtsRespondentInvalid: "Debe ser un ID SSB válido (@...ed25519)",
     courtsThStatus: "Estado",
     courtsThAnswerBy: "Responder antes de",
     courtsThEvidenceBy: "Aportar pruebas antes de",
@@ -936,7 +926,6 @@ module.exports = {
     courtsRoleDictator: "Dictador",
     courtsAssignJudgeTitle: "Elegir juez",
     courtsAssignJudgeBtn: "Elegir juez",
-    //trending
     trendingTitle: "Tendencias",
     exploreTrending: "Explora el contenido más popular en tu red.",
     ALLButton: "TODOS",
@@ -982,7 +971,6 @@ module.exports = {
     trendingAuthor: "Por",
     trendingCreatedAtLabel: "Creado el",
     trendingTotalCount: "Total de Contadores",
-    //tasks
     tasksTitle: "Tareas",
     tasksDescription: "Descubre y gestiona tareas en tu red.",
     taskTitleLabel: "Título",
@@ -1038,7 +1026,6 @@ module.exports = {
     notasks: "No hay tareas disponibles.",
     noLocation: "No se especificó ubicación",
     taskSetStatus: "Establecer estado",
-    //events
     eventTitle: "Eventos",
     eventDateLabel: "Fecha",
     eventsTitle: "Eventos",
@@ -1106,7 +1093,6 @@ module.exports = {
     eventUnattended: "No asisto",
     eventStatusOpen: "Abierto",
     eventStatusClosed: "Cerrado",
-    //tags 
     tagsTitle: "Etiquetas",
     tagsDescription: "Descubre y explora patrones de taxonomía en tu red.",
     tagsAllSectionTitle: "Etiquetas",
@@ -1118,13 +1104,13 @@ module.exports = {
     tagsNoItems: "No hay etiquetas disponibles.",
     tagsTableHeaderTag: "ETIQUETA/ENLACE:",
     tagsTableHeaderCount: "CONTADOR:",
-    //transfers
     transfersTitle: "Transferencias",
     transfersDescription: "Descubre y gestiona transferencias en tu red.",
     transfersFrom: "De",
     transfersTo: "A",
     transfersFilterAll: "TODOS",
     transfersFilterMine: "MÍAS",
+    transfersFilterUBI: "RBU",
     transfersFilterMarket: "MERCADO",
     transfersFilterTop: "TOP",
     transfersFilterPending: "PENDIENTES",
@@ -1149,6 +1135,7 @@ module.exports = {
     transfersConfirmButton: "Confirmar Transferencia",
     transfersNoItems: "No se encontraron transferencias.",
     transfersMineSectionTitle: "Tus Transferencias",
+    transfersUBISectionTitle: "Transferencias RBU",
     transfersMarketSectionTitle: "Transferencias del Mercado",
     transfersTopSectionTitle: "Transferencias Principales",
     transfersPendingSectionTitle: "Transferencias Pendientes",
@@ -1175,7 +1162,6 @@ module.exports = {
     transfersExpiredBadge: "CADUCADA",
     transfersUpdatedAt: "Actualizado",
     transfersNoMatch: "No hay transferencias que coincidan con tu búsqueda.",
-    //votations (voting/polls)
     votationsTitle: "Votaciones",
     votationsDescription: "Descubre y gestiona votaciones en tu red.",
     voteMineSectionTitle: "Tus Votaciones",
@@ -1232,7 +1218,6 @@ module.exports = {
     voteNewCommentPlaceholder: 'Escribe aquí tu comentario…',
     voteNewCommentButton: 'Publicar comentario',
     voteNewCommentLabel: 'Añade un comentario',
-    //CV
     cvTitle: "CV",
     cvLabel: "Currículum Vitae (CV)",
     cvEditSectionTitle: "Editar CV",
@@ -1271,13 +1256,11 @@ module.exports = {
     cvEditButton: "Actualizar",
     cvDeleteButton: "Eliminar",
     cvNoCV: "No se encontró ningún CV.",
-    // blog/post,
     blogSubject: "Asunto",
     blogMessage: "Mensaje",
     blogImage: "Subir contenido multimedia (max: 50MB)",
     blogPublish: "Vista previa",
     noPopularMessages: "No se han publicado mensajes populares, aún",
-    // forum
     forumTitle: "Foros",
     forumCategoryLabel: "Categoría",
     forumTitleLabel: "Título",
@@ -1318,7 +1301,6 @@ module.exports = {
     forumCatPRIVACY: "Privacidad",
     forumCatCYBERWARFARE: "Ciberguerra",
     forumCatSURVIVALISM: "Supervivencia",
-    //images
     imageTitle: "Imágenes",
     imageDescription: "Explora y gestiona contenido de imágenes en tu red.",
     imagePluginTitle: "Título",
@@ -1362,8 +1344,11 @@ module.exports = {
     imageMessageAuthorButton: "Mensaje",
     imageUpdatedAt: "Actualizado",
     imageNoMatch: "Ninguna imagen coincide con tu búsqueda.",
-    //feed
     feedTitle:        "Feed",
+    feedDetailTitle:  "Feed",
+    feedOpenDiscussion: "Abrir discusión",
+    feedPostComment:  "Publicar comentario",
+    noComments:       "Aún no hay comentarios",
     createFeedTitle:  "Crear Feed",
     createFeedButton: "Enviar Feed!",
     feedPlaceholder:  "¿Qué está pasando? (máximo 280 caracteres)",
@@ -1379,10 +1364,8 @@ module.exports = {
     author:           "Por",
     createdAtLabel:   "Creado el",
     FeedshareYourOpinions: "Descubre y comparte textos breves en tu red.",
-    //feed
     refeedButton: "Realimentar",
     alreadyRefeeded: "Ya has realimentado esto.",
-    //activity
     activityTitle:        "Actividad",
     yourActivity:         "Tu actividad",
     globalActivity:       "Actividad global",
@@ -1429,7 +1412,7 @@ module.exports = {
     typeFeed:             "FEED",
     typeContact:          "CONTACTO",
     typePub:              "PUB",
-    typeTombstone:        "TOMBSTONE",
+    typeTombstone:        "ELIMINADO",
     typeBanking:          "BANCA",
     typeBankWallet:       "BANCA/MONEDERO",
     typeBankClaim:        "BANCA/UBI",
@@ -1556,7 +1539,6 @@ module.exports = {
     courtsPublicPrefSubmit: 'Guardar preferencia de visibilidad',
     courtsMethodMEDIATION: 'Mediación',
     courtsNoCases: 'No hay casos.',
-    //reports
     reportsTitle: "Informes",
     reportsDescription: "Gestiona y realiza un seguimiento de los informes relacionados con problemas, errores, abusos y advertencias de contenido en tu red.",
     reportsFilterAll: "TODOS",
@@ -1652,7 +1634,7 @@ module.exports = {
     reportsEvidenceLinksPlaceholder: 'Pega enlaces, IDs de mensajes, capturas (si aplica), etc.',
     reportsContentLocationLabel: 'Dónde está el contenido',
     reportsContentLocationPlaceholder: 'Enlace, ID, canal, hilo, autor, etc.',
-    reportsWhyInappropriateLabel: 'Por qué es inapropiado',
+    reportsWhyInappropriateLabel: '¿Por qué es inapropiado?',
     reportsWhyInappropriatePlaceholder: 'Explica el motivo y el impacto.',
     reportsRequestedActionLabel: 'Acción solicitada',
     reportsRequestedActionPlaceholder: 'Eliminar, ocultar, marcar, advertir, etc.',
@@ -1713,8 +1695,8 @@ module.exports = {
     tribeFeedFilterMINE: "MIOS",
     tribeFeedFilterALL: "TODOS",
     tribeFeedFilterTOP: "TOP",
-    tribeFeedRefeeds: "Refeeds",
-    tribeFeedRefeed: "Refeed",
+    tribeFeedRefeeds: "Redifusiones",
+    tribeFeedRefeed: "Redifundir",
     tribeFeedMessagePlaceholder: "Escribe un feed…",
     tribeFeedSend: "Enviar",
     tribeFeedEmpty: "No hay mensajes de feed disponibles, aún.",
@@ -1733,7 +1715,7 @@ module.exports = {
     tribeSectionMarket: "Mercado",
     tribeSectionJobs: "Empleos",
     tribeSectionProjects: "Proyectos",
-    tribeSectionMedia: "Media",
+    tribeSectionMedia: "Multimedia",
     tribeSectionImages: "IMÁGENES",
     tribeSectionAudios: "AUDIOS",
     tribeSectionVideos: "VÍDEOS",
@@ -1815,7 +1797,7 @@ module.exports = {
     tribeMediaDescription: "Descripción",
     tribeMediaType: "Tipo",
     tribeMediaTypeImage: "Imagen",
-    tribeMediaTypeVideo: "Video",
+    tribeMediaTypeVideo: "Vídeo",
     tribeMediaTypeAudio: "Audio",
     tribeMediaTypeDocument: "Documento",
     tribeMediaTypeBookmark: "Marcador",
@@ -1825,7 +1807,7 @@ module.exports = {
     tribeGroupOffice: "Oficina",
     tribeGroupNetwork: "Red",
     tribeGroupEconomy: "Economía",
-    tribeGroupMedia: "Media",
+    tribeGroupMedia: "Multimedia",
     tribeStatusOpen: "ABIERTO",
     tribeStatusClosed: "CERRADO",
     tribeStatusInProgress: "EN PROGRESO",
@@ -1876,7 +1858,7 @@ module.exports = {
     tribeTrendingEngagement: "interacción",
     tribeOpinionsEmpty: "Sin opiniones aún.",
     tribeOpinionsCast: "Votar",
-    tribeOpinionsRankings: "Rankings",
+    tribeOpinionsRankings: "Clasificaciones",
     tribeOpinionsAlreadyVoted: "Ya has votado",
     tribeTopCategory: "Más votado",
     tribePixeliaTitle: "Pixelia",
@@ -1942,7 +1924,6 @@ module.exports = {
     agendaTransferConcept: "Concepto",
     agendaTransferAmount: "Cantidad",
     agendaTransferDeadline: "Plazo",
-    //opinions
     opinionsTitle: "Opiniones",
     shareYourOpinions: "Descubre y vota por opiniones en tu red.",
     author: "Por",
@@ -1983,7 +1964,7 @@ module.exports = {
     misleadingButton: "ENGAÑOSO",
     offTopicButton: "FUERA DE TEMA",
     duplicateButton: "DUPLICADO",
-    clickbaitButton: "CLICKBAIT",
+    clickbaitButton: "CEBO DE CLICS",
     spamButton: "SPAM",
     trollButton: "TROLL",
     nsfwButton: "NSFW",
@@ -2008,7 +1989,7 @@ module.exports = {
     voteMisleading: "Engañoso",
     voteOffTopic: "Fuera de tema",
     voteDuplicate: "Duplicado",
-    voteClickbait: "Clickbait",
+    voteClickbait: "Cebo de clics",
     votePropaganda: "Propaganda",
     voteFunny: "Divertido",
     voteInspiring: "Inspirador",
@@ -2030,7 +2011,6 @@ module.exports = {
     voteCreative: "Creativo",
     voteSpam: "Spam",
     voteAdultOnly: "Solo adultos",
-    //inbox
     publishBlog: "Publicar Blog",
     privateMessage: "MP",
     pmSendTitle: "Mensajes Privados",
@@ -2063,9 +2043,9 @@ module.exports = {
     pmNoSubject: "(sin asunto)",
     pmSubjectLabel: "Asunto:",
     pmBodyLabel: "Cuerpo",
-    pmBotJobs: "42-JobsBOT",
-    pmBotProjects: "42-ProjectsBOT",
-    pmBotMarket: "42-MarketBOT",
+    pmBotJobs: "42-EmpleosBOT",
+    pmBotProjects: "42-ProyectosBOT",
+    pmBotMarket: "42-MercadoBOT",
     inboxJobSubscribedTitle: "Nueva suscripción a tu oferta de trabajo",
     pmInhabitantWithId: "Habitante con ID OASIS:",
     pmHasSubscribedToYourJobOffer: "se ha suscrito a tu oferta de trabajo",
@@ -2078,9 +2058,8 @@ module.exports = {
     inboxProjectPledgedTitle: "Nueva aportación a tu proyecto",
     pmHasPledged: "ha aportado",
     pmToYourProject: "a tu proyecto",
-    //blockexplorer
-    blockchain: 'Explorador de Bloques',
-    blockchainTitle: 'Explorador de Bloques',
+    blockchain: 'Explorador',
+    blockchainTitle: 'Explorador',
     blockchainDescription: 'Explora y visualiza los bloques de la cadena.',
     blockchainNoBlocks: 'No se encontraron bloques en la cadena.',
     blockchainBlockID: 'ID del Bloque',
@@ -2097,9 +2076,8 @@ module.exports = {
     blockchainBlockInfo: 'Información del Bloque',
     blockchainBlockDetails: 'Detalles del bloque seleccionado',
     blockchainBack: 'Volver al Blockexplorer',
-    blockchainContentDeleted: 'Este contenido ha sido eliminado.',
+    blockchainContentDeleted: 'Contenido eliminado',
     visitContent: 'Visitar contenido',
-    // banking
     banking: 'Banking',
     bankingTitle: 'Banking',
     bankingDescription: 'Explora el valor actual de ECOin y la asignación de RBU correspondiente, distribuida semanalmente en función de la participación y la confianza.',
@@ -2111,6 +2089,8 @@ module.exports = {
     bankBack: 'Volver a Banking',
     bankViewTx: 'Ver Tx',
     bankClaimNow: 'Reclamar',
+    bankClaimUBI: '¡Reclamar RBU!',
+    bankNoPendingUBI: 'No hay asignaciones RBU pendientes para esta época.',
     bankPubBalance: 'Saldo del PUB',
     bankEpoch: 'Época',
     bankPool: 'Fondo (esta época)',
@@ -2159,11 +2139,75 @@ module.exports = {
     bankMyAddress: 'Tu dirección',
     bankRemoveMyAddress: 'Eliminar mi dirección',
     bankNotRemovableOasis: 'Las direcciones no se pueden eliminar localmente',
-    bankingFutureUBI: "Asignación Estimada de UBI",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "Tiendas",
+    shopDescription: "Descubre y gestiona tiendas en la red.",
+    shopTitle: "Tienda",
+    shopFilterAll: "TODAS",
+    shopFilterMine: "MÍAS",
+    shopFilterRecent: "RECIENTES",
+    shopFilterTop: "TOP",
+    shopFilterProducts: "PRODUCTOS",
+    shopFilterPrices: "PRECIOS",
+    shopFilterFavorites: "FAVORITAS",
+    shopUpload: "Crear Tienda",
+    shopAllSectionTitle: "Tiendas",
+    shopMineSectionTitle: "Mis Tiendas",
+    shopRecentSectionTitle: "Tiendas Recientes",
+    shopTopSectionTitle: "Tiendas Destacadas",
+    shopProductsSectionTitle: "Productos Destacados",
+    shopPricesSectionTitle: "Productos por Precio",
+    shopFavoritesSectionTitle: "Favoritas",
+    shopCreateSectionTitle: "Crear Tienda",
+    shopUpdateSectionTitle: "Actualizar Tienda",
+    shopCreate: "Crear",
+    shopUpdate: "Actualizar",
+    shopDelete: "Eliminar",
+    shopAddFavorite: "Añadir Favorito",
+    shopRemoveFavorite: "Quitar Favorito",
+    shopOpen: "ABIERTA",
+    shopClosed: "CERRADA",
+    shopOpenShop: "Abrir Tienda",
+    shopCloseShop: "Cerrar Tienda",
+    shopProducts: "Productos",
+    shopProductAdd: "Añadir Producto",
+    shopProductTitle: "Producto",
+    shopProductUpdate: "Actualizar Producto",
+    shopProductPrice: "Precio (ECO)",
+    shopProductStock: "Stock",
+    shopProductUntitled: "Producto sin título",
+    shopUntitled: "Tienda sin título",
+    shopNoItems: "No se encontraron tiendas.",
+    shopNoProducts: "Aún no hay productos.",
+    shopOutOfStock: "Agotado",
+    shopBuy: "Comprar",
+    shopBackToShop: "Volver a la Tienda",
+    shopShareUrl: "URL para compartir",
+    shopVisitShop: "VISITAR TIENDA",
+    shopStatus: "ESTADO",
+    shopCreatedAt: "CREADO",
+    shopSearchPlaceholder: "Buscar tiendas...",
+    shopUrl: "URL",
+    shopLocation: "Ubicación",
+    shopTags: "Etiquetas",
+    shopVisibility: "Visibilidad",
+    shopImage: "Imagen",
+    shopShortDescription: "Descripcion Corta",
+    shopShortDescriptionPlaceholder: "Descripcion breve para las tarjetas (max 160 caracteres)",
+    shopTitlePlaceholder: "Nombre de tu tienda",
+    shopDescriptionPlaceholder: "Descripcion detallada de tu tienda",
+    shopUrlPlaceholder: "https://url-de-tu-tienda.com",
+    shopLocationPlaceholder: "Ciudad, Pais",
+    shopTagsPlaceholder: "etiqueta1, etiqueta2, etiqueta3",
+    mapTitlePlaceholder: "Titulo del mapa",
+    shopProductFeatured: "Producto Destacado",
+    shopSendToMarket: "Send to Market",
+    typeShop: "TIENDA",
+    typeShopProduct: "PRODUCTO DE TIENDA",
     bankExchange: 'Intercambio',
     bankExchangeCurrentValue: 'Valor de ECOin (1h)',
     bankTotalSupply: 'Suministro Total de ECOin',
-    bankEcoinHours: 'Equivalencia de ECOin en Tiempo',
+    bankEcoinHours: 'Horas ECOin',
     bankHoursOfWork: 'horas',
     bankExchangeNoData: 'No hay datos disponibles',
     bankExchangeIndex: 'Valor de ECOin (1h)',
@@ -2172,7 +2216,6 @@ module.exports = {
     bankingSyncStatus: 'Estado de ECOin',
     bankingSyncStatusSynced: 'Sincronizado',
     bankingSyncStatusOutdated: 'Desactualizado',
-    //stats
     statsTitle: 'Estadísticas',
     statistics: "Estadísticas",
     statsInhabitant: "Estadísticas del Habitante",
@@ -2222,6 +2265,9 @@ module.exports = {
     statsAudio: "Audios",
     statsVideo: "Vídeos",
     statsDocument: "Documentos",
+    statsMap: "Mapas",
+    statsShop: "Tiendas",
+    statsShopProduct: "Productos de tienda",
     statsTransfer: "Transferencias",
     statsAiExchange: "IA",
     statsPUBs: 'PUBs',
@@ -2289,7 +2335,6 @@ module.exports = {
     statsCourtsSettlementAccepted: "Acuerdos aceptados",
     statsCourtsNomination: "Nominaciones de jueces",
     statsCourtsNominationVote: "Votos de nominación",
-    //AI
     ai: "IA",
     aiTitle: "IA",
     aiDescription: "Una Inteligencia Artificial Colectiva (IAC) llamada '42' que aprende de tu red.",
@@ -2298,9 +2343,9 @@ module.exports = {
     aiResponseTitle: "Respuesta",
     aiSubmitButton: "Enviar!",
     aiSettingsDescription: "Configura tu prompt (máx 128 caracteres) para el modelo de IA.",
-    aiPrompt: "Provide an informative and precise response.",
+    aiPrompt: "Proporciona una respuesta informativa y precisa.",
     aiConfiguration: "Configurar prompt",
-    aiPromptUsed: "Prompt",
+    aiPromptUsed: "Indicación",
     aiClearHistory: "Borrar historial de chat",
     aiSharePrompt: "Añadir esta respuesta al entrenamiento colectivo?",
     aiShareYes: "Sí",
@@ -2308,7 +2353,7 @@ module.exports = {
     aiSharedLabel: "Añadida al entrenamiento",
     aiRejectedLabel: "No añadida al entrenamiento",
     aiServerError: "La IA no ha podido responder. Inténtalo de nuevo.",
-    aiInputPlaceholder: "What is Oasis?",
+    aiInputPlaceholder: "¿Qué es Oasis?",
     typeAiExchange: "IA",
     aiApproveTrain: "Añadir al entrenamiento colectivo",
     aiRejectTrain: "No entrenar",
@@ -2321,7 +2366,6 @@ module.exports = {
     aiApproveCustomTrain: "Entrenar con esta respuesta personalizada",
     aiCustomAnswerPlaceholder: "Escribe tu respuesta personalizada…",
     statsAIExchanges: "Intercambio de Modelos",
-    //market
     marketMineSectionTitle: "Tus artículos",
     marketCreateSectionTitle: "Crear artículo",
     marketUpdateSectionTitle: "Actualizar",
@@ -2348,6 +2392,7 @@ module.exports = {
     marketItemDescription: "Descripción",
     marketItemDescriptionPlaceholder: "Describe el artículo que estás vendiendo",
     marketItemStatus: "Estado",
+    marketShopLabel: "Tienda",
     marketItemCondition: "Condición",
     marketItemPrice: "Precio",
     marketItemTags: "Etiquetas",
@@ -2356,7 +2401,7 @@ module.exports = {
     marketItemIncludesShipping: "¿Incluye envío?",
     marketItemHighestBid: "Puja más alta",
     marketItemHighestBidder: "Mejor postor",
-    marketItemStock: "Stock",
+    marketItemStock: "Existencias",
     marketOutOfStock: "Sin stock",
     marketItemBidTime: "Fecha de la puja",
     marketActionsUpdate: "Actualizar",
@@ -2384,8 +2429,7 @@ module.exports = {
     marketAuctionEnded: "Terminó",
     marketMyBidBadge: "Has pujado",
     marketNoItemsMatch: "No hay artículos que coincidan con tu búsqueda.",
-    //jobs
-    jobsTitle: "Ofertas de trabajo",
+    jobsTitle: "Laboral",
     jobsDescription: "Descubre y gestiona ofertas de trabajo en tu red.",
     jobsFilterRecent: "RECIENTES",
     jobsFilterMine: "MIOS",
@@ -2472,10 +2516,9 @@ module.exports = {
     jobsUpdatedAt: "Actualizado",
     jobSetOpen: "Marcar abierto",
     jobNewBadge: "NUEVO",
-    jobsTagsLabel: "Tags",
-    jobsTagsPlaceholder: "tag1, tag2, tag3",
+    jobsTagsLabel: "Etiquetas",
+    jobsTagsPlaceholder: "etiqueta1, etiqueta2, etiqueta3",
     noJobsMatch: "No hay ofertas que coincidan con tu búsqueda.",
-    //projects
     projectsTitle: "Proyectos",
     projectsDescription: "Crea, financia y sigue proyectos impulsados por la comunidad en tu red.",
     projectCreateProject: "Crear Proyecto",
@@ -2581,11 +2624,9 @@ module.exports = {
     projectPledgeAmount: "Cantidad",
     projectSelectMilestoneOrBounty: "Seleccionar Hito o Recompensa",
     projectPledgeButton: "Aportar",
-    //footer
     footerLicense: "GPLv3",
     footerPackage: "Paquete",
     footerVersion: "Versión",
-    //modules
     modulesModuleName: "Nombre",
     modulesModuleDescription: "Descripción",
     modulesModuleStatus: "Estado",
@@ -2632,6 +2673,8 @@ module.exports = {
     modulesTasksDescription: "Módulo para descubrir y gestionar tareas.",
     modulesMarketLabel: "Mercado",
     modulesMarketDescription: "Módulo para intercambiar bienes o servicios.",
+    modulesShopsLabel: "Tiendas",
+    modulesShopsDescription: "Módulo para gestionar y descubrir tiendas.",
     modulesTribesLabel: "Tribus",
     modulesTribesDescription: "Módulo para explorar o crear tribus (grupos).",
     modulesVotationsLabel: "Votaciones",
@@ -2669,7 +2712,7 @@ module.exports = {
     goBack: "Volver",
     directConnect: "Conexión Directa",
     directConnectDescription: "Conéctate directamente a un nodo introduciendo su dirección IP, puerto y clave pública. El nodo se añadirá como conexión seguida.",
-    peerHost: "IP / Hostname",
+    peerHost: "IP / Nombre de host",
     peerPort: "Puerto (por defecto: 8008)",
     peerPublicKey: "Clave Pública (@...ed25519)",
     connectAndFollow: "Conectar",
@@ -2686,8 +2729,72 @@ module.exports = {
     statsCarbonTombstone: "Huella del tombstoning",
     feedSuccessMsg: "¡Feed publicado correctamente!",
     dominantOpinionLabel: "Opinión predominante",
-    uploadMedia: "Subir contenido multimedia (max: 50MB)"
-
-     //END
+    uploadMedia: "Subir contenido multimedia (max: 50MB)",
+    invitesUnfollow: "Dejar de seguir",
+    invitesFollow: "Seguir",
+    invitesUnfollowedInvites: "Redes no federadas",
+    invitesNoUnfollowed: "Sin redes no federadas.",
+    reportsWhyInappropriateLabel: "¿Por qué es inapropiado?",
+    blockchainContentDeleted: "Este contenido ha sido eliminado",
+    visitContent: "Visitar contenido",
+    bankEcoinHours: "Equivalencia ECOin en tiempo",
+    mapsLabel: "Mapas",
+    mapTitle: "Mapas",
+    mapDescription: "Explora y gestiona mapas offline en tu red.",
+    mapMineSectionTitle: "Tus Mapas",
+    mapCreateSectionTitle: "Crear Mapa",
+    mapUpdateSectionTitle: "Actualizar Mapa",
+    mapAllSectionTitle: "Mapas",
+    mapRecentSectionTitle: "Mapas Recientes",
+    mapFavoritesSectionTitle: "Favoritos",
+    mapFilterAll: "TODOS",
+    mapFilterMine: "MÍOS",
+    mapFilterRecent: "RECIENTES",
+    mapFilterFavorites: "FAVORITOS",
+    mapUploadButton: "Crear Mapa",
+    mapCreateButton: "Crear Mapa",
+    mapUpdateButton: "Actualizar",
+    mapDeleteButton: "Eliminar",
+    mapAddFavoriteButton: "Añadir favorito",
+    mapRemoveFavoriteButton: "Quitar favorito",
+    mapLatLabel: "Latitud",
+    mapLatPlaceholder: "ej. 40.4168",
+    mapLngLabel: "Longitud",
+    mapLngPlaceholder: "ej. -3.7038",
+    mapDescriptionLabel: "Descripción",
+    mapDescriptionPlaceholder: "Describe el mapa o ubicación...",
+    mapTypeLabel: "Tipo de mapa",
+    mapTypeSingle: "ÚNICO (solo ubicación inicial)",
+    mapTypeOpen: "ABIERTO (cualquiera añade marcadores)",
+    mapTypeClosed: "CERRADO (solo el creador añade marcadores)",
+    mapTagsLabel: "Etiquetas",
+    mapTagsPlaceholder: "Introduce etiquetas separadas por comas",
+    mapUrlLabel: "URL del mapa",
+    mapPickCoordLabel: "O selecciona una ubicación en la cuadrícula:",
+    mapMarkersLabel: "marcadores",
+    mapMarkersTitle: "Marcadores",
+    mapMarkerDefault: "Marcador",
+    mapMarkerLatLabel: "Latitud del marcador",
+    mapMarkerLngLabel: "Longitud del marcador",
+    mapMarkerLabelField: "Etiqueta del marcador",
+    mapMarkerLabelPlaceholder: "Describe este marcador...",
+    markerImageLabel: "Imagen del Marker",
+    mapAddMarkerTitle: "Añadir Marcador",
+    mapAddMarkerButton: "Añadir Marcador",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "Aplicar Zoom",
+    mapSearchPlaceholder: "Buscar descripción, etiquetas, autor...",
+    mapSearchButton: "Buscar",
+    mapClickToCreate: "Haz click en el mapa para seleccionar ubicación",
+    mapClickToAddMarker: "Haz click en el mapa para añadir un marcador",
+    mapUpdatedAt: "Actualizado",
+    mapNoMatch: "Ningún mapa coincide con tu búsqueda.",
+    noMaps: "No hay mapas disponibles.",
+    mapLocationTitle: "Ubicación",
+    mapVisitLabel: "Visitar mapa",
+    typeMap: "MAPAS",
+    typeMapMarker: "MARCADOR DE MAPA",
+    modulesMapLabel: "Mapas",
+    modulesMapDescription: "Módulo para gestionar y compartir mapas offline."
     }
 };

+ 177 - 83
src/client/assets/translations/oasis_eu.js

@@ -2,7 +2,7 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe');
 module.exports = {
   eu: {
     languageName: "Basque",
-    extended: "Multiverse",
+    extended: "Multibertsoa",
     extendedDescription: [
     "Norbait laguntzen duzunean, laguntzen dituzun erabiltzaileen bidalketak deskargatu ditzakezu. Mezu horiek hemen azalduko dira, dataren arabera antolatuta.",
     ],
@@ -31,7 +31,7 @@ module.exports = {
      "Zure ", strong("gai eta iruzkin batzuk"),
       " eta laguntzen dituzun bizilagunenak, gaurkotasunaren arabera antolatuta. Hautatu edozein bidalketaren ordu-marka hari osoa ikusteko.",
     ],
-    threads: "Threads",
+    threads: "Hariak",
     threadsDescription: [
       "Zure ", strong("iruzkindutako bidalketak"),
       " eta laguntzen dituzun bizilagunenak, gaurkotasunaren arabera antolatuta. Hautatu edozein bidalketaren ordu-marka hari osoa ikusteko.",
@@ -48,8 +48,8 @@ module.exports = {
     previousPage: "Aurrekoa",
     noMentions: "Ez duzu aipamenik jaso, oraindik.",
     private: "Sarrera-ontzia",
-    privateDescription: "View and manage your private messages.",
-    privateInbox: "INBOX",
+    privateDescription: "Ikusi eta kudeatu zure mezu pribatuak.",
+    privateInbox: "SARRERA-ONTZIA",
     privateSent: "BIDALITAKOAK",
     privateFrom: "Nork",
     privateTo: "Nori",
@@ -60,14 +60,13 @@ module.exports = {
     peers: "Parekoak",
     privateDescription: ["Mezu pribatuak ", strong("zure gako publikoarekin zifratzen dira")," eta 7 hartzaile izan ditzakete gehienez."],
     search: "Bilatu",
-    searchDescription: "Bilatu edukia zure sarean.",
+    searchDescription: "Deskribapena",
     imageSearch: "Irudiak bilatu",
     searchPlaceholder: "Bilatu @bizilagunak, #etiketak eta gako-hitzak...",
     settings: "Ezarpenak",
     continueReading: "Irakurtzen jarraitu",
     moreComments: "iruzkin gehiago",
     readThread: "irakurri hari osoa",    
-    // pixelia
     pixeliaTitle: 'Pixelia',
     pixeliaDescription: 'Draw pixels on the grid and collaborARTe with others in your network.',
     pixeliaDescription: 'Marraztu pixelak saretan eta ekin elkARTlanari zure sarean.',
@@ -80,7 +79,6 @@ module.exports = {
     goToMuralButton: "Ikusi Murala",
     totalPixels: 'Pixelak guztira',
     pixeliaBy: "egilea",
-    // modules
     modules: "Moduluak",
     modulesViewTitle: "Moduluak",
     modulesViewDescription: "Konfiguratu zure ingurunea moduluak gaitu edo desgaituz.",
@@ -113,7 +111,6 @@ module.exports = {
     opinionsTitle: "Iritziak",
     saveSettings: "Gorde konfigurazioa",
     apply: "Aplikatu",
-    // menu categories
     menuPersonal: "Pertsonala",
     menuContent: "Edukia",
     menuGovernance: "Gobernantza",
@@ -124,13 +121,11 @@ module.exports = {
     menuEconomy: "Ekonomia",
     menuMedia: "Multimedia",
     menuTools: "Tresnak",
-    // post actions
     comment: "Iruzkindu",
     subtopic: "Azpi-gaia",
     json: "JSON",
     createdAt: "Sorrera data",
     createdBy: "Sortzailea",
-    // relationships
     unfollow: "Laguntza eten",
     follow: "Lagundu",
     block: "Blokeatu",
@@ -153,11 +148,9 @@ module.exports = {
     relationshipNone: "Ez duzu laguntzen",
     relationshipConflict: "Gatazka",
     relationshipBlockingPost: "Blokeatutako bidalketa",
-    // spreads view
     viewLikes: "Ikusi zabalpenak",
     spreadedDescription: "Bizilagunak zabaldutako bidalketen zerrenda.",
     totalspreads: "Hedapenak guztira",
-    // composer
     attachFiles: "Erantsi fitxategiak",
     preview: "Aurrebista",
     publish: "Idatzi",
@@ -192,12 +185,10 @@ module.exports = {
     ],
     publishCustom: "Idatzi bidalketa aurreratua",
     subtopicLabel: "Bidalketa honen azpi-gaia sortu",
-    // mentions
     messagePreview: "Aurreikusi bidalketa",
     mentionsMatching: "Bat datozen aipamenak",
     mentionsName: "Izena",
     mentionsRelationship: "Erlazioa",
-    // settings
     updateit: "LORTU EGUNERAKETAK!",
     updateBannerText: "Oasis-en bertsio berri bat eskuragarri dago.",
     updateBannerAction: "Eguneratu orain →",
@@ -267,7 +258,6 @@ module.exports = {
     homePageTitle: "Hasierako orria",
     homePageDescription: "Hautatu nahi duzun moduluaren orria hasiera gisa.",
     saveHomePage: "Gorde hasierako orria",
-    //invites
     invites: "Gonbidapenak",
     invitesTitle: "Gonbidapenak",
     invitesInvites: "Gonbidapenak",
@@ -290,9 +280,7 @@ module.exports = {
     currentlyUnreachable: "ERROR!",
     errorDetails: "Errore Xehetasunak",
     genericError: "Errore bat gertatu da.",
-    //panic  
     panicMode: "Izu Modua!",
-    //cipher
     encryptData: "Idatzi pasahitza (gutxienez 32 karaketere) zure blockchain-a zifratzeko.",
     decryptData: "Sartu pasahitza zure blockchain-a deszifratzeko.",
     panicModeDescription: "Zifratu/deszifratu edo EZABATU zure blockchain-a",
@@ -300,7 +288,6 @@ module.exports = {
     encryptPanicButton: "Zifratu blockchain-a",
     decryptPanicButton: "Deszifratu blockchain-a",
     removePanicButton: "EZABATU ZURE DATU GUZTIAK!",    
-    //search
     searchTitle: "Bilatu",
     searchDescriptionLabel: "Bilatu edukia zure sarean.",
     searchLanguagesLabel: "Hizkuntzak",
@@ -348,9 +335,7 @@ module.exports = {
     votesCreatedByLabel:"Noiz",
     voteStatusOpen:"IREKITA",
     voteStatusClosed:"ITXITA",
-    //image search page
     imageSearchLabel: "Hitz hauek dituzten irudiak bilatu.",
-    //posts and comments
     commentDescription: ({ parentUrl }) => [
       " noiz komentatuta ",
       a({ href: parentUrl }, " haria"),
@@ -362,7 +347,6 @@ module.exports = {
     ],
     subtopicTitle: ({ authorName }) => [`Subtopic on @${authorName}'s post`],
     mysteryDescription: " bidalketa misteriotsua argitaratu du",
-    // misc + search
     oasisDescription: "OASIS Proiektuko Sarea",
     searchSubmit: "Bilatu...",
     hashtagDescription: "Traola honekin etiketaturiko bidalketak.",
@@ -383,6 +367,17 @@ module.exports = {
     bookmarkLabel: "MARKAGAILUAK",
     tribeLabel: "TRIBUAK",
     marketLabel: "MERKATUA",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "ZORROTAK",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "CV-ak",
     submit: "Bidali",
     subjectLabel: "Gaia",
@@ -406,7 +401,7 @@ module.exports = {
     walletReceiveTitle: "Jaso",
     walletHistoryTitle: "Historia",
     walletBalanceLine: ({ balance }) => `${balance} ECO`,
-    walletCnfrs: "Berrespen",
+    walletCnfrs: "Berr.",
     walletConfirm: "Berretsi",
     walletDescription: "Kudeatu zure ondasun digitalak. Hala nola, ECOin-ak bidali eta jaso, zure balantzea ikusi eta zure transakzio historia kontsultatu.",
     walletDate: "Data",
@@ -443,7 +438,6 @@ module.exports = {
     walletUser: "Erabiltzaile izena",
     walletPass: "Pasahitza",
     walletConfiguration: "Ezarri zorroa",
-    //cipher
     cipher: "Zifratzailea",
     cipherTitle: "Zifratzailea",
     cipherDescription: "Zifratu eta deszifratu testua simetrikoki (Pasahitza erabiliz).",
@@ -453,6 +447,7 @@ module.exports = {
     cipherTextLabel: "Zifratuko den testua",
     cipherTextPlaceholder: "Idatzi zifratuko den testua...",
     cipherPasswordLabel: "Pasahitza",
+    cipherPasswordDecryptLabel: "Pasahitza ezarri (gutxienez 32 karaktere) testua deszifratzeko",
     cipherPasswordPlaceholder: "Idatzi pasahitza...",
     cipherEncryptButton: "Zifratu",
     cipherDecryptTitle: "Deszifratu testua",
@@ -478,7 +473,6 @@ module.exports = {
     missingFieldsError: "Ez da testurik, pasahitzik edo HB-rik sartu.",
     encryptionError: "Errorea testua zifratzean.",
     decryptionError: "Errorea testua deszifratzean.",
-    //bookmarking
     bookmarkTitle: "Laster-markak",
     bookmarkDescription: "Zure sareko laster-markak aurkitu eta kudeatu.",
     bookmarkAllSectionTitle: "Laster-markak",
@@ -518,7 +512,6 @@ module.exports = {
     noUrl: "Estekarik ez",
     noCategory: "Kategoriarik ez",
     noLastVisit: "Azken bisitarik ez",
-    //videos
     videoTitle: "Bideoak",
     videoDescription: "Arakatu eta kudeatu zure sareko bideo-edukia.",
     videoPluginTitle: "Izenburua",
@@ -557,7 +550,6 @@ module.exports = {
     videoMessageAuthorButton: "MP",
     videoUpdatedAt: "Eguneratua",
     videoNoMatch: "Ez dago zure bilaketarekin bat datorren bideorik.",
-    //documents
     documentTitle: "Dokumentuak",
     documentDescription: "Aurkitu eta kudeatu dokumentuak zure sarean.",
     documentAllSectionTitle: "Dokumentuak",
@@ -594,7 +586,6 @@ module.exports = {
     documentSearchButton: "Bilatu",
     documentNoMatch: "Ez dago bilaketarekin bat datorren dokumenturik.",
     documentUpdatedAt: "Eguneratua",
-    //audios
     audioTitle: "Audioak",
     audioDescription: "Arakatu eta kudeatu zure sareko audio-edukia.",
     audioPluginTitle: "Izenburua",
@@ -633,7 +624,6 @@ module.exports = {
     audioNoMatch: "Ez dago zure bilaketarekin bat datorren audiorik.",
     audioFavoritesSectionTitle: "Gogokoak",
     audioFilterFavorites: "GOGOKOAK",
-    //favorites
     favoritesTitle: "Gogokoak",
     favoritesDescription: "Zure gogoko guztiak leku berean.",
     favoritesFilterAll: "GUZTIAK",
@@ -642,10 +632,10 @@ module.exports = {
     favoritesFilterBookmarks: "MARKATZAILEAK",
     favoritesFilterDocuments: "DOKUMENTUAK",
     favoritesFilterImages: "IRUDIAK",
+    favoritesFilterMaps: "MAPAK",
     favoritesFilterVideos: "BIDEOAK",
     favoritesRemoveButton: "Gogokoetatik kendu",
     favoritesNoItems: "Oraindik ez dago gogokorik.",
-    //inhabitants
     yourContacts:       "Zure Kontaktuak",
     allInhabitants:     "Bizilagunak",
     allCVs:             "CV guztiak",
@@ -670,7 +660,7 @@ module.exports = {
     viewAvatar:        "Ikusi Abatarra",
     viewCV:            "Ikusi CV-a",
     suggestedSectionTitle: "Gomendatuta",
-    topkarmaSectionTitle: "Top Karma",
+    topkarmaSectionTitle: "Karma Onena",
     topactivitySectionTitle: "Top Jarduera",
     blockedSectionTitle: "Blokeatuta",
     gallerySectionTitle: "GALERIA",
@@ -682,11 +672,11 @@ module.exports = {
     oasisId: "ID-a",
     noInhabitantsFound:  "Bizilagunik ez, oraindik.",
     inhabitantActivityLevel: "Jarduera Maila",
-    //parliament
+    deviceLabel: "Gailua",
     parliamentTitle: "Legebiltzarra",
     parliamentDescription: "Gobernu formak eta kudeaketa kolektiboko politikak arakatu.",
     parliamentFilterGovernment: "GOBERNUA",
-    parliamentFilterCandidatures: "KANDIDATURAK",
+    parliamentFilterCandidatures: "HAUTAGAITZAK",
     parliamentFilterProposals: "PROPOSAMENAK",
     parliamentFilterLaws: "LEGEAK",
     parliamentFilterHistorical: "HISTORIKOA",
@@ -721,7 +711,6 @@ module.exports = {
     parliamentRevocationLaw: 'Legea',
     parliamentRevocationTitle: 'Izenburua',
     parliamentRevocationReasons: 'Arrazoiak',
-    parliamentRevocationPublish: "Indargabetzea Argitaratu",
     parliamentCurrentRevocationsTitle: 'Uneko Indargabetzeak',
     parliamentFutureRevocationsTitle: 'Etorkizuneko Indargabetzeak',
     parliamentPoliciesRevocated: 'HISTORIKOA',
@@ -731,7 +720,7 @@ module.exports = {
     parliamentNoStableGov: "Oraindik ez da gobernurik hautatu.",
     parliamentNoGovernments: "Oraindik ez dago gobernurik.",
     parliamentCandidatureFormTitle: "Kandidatura Proposatu",
-    parliamentCandidatureId: "Kandidatura",
+    parliamentCandidatureId: "Hautagaitza",
     parliamentCandidatureIdPh: "Oasis ID (@...) edo tribuaren izena",
     parliamentCandidatureSlogan: "Leloa (geh. 140 karaktere)",
     parliamentCandidatureSloganPh: "Lelo labur bat",
@@ -786,7 +775,7 @@ module.exports = {
     parliamentPopulation: "Biztanleria",
     parliamentThType: "Mota",
     parliamentThInPower: "Agintean",
-    parliamentThPresented: "KANDIDATURAK",
+    parliamentThPresented: "HAUTAGAITZAK",
     parliamentNoLeaders: "Oraindik ez dago liderrik.",
     typeParliament: "PARLAMENTU",
     typeParliamentCandidature: "Parlamentu · Hautagaitza",
@@ -823,7 +812,6 @@ module.exports = {
     parliamentProposalVoteStatusLabel: "Bozketa egoera",
     parliamentProposalOnTrackYes: "Atalasea lortuta",
     parliamentProposalOnTrackNo: "Atalasearen azpitik",
-    //courts
     courtsTitle: "Auzitegiak",
     courtsDescription: "Aztertu gatazkak konpontzeko eta justizia kolektiboa kudeatzeko modu desberdinak.",
     courtsFilterCases: "KASUAK",
@@ -848,9 +836,6 @@ module.exports = {
     courtsJudgeIdPh: "Oasis ID (@...) edo Biztanle izena",
     courtsNominateBtn: "Proposatu",
     courtsJudge: "Epailea",
-    courtsEvidenceFileLabel: "Froga fitxategia (irudia, audioa, bideoa edo PDF)",
-    courtsCaseMediators: "Bitartekariak",
-    courtsMediatorsLabel: "Bitartekariak",
     courtsAddEvidence: "Frogak gehitu",
     courtsEvidenceText: "Testua",
     courtsEvidenceLink: "Esteka",
@@ -878,10 +863,11 @@ module.exports = {
     courtsThJudge: "Epailea",
     courtsThSupports: "Babesak",
     courtsThDate: "Data",
-    courtsThVote: "Bozkatu",
+    courtsThVote: "Botoa",
     courtsNoNominations: "Oraindik ez dago izendapenik.",
     courtsAccuser: "Akusatzailea",
     courtsRespondent: "Erantzulea",
+    courtsRespondentInvalid: "SSB ID baliozkoa izan behar da (@...ed25519)",
     courtsThStatus: "Egoera",
     courtsThAnswerBy: "Erantzuteko epea",
     courtsThEvidenceBy: "Frogak aurkezteko epea",
@@ -953,7 +939,6 @@ module.exports = {
     courtsRoleDictator: "Diktadorea",
     courtsAssignJudgeTitle: "Epailea aukeratu",
     courtsAssignJudgeBtn: "Epailea aukeratu",
-    //trending
     trendingTitle: "Pil-pilean",
     exploreTrending: "Aurkitu pil-pileko edukia zure sarean.",
     ALLButton: "GUZTIAK",
@@ -999,7 +984,6 @@ module.exports = {
     trendingAuthor: "Nork",
     trendingCreatedAtLabel: "Noiz",
     trendingTotalCount: "Kopurua Guztira",
-    //tasks
     tasksTitle: "Atazak",
     tasksDescription: "Aurkitu eta kudeatu atazak",
     taskTitleLabel: "Izenburua",
@@ -1055,7 +1039,6 @@ module.exports = {
     notasks: "Atazarik ez.",
     noLocation: "Ez da kokapenik zehaztu",
     taskSetStatus: "Egoera ezarri",
-    //events
     eventTitle: "Ekitaldiak",
     eventDateLabel: "Data",
     eventsTitle: "Ekitaldiak",
@@ -1123,7 +1106,6 @@ module.exports = {
     eventUnattended: "Ez naiz bertaratzen",
     eventStatusOpen: "Irekia",
     eventStatusClosed: "Itxita",
-    //tags 
     tagsTitle: "Etiketak",
     tagsDescription: "Aurkitu eta kudeatu taxonomia ereduak zure sarean.",
     tagsAllSectionTitle: "Etiketak",
@@ -1135,13 +1117,13 @@ module.exports = {
     tagsNoItems: "Etiketarik ez.",
     tagsTableHeaderTag: "ETIKETA/LOTURA:",
     tagsTableHeaderCount: "KONTAGAILUA:",
-    //transfers
     transfersTitle: "Transferentziak",
     transfersDescription: "Aurkitu eta kudeatu transferentziak zure sarean.",
     transfersFrom: "Nork",
     transfersTo: "Nori",
     transfersFilterAll: "GUZTIAK",
     transfersFilterMine: "NEUREAK",
+    transfersFilterUBI: "RBU",
     transfersFilterMarket: "MERKATUA",
     transfersFilterTop: "GORENAK",
     transfersFilterPending: "ZAIN",
@@ -1166,6 +1148,7 @@ module.exports = {
     transfersConfirmButton: "Berretsi Transferentzia",
     transfersNoItems: "Transferentziarik ez.",
     transfersMineSectionTitle: "Zeure Transferentziak",
+    transfersUBISectionTitle: "RBU Transferentziak",
     transfersMarketSectionTitle: "Merkatuko Transferentziak",
     transfersTopSectionTitle: "Transferentzia Gorenak",
     transfersPendingSectionTitle: "Transferentziak Zain",
@@ -1192,8 +1175,7 @@ module.exports = {
     transfersExpiredBadge: "IRAUNGITA",
     transfersUpdatedAt: "Eguneratua",
     transfersNoMatch: "Ez dago bilaketarekin bat datorren transferentziarik.",
-    //votations (voting/polls)
-    votationsTitle: "Bozketak",
+    votationsTitle: "Bozkatzeak",
     votationsDescription: "Aurkitu eta kudeatu bozketak zure sarean.",
     voteMineSectionTitle: "Zeure Bozketak",
     voteCreateSectionTitle: "Sortu Bozketa",
@@ -1249,7 +1231,6 @@ module.exports = {
     voteNewCommentPlaceholder: 'Idatzi hemen zure iruzkina…',
     voteNewCommentButton: 'Bidali iruzkina',
     voteNewCommentLabel: 'Gehitu iruzkina',
-    // CV
     cvTitle: "CV",
     cvLabel: "Curriculum Vitae (CV)",
     cvEditSectionTitle: "Editatu CV-a",
@@ -1282,19 +1263,17 @@ module.exports = {
     cvAvailabilityView: "Eskuragarritasuna",
     cvUpdateButton: "Eguneratu",
     cvCreateButton: "Sortu CV-a",
-    cvContactLabel: "Harremana",
+    cvContactLabel: "Kontaktua",
     cvCreatedAt: "Noiz sortua",
     cvUpdatedAt: "Noiz eguneratua",
     cvEditButton: "Eguneratu",
     cvDeleteButton: "Ezabatu",
     cvNoCV: "CV-rik ez.",
-    // blog/post,
     blogSubject: "Gaia",
     blogMessage: "Mezua",
     blogImage: "Multimedia igo (max: 50MB)",
     blogPublish: "Aurrebista",
     noPopularMessages: "Pil-pileko mezurike ez, oraindik",
-    // forum
     forumTitle: "Foroak",
     forumCategoryLabel: "Kategoria",
     forumTitleLabel: "Izenburua",
@@ -1335,7 +1314,6 @@ module.exports = {
     forumCatPRIVACY: "Pribatutasuna",
     forumCatCYBERWARFARE: "Zibergerra",
     forumCatSURVIVALISM: "Biziraupena",
-    //images
     imageTitle: "Irudiak",
     imageDescription: "Arakatu eta kudeatu zure sareko irudi-edukia.",
     imagePluginTitle: "Izenburua",
@@ -1379,8 +1357,11 @@ module.exports = {
     imageMessageAuthorButton: "Mezua",
     imageUpdatedAt: "Eguneratua",
     imageNoMatch: "Ez dago zure bilaketarekin bat datorren irudirik.",
-    //feed
     feedTitle:        "Jarioa",
+    feedDetailTitle: "Feed",
+    feedOpenDiscussion: "Eztabaida ireki",
+    feedPostComment: "Iruzkina argitaratu",
+    noComments: "Oraindik iruzkinik ez",
     createFeedTitle:  "Sortu Jarioa",
     createFeedButton: "Bidali Jarioa!",
     feedPlaceholder:  "Zer berri? (max 280 characters)",
@@ -1396,10 +1377,8 @@ module.exports = {
     author:           "Nork",
     createdAtLabel:   "Noiz",
     FeedshareYourOpinions: "Aurkitu eta partekatu testu laburrak zure sarean.",
-    //feed
     refeedButton: "Realimentatu",
     alreadyRefeeded:  "Berjaiotu duzu jada.",
-    //activity
     activityTitle:        "Jarduera",
     yourActivity:         "Zure jarduera",
     globalActivity:       "Jarduera globala",
@@ -1526,7 +1505,6 @@ module.exports = {
     parliamentLawEnacted:      "Onartutako eguna",
     parliamentLawVotes:        "Botoak",
     createdAt:                 "Sortze-data",
-    //reports
     reportsTitle: "Txostenak",
     reportsDescription: "Kudeatu eta jarraitu arazo, akats, gehiegikeri eta eduki-abisuei buruzko txostena zure sarean.",
     reportsFilterAll: "GUZTIAK",
@@ -1622,7 +1600,7 @@ module.exports = {
     reportsEvidenceLinksPlaceholder: 'Itsatsi estekak, mezu ID-ak, pantaila-argazkiak (bada), etab.',
     reportsContentLocationLabel: 'Non dago edukia',
     reportsContentLocationPlaceholder: 'Esteka, ID, kanala, haria, egilea, etab.',
-    reportsWhyInappropriateLabel: 'Zergatik da desegokia',
+    reportsWhyInappropriateLabel: 'Zergatik da desegokia?',
     reportsWhyInappropriatePlaceholder: 'Azaldu arrazoia eta eragina.',
     reportsRequestedActionLabel: 'Eskatutako ekintza',
     reportsRequestedActionPlaceholder: 'Kendu, ezkutatu, etiketatu, ohartarazi, etab.',
@@ -1643,7 +1621,7 @@ module.exports = {
     tribeFilterMembership: "BAZKIDETZA",
     tribeFilterRecent: "BERRIAK",
     tribeFilterLarp: "L.A.R.P.",
-    tribeFilterTop: "GORENAK",
+    tribeFilterTop: "TOP",
     tribeFilterSubtribes: "AZPI-TRIBUAK",
     tribeFilterGallery: "GALERIA",
     tribeMainTribeLabel: "TRIBU NAGUSIA",
@@ -1844,7 +1822,7 @@ module.exports = {
     tribeTrendingPeriodDay: "Gaur",
     tribeTrendingPeriodWeek: "Aste Honetan",
     tribeTrendingPeriodAll: "Dena",
-    tribeTrendingEngagement: "interakzioa",
+    tribeTrendingEngagement: "parte-hartzea",
     tribeOpinionsEmpty: "Ez dago iritzirik oraindik.",
     tribeOpinionsCast: "Bozkatu",
     tribeOpinionsRankings: "Sailkapenak",
@@ -1901,7 +1879,7 @@ module.exports = {
     agendareportCategory: "Kategoria",
     agendareportSeverity: "Larritasuna",
     agendareportStatus: "Egoera",
-    agendareportDescription: "Description",
+    agendareportDescription: "Deskribapena",
     agendaTaskEnd: "Amaiera Data",
     agendaTaskPriority: "Lehentasuna",
     agendaTransferFrom: "Nork",
@@ -1914,7 +1892,6 @@ module.exports = {
     agendaTransferConcept: "Kontzeptua",
     agendaTransferAmount: "Kopurua",
     agendaTransferDeadline: "Epemuga",  
-    //opinions
     opinionsTitle: "Iritziak",
     shareYourOpinions: "Deskubritu eta bozkatu zure sareko iritziak.",
     author: "Egilea",
@@ -1929,7 +1906,7 @@ module.exports = {
     necessaryButton: "BEHARREZKOA",
     funnyButton: "BARREGARRIA",
     disgustingButton: "LOREZTUGARRIA",
-    sensibleButton: "SENDEKOA",
+    sensibleButton: "SENTIKORRA",
     propagandaButton: "PROPAGANDA",
     adultOnlyButton: "ADULTO BAKARRIK",
     boringButton: "NEKAGARRIA",
@@ -1959,7 +1936,7 @@ module.exports = {
     spamButton: "SPAM",
     trollButton: "TROLL",
     nsfwButton: "NSFW",
-    violentButton: "BIOLENTOA",
+    violentButton: "BORTITZA",
     toxicButton: "TOXIKOA",
     harassmentButton: "BAZTERKETA",
     hateButton: "ARRETA",
@@ -1990,7 +1967,7 @@ module.exports = {
     voteConfusing: "Konfusio",
     voteTroll: "Troll",
     voteNsfw: "NSFW",
-    voteViolent: "Indarkeria",
+    voteViolent: "Bortitza",
     voteToxic: "Toxikoa",
     voteHarassment: "Jazarpena",
     voteHate: "Arraza",
@@ -2002,7 +1979,6 @@ module.exports = {
     voteCreative: "Sortzailea",
     voteSpam: "Spam",
     voteAdultOnly: "Ados bakarrik",
-    //inbox
     publishBlog: "Bloga argitaratu",
     privateMessage: "MP",
     pmSendTitle: "Mezu Pribatuak",
@@ -2050,8 +2026,6 @@ module.exports = {
     inboxProjectPledgedTitle: "Ekarpen berria zure proiektuan",
     pmHasPledged: "ekarpena egin du",
     pmToYourProject: "zure proiektura",
-    //blockexplorer
-    blockchain: "BlockExplorer",
     blockchainTitle: 'BlockExplorer',
     blockchainDescription: 'Blokeak aztertu eta ikusgaitu blockchain-ean.',
     blockchainNoBlocks: 'Ez da blokeik aurkitu blockchain-ean.',
@@ -2070,8 +2044,7 @@ module.exports = {
     blockchainBlockDetails: 'Hautatutako blokearen xehetasunak',
     blockchainBack: 'Itzuli blokearen azterkira',
     blockchainContentDeleted: "Edukia ezabatu egin da",
-    visitContent: 'Bisitatu Edukia',
-    // banking
+    visitContent: 'Edukia bisitatu',
     banking: 'Banking',
     bankingTitle: 'Banking',
     bankingDescription: 'Aztertu ECOin-en egungo balioa eta dagokion RBU esleipena, astero banatzen dena parte-hartzearen eta konfiantzaren arabera.',
@@ -2083,6 +2056,8 @@ module.exports = {
     bankBack: 'Itzuli Banking-era',
     bankViewTx: 'Tx ikusi',
     bankClaimNow: 'Esleitu',
+    bankClaimUBI: 'RBU eskatu!',
+    bankNoPendingUBI: 'Ez dago RBU esleipen zain garai honetan.',
     bankPubBalance: 'PUB saldoa',
     bankEpoch: 'Epea',
     bankPool: 'Funtsa (epe honetan)',
@@ -2132,11 +2107,75 @@ module.exports = {
     bankRemoveMyAddress: 'Nire helbidea kendu',
     bankNotRemovableOasis: 'Oasis helbideak ezin dira lokalki kendu',
     bankingUserEngagementScore: "KARMA puntuazioa",
-    bankingFutureUBI: "RBUren Estimatutako Esleipena",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "Dendak",
+    shopDescription: "Sareko dendak aurkitu eta kudeatu.",
+    shopTitle: "Denda",
+    shopFilterAll: "GUZTIAK",
+    shopFilterMine: "NIREAK",
+    shopFilterRecent: "BERRIAK",
+    shopFilterTop: "TOP",
+    shopFilterProducts: "PRODUKTUAK",
+    shopFilterPrices: "PREZIOAK",
+    shopFilterFavorites: "GOGOKOAK",
+    shopUpload: "Denda sortu",
+    shopAllSectionTitle: "Denda guztiak",
+    shopMineSectionTitle: "Nire Dendak",
+    shopRecentSectionTitle: "Denda Berriak",
+    shopTopSectionTitle: "Top Dendak",
+    shopProductsSectionTitle: "Top Produktuak",
+    shopPricesSectionTitle: "Prezio Arabera Produktuak",
+    shopFavoritesSectionTitle: "Gogokoak",
+    shopCreateSectionTitle: "Denda sortu",
+    shopUpdateSectionTitle: "Denda eguneratu",
+    shopCreate: "Sortu",
+    shopUpdate: "Eguneratu",
+    shopDelete: "Ezabatu",
+    shopAddFavorite: "Gogoko gehitu",
+    shopRemoveFavorite: "Gogoko kendu",
+    shopOpen: "IREKIA",
+    shopClosed: "ITXIA",
+    shopOpenShop: "Denda ireki",
+    shopCloseShop: "Denda itxi",
+    shopProducts: "Produktuak",
+    shopProductAdd: "Produktua gehitu",
+    shopProductTitle: "Produktua",
+    shopProductUpdate: "Produktua eguneratu",
+    shopProductPrice: "Prezioa (ECO)",
+    shopProductStock: "Stock",
+    shopProductUntitled: "Izengabeko produktua",
+    shopUntitled: "Izengabeko denda",
+    shopNoItems: "Ez da dendarik aurkitu.",
+    shopNoProducts: "Oraindik produkturik ez.",
+    shopOutOfStock: "Agortuta",
+    shopBuy: "Erosi",
+    shopBackToShop: "Dendara itzuli",
+    shopShareUrl: "Partekatzeko URLa",
+    shopVisitShop: "DENDA BISITATU",
+    shopStatus: "EGOERA",
+    shopCreatedAt: "SORTUA",
+    shopSearchPlaceholder: "Dendak bilatu...",
+    shopUrl: "URL",
+    shopLocation: "Kokapena",
+    shopTags: "Etiketak",
+    shopVisibility: "Ikusgarritasuna",
+    shopImage: "Irudia",
+    shopShortDescription: "Deskribapen Laburra",
+    shopShortDescriptionPlaceholder: "Deskribapen laburra txarteletarako (gehienez 160 karaktere)",
+    shopTitlePlaceholder: "Zure dendaren izena",
+    shopDescriptionPlaceholder: "Zure dendaren deskribapen zehatza",
+    shopUrlPlaceholder: "https://zure-denda-url.com",
+    shopLocationPlaceholder: "Hiria, Herrialdea",
+    shopTagsPlaceholder: "tag1, tag2, tag3",
+    mapTitlePlaceholder: "Maparen titulua",
+    shopProductFeatured: "Produktu Nabarmendua",
+    shopSendToMarket: "Send to Market",
+    typeShop: "DENDA",
+    typeShopProduct: "DENDA PRODUKTUA",
     bankExchange: 'Trukea',
     bankExchangeCurrentValue: 'ECOin Balioa (1h)',
     bankTotalSupply: 'ECOin Hornidura Guztizkoa',
-    bankEcoinHours: 'ECOin Oreka Denboran',
+    bankEcoinHours: 'ECOin Orduak',
     bankHoursOfWork: 'lan orduak',
     bankExchangeNoData: 'Ez dago daturik eskuragarri',
     bankExchangeIndex: 'ECOin Balioa (1h)',
@@ -2145,7 +2184,6 @@ module.exports = {
     bankingSyncStatus: 'ECOin Egoera',
     bankingSyncStatusSynced: 'Sinkronizatuta',
     bankingSyncStatusOutdated: 'Desegonetik',
-    //stats
     statsTitle: 'Estatistikak',
     statistics: "Estatistikak",
     statsInhabitant: "Bizilagunaren estatistikak",
@@ -2195,6 +2233,9 @@ module.exports = {
     statsAudio: "Audioak",
     statsVideo: "Bideoak",
     statsDocument: "Dokumentuak",
+    statsMap: "Mapak",
+    statsShop: "Dendak",
+    statsShopProduct: "Denda produktuak",
     statsTransfer: "Transferentziak",
     statsAiExchange: "IA",
     statsPUBs: 'PUBack',
@@ -2261,7 +2302,6 @@ module.exports = {
     statsCourtsSettlementAccepted: "Onartutako akordioak",
     statsCourtsNomination: "Epaileen izendapenak",
     statsCourtsNominationVote: "Izendapenen botoak",
-    //IA
     ai: "IA",
     aiTitle: "IA",
     aiDescription: "Zure saretik ikasten duen '42' izeneko Adimen Artifizial Kolektibo (AIA) bat.",
@@ -2293,7 +2333,6 @@ module.exports = {
     aiApproveCustomTrain: "Erantzun pertsonalizatu hau erabiliz trebatu",
     aiCustomAnswerPlaceholder: "Idatzi zure erantzun pertsonalizatua…",
     statsAIExchanges: "Ereduen trukea",
-    //market
     marketMineSectionTitle: "Zure artikuluak",
     marketCreateSectionTitle: "Artikulua sortu",
     marketUpdateSectionTitle: "Eguneratu",
@@ -2320,6 +2359,7 @@ module.exports = {
     marketItemDescription: "Deskribapena",
     marketItemDescriptionPlaceholder: "Deskribatu saltzen ari zaren artikulua",
     marketItemStatus: "Egoera",
+    marketShopLabel: "Denda",
     marketItemCondition: "Egoera (kalitatea)",
     marketItemPrice: "Prezioa",
     marketItemTags: "Etiketak",
@@ -2356,7 +2396,6 @@ module.exports = {
     marketAuctionEnded: "Amaituta",
     marketMyBidBadge: "Puja egin duzu",
     marketNoItemsMatch: "Ez dago zure bilaketarekin bat datorren artikulurik.",
-    //jobs
     jobsTitle: "Lanak",
     jobsDescription: "Aurki eta kudeatu zure sarean lan eskaintzak.",
     jobsFilterRecent: "BERANDUENAK",
@@ -2417,8 +2456,6 @@ module.exports = {
     viewDetailsButton: "Ikusi Xehetasunak",
     noJobsFound: "Ez daude lan eskaintzak.",
     jobAuthor: "Egilea",
-    jobTypeEmployee: "Enplegatu",
-    jobTypeFreelancer: "Lanikide",
     jobTimePartial: "Partziala",
     jobTimeComplete: "Osotua",
     jobsDeleteButton: "EZABATU",
@@ -2449,7 +2486,6 @@ module.exports = {
     jobsTagsLabel: "Etiketak",
     jobsTagsPlaceholder: "etiketa1, etiketa2, etiketa3",
     noJobsMatch: "Ez dago bilaketarekin bat datorren lan-eskaintzarik.",
-    //projects
     projectsTitle: "Proiektuak",
     projectsDescription: "Sortu, finantzatu eta jarraitu komunitateak gidatutako proiektuak zure sarean.",
     projectCreateProject: "Proiektu Bat Sortu",
@@ -2555,11 +2591,9 @@ module.exports = {
     projectPledgeAmount: "Kantitatea",
     projectSelectMilestoneOrBounty: "Hegoa edo Saria hautatu",
     projectPledgeButton: "Ekimen egin",
-    //footer
     footerLicense: "GPLv3",
     footerPackage: "Paketea",
     footerVersion: "Bertsioa",
-    //modules
     modulesModuleName: "Izena",
     modulesModuleDescription: "Deskribapena",
     modulesModuleStatus: "Egoera",
@@ -2606,9 +2640,11 @@ module.exports = {
     modulesTasksDescription: "Atazak aurkitu eta kudeatzeko modulua.",
     modulesMarketLabel: "Merkatua",
     modulesMarketDescription: "Ondasun edo zerbitzuak trukatzeko modulua.",
+    modulesShopsLabel: "Dendak",
+    modulesShopsDescription: "Dendak kudeatzeko eta aurkitzeko modulua.",
     modulesTribesLabel: "Tribuak",
     modulesTribesDescription: "Tribuak (taldeak) esploratu edo sortzeko modulua.",
-    modulesVotationsLabel: "Bozketak",
+    modulesVotationsLabel: "Bozkatzeak",
     modulesVotationsDescription: "Bozketak aurkitu eta kudeatzeko modulua.", 
     modulesParliamentLabel: "Legebiltzarra",
     modulesParliamentDescription: "Gobernuak hautatu eta legeak bozkatzeko modulua.",
@@ -2649,7 +2685,7 @@ module.exports = {
     connectAndFollow: "Konektatu",
     deviceSourceLabel: "Gailua",
     modulesPresetTitle: "Konfigurazio Arruntak",
-    modulesPreset_minimal: "Minimoa",
+    modulesPreset_minimal: "Gutxienekoa",
     modulesPreset_basic: "Oinarrizkoa",
     modulesPreset_social: "Soziala",
     modulesPreset_economy: "Ekonomia",
@@ -2662,6 +2698,64 @@ module.exports = {
     dominantOpinionLabel: "Iritzi nagusia",
     uploadMedia: "Multimedia igo (max: 50MB)",
 
-     //END
+    reportsWhyInappropriateLabel: "Zergatik da desegokia?",
+    visitContent: "Edukia bisitatu",
+    bankEcoinHours: "ECOin Orduak",
+    mapsLabel: "Mapak",
+    mapTitle: "Mapak",
+    mapDescription: "Esploratu eta kudeatu lineaz kanpoko mapak zure sarean.",
+    mapMineSectionTitle: "Zure Mapak",
+    mapCreateSectionTitle: "Mapa Sortu",
+    mapUpdateSectionTitle: "Mapa Eguneratu",
+    mapAllSectionTitle: "Mapak",
+    mapRecentSectionTitle: "Azken Mapak",
+    mapFavoritesSectionTitle: "Gogokoak",
+    mapFilterAll: "DENAK",
+    mapFilterMine: "NIREAK",
+    mapFilterRecent: "AZKENAK",
+    mapFilterFavorites: "GOGOKOAK",
+    mapUploadButton: "Mapa Sortu",
+    mapCreateButton: "Mapa Sortu",
+    mapUpdateButton: "Eguneratu",
+    mapDeleteButton: "Ezabatu",
+    mapAddFavoriteButton: "Gogokoetara gehitu",
+    mapRemoveFavoriteButton: "Gogokoetatik kendu",
+    mapLatLabel: "Latitudea",
+    mapLatPlaceholder: "adib. 43.2630",
+    mapLngLabel: "Longitudea",
+    mapLngPlaceholder: "adib. -2.9350",
+    mapDescriptionLabel: "Deskribapena",
+    mapDescriptionPlaceholder: "Deskribatu mapa edo kokapena...",
+    mapTypeLabel: "Mapa mota",
+    mapTypeSingle: "BAKARRA (hasierako kokapena soilik)",
+    mapTypeOpen: "IREKIA (edonork gehitu ditzake markatzaileak)",
+    mapTypeClosed: "ITXIA (sortzaileak soilik gehitzen ditu markatzaileak)",
+    mapTagsLabel: "Etiketak",
+    mapTagsPlaceholder: "Sartu etiketak komaz bereizita",
+    mapUrlLabel: "Maparen URLa",
+    mapPickCoordLabel: "Edo hautatu kokapen bat sarean:",
+    mapMarkersLabel: "markatzaileak",
+    mapMarkersTitle: "Markatzaileak",
+    mapMarkerDefault: "Markatzailea",
+    mapMarkerLatLabel: "Markatzailearen latitudea",
+    mapMarkerLngLabel: "Markatzailearen longitudea",
+    mapMarkerLabelField: "Markatzailearen etiketa",
+    mapMarkerLabelPlaceholder: "Deskribatu markatzaile hau...",
+    markerImageLabel: "Markatzailearen Irudia",
+    mapAddMarkerTitle: "Markatzailea Gehitu",
+    mapAddMarkerButton: "Markatzailea Gehitu",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "Zoom aplikatu",
+    mapSearchPlaceholder: "Bilatu deskribapena, etiketak, egilea...",
+    mapSearchButton: "Bilatu",
+    mapUpdatedAt: "Eguneratua",
+    mapNoMatch: "Ez da maparik aurkitu zure bilaketarekin.",
+    noMaps: "Ez dago maparik eskuragarri.",
+    mapLocationTitle: "Kokapena",
+    mapVisitLabel: "Mapa bisitatu",
+    typeMap: "MAPAK",
+    typeMapMarker: "MAPA MARKATZAILEA",
+    modulesMapLabel: "Mapak",
+    modulesMapDescription: "Lineaz kanpoko mapak kudeatzeko eta partekatzeko modulua."
   }
 };

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 231 - 135
src/client/assets/translations/oasis_fr.js


+ 154 - 17
src/client/assets/translations/oasis_hi.js

@@ -373,7 +373,18 @@ module.exports = {
     bookmarkLabel: "बुकमार्क",
     tribeLabel: "जनजातियाँ",
     marketLabel: "बाज़ार",
-    cvLabel: "CVs",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "वॉलेट",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
+    cvLabel: "बायोडाटा",
     submit: "जमा करें",
     subjectLabel: "विषय",
     editProfile: "अवतार संपादित करें",
@@ -627,6 +638,7 @@ module.exports = {
     favoritesFilterBookmarks: "बुकमार्क",
     favoritesFilterDocuments: "दस्तावेज़",
     favoritesFilterImages: "छवियाँ",
+    favoritesFilterMaps: "मानचित्र",
     favoritesFilterVideos: "वीडियो",
     favoritesRemoveButton: "पसंदीदा से हटाएँ",
     favoritesNoItems: "अभी तक कोई पसंदीदा नहीं।",
@@ -636,7 +648,7 @@ module.exports = {
     discoverPeople: "अपने नेटवर्क में निवासियों को खोजें।",
     allInhabitantsButton: "सभी",
     contactsButton: "समर्थन",
-    CVsButton: "CVs",
+    CVsButton: "बायोडाटा",
     matchSkills: "कौशल मिलान",
     matchSkillsButton: "कौशल मिलान",
     suggestedButton: "सुझाए गए",
@@ -654,7 +666,7 @@ module.exports = {
     viewAvatar: "अवतार देखें",
     viewCV: "CV देखें",
     suggestedSectionTitle: "सुझाए गए",
-    topkarmaSectionTitle: "शीर्ष कर्म",
+    topkarmaSectionTitle: "शीर्ष कर्म",
     topactivitySectionTitle: "शीर्ष गतिविधि",
     blockedSectionTitle: "ब्लॉक किए गए",
     gallerySectionTitle: "गैलरी",
@@ -666,6 +678,7 @@ module.exports = {
     oasisId: "ID",
     noInhabitantsFound: "अभी तक कोई निवासी नहीं मिला।",
     inhabitantActivityLevel: "गतिविधि स्तर",
+    deviceLabel: "उपकरण",
     parliamentTitle: "संसद",
     parliamentDescription: "शासन के रूपों और सामूहिक प्रबंधन कानूनों का अन्वेषण करें।",
     parliamentFilterGovernment: "सरकार",
@@ -725,7 +738,7 @@ module.exports = {
     parliamentThDate: "प्रस्ताव तिथि",
     parliamentThSlogan: "नारा",
     parliamentThMethod: "पद्धति",
-    parliamentThKarma: "कर्म",
+    parliamentThKarma: "कर्म",
     parliamentThSince: "प्रोफ़ाइल से",
     parliamentThVotes: "प्राप्त मत",
     parliamentThVoteAction: "मत दें",
@@ -1105,6 +1118,7 @@ module.exports = {
     transfersTo: "को",
     transfersFilterAll: "सभी",
     transfersFilterMine: "मेरे",
+    transfersFilterUBI: "UBI",
     transfersFilterMarket: "बाज़ार",
     transfersFilterTop: "शीर्ष",
     transfersFilterPending: "लंबित",
@@ -1114,7 +1128,7 @@ module.exports = {
     transfersCreateButton: "स्थानांतरण बनाएँ",
     transfersUpdateButton: "अपडेट",
     transfersDeleteButton: "हटाएँ",
-    transfersToUser: "Oasis ID",
+    transfersToUser: "ओएसिस ID",
     transfersToUserValidation: "वैध Oasis ID, जैसे @…=.ed25519",
     transfersConcept: "अवधारणा",
     transfersAmount: "राशि",
@@ -1129,6 +1143,7 @@ module.exports = {
     transfersConfirmButton: "स्थानांतरण पुष्टि करें",
     transfersNoItems: "कोई स्थानांतरण नहीं मिला।",
     transfersMineSectionTitle: "आपके स्थानांतरण",
+    transfersUBISectionTitle: "UBI स्थानांतरण",
     transfersMarketSectionTitle: "बाज़ार स्थानांतरण",
     transfersTopSectionTitle: "शीर्ष स्थानांतरण",
     transfersPendingSectionTitle: "लंबित स्थानांतरण",
@@ -1395,7 +1410,7 @@ module.exports = {
     typeEvent:            "कार्यक्रम",
     typeTransfer:         "स्थानांतरण",
     typeTask:             "कार्य",
-    typePixelia:          "PIXELIA",
+    typePixelia:          "पिक्सेलिया",
     typeForum:            "मंच",
     typeReport:           "रिपोर्ट",
     typeFeed:             "फ़ीड",
@@ -1405,7 +1420,7 @@ module.exports = {
     typeBanking:          "बैंकिंग",
     typeBankWallet:       "बैंकिंग/वॉलेट",
     typeBankClaim:        "बैंकिंग/UBI",
-    typeKarmaScore:       "कर्म",
+    typeKarmaScore:       "कर्म",
     typeParliament:       "संसद",
     typeSpread:           "प्रसार",
     typeParliamentCandidature: "संसद · उम्मीदवारी",
@@ -2071,6 +2086,8 @@ module.exports = {
     bankBack: 'बैंकिंग पर वापस जाएँ',
     bankViewTx: 'Tx देखें',
     bankClaimNow: 'अभी दावा करें',
+    bankClaimUBI: 'UBI का दावा करें!',
+    bankNoPendingUBI: 'इस अवधि के लिए कोई लंबित UBI आवंटन नहीं।',
     bankPubBalance: 'PUB शेष',
     bankEpoch: 'युग',
     bankPool: 'पूल (यह युग)',
@@ -2119,7 +2136,71 @@ module.exports = {
     bankMyAddress: 'आपका पता',
     bankRemoveMyAddress: 'मेरा पता हटाएँ',
     bankNotRemovableOasis: 'पते स्थानीय रूप से नहीं हटाए जा सकते',
-    bankingFutureUBI: "अनुमानित UBI आवंटन",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "दुकानें",
+    shopDescription: "नेटवर्क में दुकानें खोजें और प्रबंधित करें।",
+    shopTitle: "दुकान",
+    shopFilterAll: "सभी",
+    shopFilterMine: "मेरी",
+    shopFilterRecent: "हालिया",
+    shopFilterTop: "शीर्ष",
+    shopFilterProducts: "उत्पाद",
+    shopFilterPrices: "मूल्य",
+    shopFilterFavorites: "पसंदीदा",
+    shopUpload: "दुकान बनाएं",
+    shopAllSectionTitle: "सभी दुकानें",
+    shopMineSectionTitle: "मेरी दुकानें",
+    shopRecentSectionTitle: "हालिया दुकानें",
+    shopTopSectionTitle: "शीर्ष दुकानें",
+    shopProductsSectionTitle: "शीर्ष उत्पाद",
+    shopPricesSectionTitle: "मूल्य अनुसार उत्पाद",
+    shopFavoritesSectionTitle: "पसंदीदा",
+    shopCreateSectionTitle: "दुकान बनाएं",
+    shopUpdateSectionTitle: "दुकान अपडेट",
+    shopCreate: "बनाएं",
+    shopUpdate: "अपडेट",
+    shopDelete: "हटाएं",
+    shopAddFavorite: "पसंदीदा जोड़ें",
+    shopRemoveFavorite: "पसंदीदा हटाएं",
+    shopOpen: "खुली",
+    shopClosed: "बंद",
+    shopOpenShop: "दुकान खोलें",
+    shopCloseShop: "दुकान बंद करें",
+    shopProducts: "उत्पाद",
+    shopProductAdd: "उत्पाद जोड़ें",
+    shopProductTitle: "उत्पाद",
+    shopProductUpdate: "उत्पाद अपडेट",
+    shopProductPrice: "मूल्य (ECO)",
+    shopProductStock: "स्टॉक",
+    shopProductUntitled: "बिना शीर्षक उत्पाद",
+    shopUntitled: "बिना शीर्षक दुकान",
+    shopNoItems: "कोई दुकान नहीं मिली।",
+    shopNoProducts: "अभी तक कोई उत्पाद नहीं।",
+    shopOutOfStock: "स्टॉक में नहीं",
+    shopBuy: "खरीदें",
+    shopBackToShop: "दुकान पर वापस",
+    shopShareUrl: "शेयर URL",
+    shopVisitShop: "दुकान देखें",
+    shopStatus: "स्थिति",
+    shopCreatedAt: "बनाया गया",
+    shopSearchPlaceholder: "दुकानें खोजें...",
+    shopUrl: "URL",
+    shopLocation: "स्थान",
+    shopTags: "टैग",
+    shopVisibility: "दृश्यता",
+    shopImage: "छवि",
+    shopShortDescription: "संक्षिप्त विवरण",
+    shopShortDescriptionPlaceholder: "दुकान कार्ड के लिए संक्षिप्त विवरण (अधिकतम 160 अक्षर)",
+    shopTitlePlaceholder: "आपकी दुकान का नाम",
+    shopDescriptionPlaceholder: "आपकी दुकान का विस्तृत विवरण",
+    shopUrlPlaceholder: "https://aapki-dukan-url.com",
+    shopLocationPlaceholder: "शहर, देश",
+    shopTagsPlaceholder: "टैग1, टैग2, टैग3",
+    mapTitlePlaceholder: "मानचित्र शीर्षक",
+    shopProductFeatured: "विशेष उत्पाद",
+    shopSendToMarket: "Send to Market",
+    typeShop: "दुकान",
+    typeShopProduct: "दुकान उत्पाद",
     bankExchange: 'विनिमय',
     bankExchangeCurrentValue: 'ECOin मूल्य (1h)',
     bankTotalSupply: 'ECOin कुल आपूर्ति',
@@ -2181,6 +2262,9 @@ module.exports = {
     statsAudio: "ऑडियो",
     statsVideo: "वीडियो",
     statsDocument: "दस्तावेज़",
+    statsMap: "मानचित्र",
+    statsShop: "दुकानें",
+    statsShopProduct: "दुकान उत्पाद",
     statsTransfer: "स्थानांतरण",
     statsAiExchange: "AI",
     statsPUBs: 'PUBs',
@@ -2304,6 +2388,7 @@ module.exports = {
     marketItemDescription: "विवरण",
     marketItemDescriptionPlaceholder: "जो आइटम आप बेच रहे हैं उसका वर्णन करें",
     marketItemStatus: "स्थिति",
+    marketShopLabel: "दुकान",
     marketItemCondition: "स्थिति",
     marketItemPrice: "मूल्य",
     marketItemTags: "टैग",
@@ -2348,7 +2433,7 @@ module.exports = {
     jobsFilterRemote: "दूरस्थ",
     jobsFilterOpen: "खुले",
     jobsFilterClosed: "बंद",
-    jobsCV: "CVs",
+    jobsCV: "बायोडाटा",
     jobsCreateJob: "नौकरी बनाएँ",
     jobsRecentTitle: "हाल की नौकरियाँ",
     jobsMineTitle: "आपकी नौकरियाँ",
@@ -2356,7 +2441,7 @@ module.exports = {
     jobsRemoteTitle: "दूरस्थ नौकरियाँ",
     jobsOpenTitle: "खुली नौकरियाँ",
     jobsClosedTitle: "बंद नौकरियाँ",
-    jobsCVTitle: "CVs",
+    jobsCVTitle: "बायोडाटा",
     jobsFilterPresencial: "उपस्थित",
     jobsFilterFreelancer: "फ्रीलांसर",
     jobsFilterEmployee:   "कर्मचारी",
@@ -2584,6 +2669,8 @@ module.exports = {
     modulesTasksDescription: "कार्य खोजने और प्रबंधित करने का मॉड्यूल।",
     modulesMarketLabel: "बाज़ार",
     modulesMarketDescription: "सामान या सेवाओं के आदान-प्रदान का मॉड्यूल।",
+    modulesShopsLabel: "दुकानें",
+    modulesShopsDescription: "दुकानों को प्रबंधित और खोजने के लिए मॉड्यूल।",
     modulesTribesLabel: "जनजातियाँ",
     modulesTribesDescription: "जनजातियाँ (समूह) खोजने या बनाने का मॉड्यूल।",
     modulesVotationsLabel: "मतदान",
@@ -2639,16 +2726,66 @@ module.exports = {
     feedSuccessMsg: "फ़ीड सफलतापूर्वक प्रकाशित!",
     dominantOpinionLabel: "प्रमुख राय",
     uploadMedia: "मीडिया अपलोड करें (अधिकतम-आकार: 50MB)",
-    feedViewDetails: "विवरण देखें",
     feedOpenDiscussion: "चर्चा खोलें",
     feedDetailTitle: "फ़ीड",
     feedPostComment: "टिप्पणी पोस्ट करें",
-    eventFileLabel: "छवि संलग्न करें",
-    taskFileLabel: "छवि संलग्न करें",
-    courtsRespondentRequired: "प्रतिवादी ID आवश्यक है",
     courtsRespondentInvalid: "एक वैध SSB ID होनी चाहिए (@...ed25519)",
-    noComments: "अभी तक कोई टिप्पणी नहीं"
-
-     //END
+    noComments: "अभी तक कोई टिप्पणी नहीं",
+    mapsLabel: "मानचित्र",
+    mapTitle: "मानचित्र",
+    mapDescription: "अपने नेटवर्क में ऑफ़लाइन मानचित्र खोजें और प्रबंधित करें।",
+    mapMineSectionTitle: "आपके मानचित्र",
+    mapCreateSectionTitle: "मानचित्र बनाएं",
+    mapUpdateSectionTitle: "मानचित्र अपडेट करें",
+    mapAllSectionTitle: "मानचित्र",
+    mapRecentSectionTitle: "हाल के मानचित्र",
+    mapFavoritesSectionTitle: "पसंदीदा",
+    mapFilterAll: "सभी",
+    mapFilterMine: "मेरे",
+    mapFilterRecent: "हाल के",
+    mapFilterFavorites: "पसंदीदा",
+    mapUploadButton: "मानचित्र बनाएं",
+    mapCreateButton: "मानचित्र बनाएं",
+    mapUpdateButton: "अपडेट करें",
+    mapDeleteButton: "हटाएं",
+    mapAddFavoriteButton: "पसंदीदा में जोड़ें",
+    mapRemoveFavoriteButton: "पसंदीदा से हटाएं",
+    mapLatLabel: "अक्षांश",
+    mapLatPlaceholder: "उदा. 28.6139",
+    mapLngLabel: "देशांतर",
+    mapLngPlaceholder: "उदा. 77.2090",
+    mapDescriptionLabel: "विवरण",
+    mapDescriptionPlaceholder: "मानचित्र या स्थान का वर्णन करें...",
+    mapTypeLabel: "मानचित्र प्रकार",
+    mapTypeSingle: "एकल (केवल प्रारंभिक स्थान)",
+    mapTypeOpen: "खुला (कोई भी मार्कर जोड़ सकता है)",
+    mapTypeClosed: "बंद (केवल निर्माता मार्कर जोड़ता है)",
+    mapTagsLabel: "टैग",
+    mapTagsPlaceholder: "अल्पविराम से अलग टैग दर्ज करें",
+    mapUrlLabel: "मानचित्र URL",
+    mapPickCoordLabel: "या ग्रिड पर स्थान चुनें:",
+    mapMarkersLabel: "मार्कर",
+    mapMarkersTitle: "मार्कर",
+    mapMarkerDefault: "मार्कर",
+    mapMarkerLatLabel: "मार्कर अक्षांश",
+    mapMarkerLngLabel: "मार्कर देशांतर",
+    mapMarkerLabelField: "मार्कर लेबल",
+    mapMarkerLabelPlaceholder: "इस मार्कर का वर्णन करें...",
+    markerImageLabel: "मार्कर छवि",
+    mapAddMarkerTitle: "मार्कर जोड़ें",
+    mapAddMarkerButton: "मार्कर जोड़ें",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "ज़ूम लागू करें",
+    mapSearchPlaceholder: "विवरण, टैग, लेखक खोजें...",
+    mapSearchButton: "खोजें",
+    mapUpdatedAt: "अपडेट किया गया",
+    mapNoMatch: "आपकी खोज से मेल खाने वाला कोई मानचित्र नहीं।",
+    noMaps: "कोई मानचित्र उपलब्ध नहीं।",
+    mapLocationTitle: "स्थान",
+    mapVisitLabel: "मानचित्र देखें",
+    typeMap: "मानचित्र",
+    typeMapMarker: "मानचित्र मार्कर",
+    modulesMapLabel: "मानचित्र",
+    modulesMapDescription: "ऑफ़लाइन मानचित्रों को प्रबंधित और साझा करने का मॉड्यूल।"
     }
 };

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1901 - 1812
src/client/assets/translations/oasis_it.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1755 - 1666
src/client/assets/translations/oasis_pt.js


+ 151 - 10
src/client/assets/translations/oasis_ru.js

@@ -373,6 +373,17 @@ module.exports = {
     bookmarkLabel: "ЗАКЛАДКИ",
     tribeLabel: "ПЛЕМЕНА",
     marketLabel: "РЫНОК",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "КОШЕЛЬКИ",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "Резюме",
     submit: "Отправить",
     subjectLabel: "Тема",
@@ -627,6 +638,7 @@ module.exports = {
     favoritesFilterBookmarks: "ЗАКЛАДКИ",
     favoritesFilterDocuments: "ДОКУМЕНТЫ",
     favoritesFilterImages: "ИЗОБРАЖЕНИЯ",
+    favoritesFilterMaps: "КАРТЫ",
     favoritesFilterVideos: "ВИДЕО",
     favoritesRemoveButton: "Убрать из избранного",
     favoritesNoItems: "Пока нет избранного.",
@@ -636,7 +648,7 @@ module.exports = {
     discoverPeople: "Находите жителей в вашей сети.",
     allInhabitantsButton: "ВСЕ",
     contactsButton: "ПОДДЕРЖКА",
-    CVsButton: "РЕЗЮМЕ",
+    CVsButton: "Резюме",
     matchSkills: "Совпадение навыков",
     matchSkillsButton: "СОВПАДЕНИЕ НАВЫКОВ",
     suggestedButton: "РЕКОМЕНДОВАННЫЕ",
@@ -654,7 +666,7 @@ module.exports = {
     viewAvatar: "Смотреть аватар",
     viewCV: "Смотреть резюме",
     suggestedSectionTitle: "Рекомендованные",
-    topkarmaSectionTitle: "Лучшая Karma",
+    topkarmaSectionTitle: "Лучшая Карма",
     topactivitySectionTitle: "Наибольшая активность",
     blockedSectionTitle: "Заблокированные",
     gallerySectionTitle: "ГАЛЕРЕЯ",
@@ -725,7 +737,7 @@ module.exports = {
     parliamentThDate: "Дата предложения",
     parliamentThSlogan: "Слоган",
     parliamentThMethod: "Метод",
-    parliamentThKarma: "Karma",
+    parliamentThKarma: "Карма",
     parliamentThSince: "Профиль с",
     parliamentThVotes: "Получено голосов",
     parliamentThVoteAction: "Голосовать",
@@ -1098,6 +1110,7 @@ module.exports = {
     transfersTo: "Кому",
     transfersFilterAll: "ВСЕ",
     transfersFilterMine: "МОИ",
+    transfersFilterUBI: "UBI",
     transfersFilterMarket: "РЫНОК",
     transfersFilterTop: "ЛУЧШИЕ",
     transfersFilterPending: "ОЖИДАЮЩИЕ",
@@ -1122,6 +1135,7 @@ module.exports = {
     transfersConfirmButton: "Подтвердить перевод",
     transfersNoItems: "Переводы не найдены.",
     transfersMineSectionTitle: "Ваши переводы",
+    transfersUBISectionTitle: "UBI переводы",
     transfersMarketSectionTitle: "Рыночные переводы",
     transfersTopSectionTitle: "Лучшие переводы",
     transfersPendingSectionTitle: "Ожидающие переводы",
@@ -1388,7 +1402,7 @@ module.exports = {
     typeEvent: "СОБЫТИЯ",
     typeTransfer: "ПЕРЕВОД",
     typeTask: "ЗАДАЧИ",
-    typePixelia: "PIXELIA",
+    typePixelia: "ПИКСЕЛИ",
     typeForum: "ФОРУМ",
     typeReport: "ОТЧЁТЫ",
     typeFeed: "ЛЕНТА",
@@ -2038,6 +2052,8 @@ module.exports = {
     bankBack: "Назад в Банкинг",
     bankViewTx: "Просмотр транзакции",
     bankClaimNow: "Получить сейчас",
+    bankClaimUBI: "Получить UBI!",
+    bankNoPendingUBI: "Нет ожидающих распределений UBI для этой эпохи.",
     bankPubBalance: "Баланс PUB",
     bankEpoch: "Эпоха",
     bankPool: "Пул (эта эпоха)",
@@ -2084,7 +2100,71 @@ module.exports = {
     bankMyAddress: "Ваш адрес",
     bankRemoveMyAddress: "Удалить мой адрес",
     bankNotRemovableOasis: "Адреса нельзя удалить локально",
-    bankingFutureUBI: "Предполагаемое распределение UBI",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "Магазины",
+    shopDescription: "Откройте и управляйте магазинами в сети.",
+    shopTitle: "Магазин",
+    shopFilterAll: "ВСЕ",
+    shopFilterMine: "МОИ",
+    shopFilterRecent: "НЕДАВНИЕ",
+    shopFilterTop: "ТОП",
+    shopFilterProducts: "ТОВАРЫ",
+    shopFilterPrices: "ЦЕНЫ",
+    shopFilterFavorites: "ИЗБРАННЫЕ",
+    shopUpload: "Создать магазин",
+    shopAllSectionTitle: "Все магазины",
+    shopMineSectionTitle: "Мои магазины",
+    shopRecentSectionTitle: "Недавние магазины",
+    shopTopSectionTitle: "Лучшие магазины",
+    shopProductsSectionTitle: "Лучшие товары",
+    shopPricesSectionTitle: "Товары по цене",
+    shopFavoritesSectionTitle: "Избранные",
+    shopCreateSectionTitle: "Создать магазин",
+    shopUpdateSectionTitle: "Обновить магазин",
+    shopCreate: "Создать",
+    shopUpdate: "Обновить",
+    shopDelete: "Удалить",
+    shopAddFavorite: "Добавить в избранное",
+    shopRemoveFavorite: "Убрать из избранного",
+    shopOpen: "ОТКРЫТ",
+    shopClosed: "ЗАКРЫТ",
+    shopOpenShop: "Открыть магазин",
+    shopCloseShop: "Закрыть магазин",
+    shopProducts: "Товары",
+    shopProductAdd: "Добавить товар",
+    shopProductTitle: "Товар",
+    shopProductUpdate: "Обновить товар",
+    shopProductPrice: "Цена (ECO)",
+    shopProductStock: "Наличие",
+    shopProductUntitled: "Товар без названия",
+    shopUntitled: "Магазин без названия",
+    shopNoItems: "Магазины не найдены.",
+    shopNoProducts: "Товаров пока нет.",
+    shopOutOfStock: "Нет в наличии",
+    shopBuy: "Купить",
+    shopBackToShop: "Назад в магазин",
+    shopShareUrl: "Ссылка для обмена",
+    shopVisitShop: "ПОСЕТИТЬ МАГАЗИН",
+    shopStatus: "СТАТУС",
+    shopCreatedAt: "СОЗДАН",
+    shopSearchPlaceholder: "Поиск магазинов...",
+    shopUrl: "URL",
+    shopLocation: "Местоположение",
+    shopTags: "Теги",
+    shopVisibility: "Видимость",
+    shopImage: "Изображение",
+    shopShortDescription: "Краткое описание",
+    shopShortDescriptionPlaceholder: "Краткое описание для карточек (макс. 160 символов)",
+    shopTitlePlaceholder: "Название вашего магазина",
+    shopDescriptionPlaceholder: "Подробное описание вашего магазина",
+    shopUrlPlaceholder: "https://url-vashego-magazina.ru",
+    shopLocationPlaceholder: "Город, Страна",
+    shopTagsPlaceholder: "тег1, тег2, тег3",
+    mapTitlePlaceholder: "Название карты",
+    shopProductFeatured: "Рекомендуемый товар",
+    shopSendToMarket: "Send to Market",
+    typeShop: "МАГАЗИН",
+    typeShopProduct: "ТОВАР МАГАЗИНА",
     bankExchange: "Обмен",
     bankExchangeCurrentValue: "Стоимость ECOin (1ч)",
     bankTotalSupply: "Общий объём ECOin",
@@ -2144,6 +2224,9 @@ module.exports = {
     statsAudio: "Аудио",
     statsVideo: "Видео",
     statsDocument: "Документы",
+    statsMap: "Карты",
+    statsShop: "Магазины",
+    statsShopProduct: "Товары магазина",
     statsTransfer: "Переводы",
     statsAiExchange: "ИИ",
     statsPUBs: "PUBs",
@@ -2261,6 +2344,7 @@ module.exports = {
     marketItemDescription: "Описание",
     marketItemDescriptionPlaceholder: "Опишите продаваемый товар",
     marketItemStatus: "Статус",
+    marketShopLabel: "Магазин",
     marketItemCondition: "Состояние",
     marketItemPrice: "Цена",
     marketItemTags: "Теги",
@@ -2539,6 +2623,8 @@ module.exports = {
     modulesTasksDescription: "Модуль для открытия и управления задачами.",
     modulesMarketLabel: "Рынок",
     modulesMarketDescription: "Модуль для обмена товарами или услугами.",
+    modulesShopsLabel: "Магазины",
+    modulesShopsDescription: "Модуль для управления и поиска магазинов.",
     modulesTribesLabel: "Племена",
     modulesTribesDescription: "Модуль для исследования или создания племён (групп).",
     modulesVotationsLabel: "Голосования",
@@ -2582,9 +2668,9 @@ module.exports = {
     connectAndFollow: "Подключиться",
     deviceSourceLabel: "Источник устройства",
     modulesPresetTitle: "Общие конфигурации",
-    modulesPreset_minimal: "Минимальная",
+    modulesPreset_minimal: "Минимальный",
     modulesPreset_basic: "Базовая",
-    modulesPreset_social: "Социальная",
+    modulesPreset_social: "Социальный",
     modulesPreset_economy: "Экономическая",
     modulesPreset_full: "Полная",
     statsCarbonFootprintTitle: "Углеродный след",
@@ -2606,8 +2692,63 @@ module.exports = {
     bankAllocId: "ID распределения",
     pmFromLabel: "От:",
     pmToLabel: "Кому:",
-    aiTitle: "ИИ"
-
-     //END
+    aiTitle: "ИИ",
+    deviceLabel: "Устройство",
+    mapsLabel: "Карты",
+    mapTitle: "Карты",
+    mapDescription: "Исследуйте и управляйте офлайн-картами в вашей сети.",
+    mapMineSectionTitle: "Ваши Карты",
+    mapCreateSectionTitle: "Создать Карту",
+    mapUpdateSectionTitle: "Обновить Карту",
+    mapAllSectionTitle: "Карты",
+    mapRecentSectionTitle: "Недавние Карты",
+    mapFavoritesSectionTitle: "Избранное",
+    mapFilterAll: "ВСЕ",
+    mapFilterMine: "МОИ",
+    mapFilterRecent: "НЕДАВНИЕ",
+    mapFilterFavorites: "ИЗБРАННОЕ",
+    mapUploadButton: "Создать Карту",
+    mapCreateButton: "Создать Карту",
+    mapUpdateButton: "Обновить",
+    mapDeleteButton: "Удалить",
+    mapAddFavoriteButton: "В избранное",
+    mapRemoveFavoriteButton: "Из избранного",
+    mapLatLabel: "Широта",
+    mapLatPlaceholder: "напр. 55.7558",
+    mapLngLabel: "Долгота",
+    mapLngPlaceholder: "напр. 37.6173",
+    mapDescriptionLabel: "Описание",
+    mapDescriptionPlaceholder: "Опишите карту или местоположение...",
+    mapTypeLabel: "Тип карты",
+    mapTypeSingle: "ОДИНОЧНЫЙ (только начальное местоположение)",
+    mapTypeOpen: "ОТКРЫТЫЙ (любой может добавлять маркеры)",
+    mapTypeClosed: "ЗАКРЫТЫЙ (только создатель добавляет маркеры)",
+    mapTagsLabel: "Теги",
+    mapTagsPlaceholder: "Введите теги через запятую",
+    mapUrlLabel: "URL карты",
+    mapPickCoordLabel: "Или выберите местоположение на сетке:",
+    mapMarkersLabel: "маркеры",
+    mapMarkersTitle: "Маркеры",
+    mapMarkerDefault: "Маркер",
+    mapMarkerLatLabel: "Широта маркера",
+    mapMarkerLngLabel: "Долгота маркера",
+    mapMarkerLabelField: "Метка маркера",
+    mapMarkerLabelPlaceholder: "Опишите этот маркер...",
+    markerImageLabel: "Изображение Маркера",
+    mapAddMarkerTitle: "Добавить Маркер",
+    mapAddMarkerButton: "Добавить Маркер",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "Применить масштаб",
+    mapSearchPlaceholder: "Поиск по описанию, тегам, автору...",
+    mapSearchButton: "Искать",
+    mapUpdatedAt: "Обновлено",
+    mapNoMatch: "Нет карт, соответствующих вашему запросу.",
+    noMaps: "Нет доступных карт.",
+    mapLocationTitle: "Местоположение",
+    mapVisitLabel: "Посетить карту",
+    typeMap: "КАРТЫ",
+    typeMapMarker: "МАРКЕР КАРТЫ",
+    modulesMapLabel: "Карты",
+    modulesMapDescription: "Модуль для управления и обмена офлайн-картами."
     }
 };

+ 155 - 18
src/client/assets/translations/oasis_zh.js

@@ -271,7 +271,7 @@ module.exports = {
     invitesTribesTitle: "部落",
     invitesTribeInviteCodePlaceholder: "输入部落邀请码",
     invitesTribeJoinButton: "加入部落",
-    invitesPubsTitle: "PUB",
+    invitesPubsTitle: "PUBs",
     invitesPubInviteCodePlaceholder: "输入 PUB 邀请码",
     invitesAcceptInvite: "加入 PUB",
     invitesAcceptedInvites: "已联邦网络",
@@ -374,6 +374,17 @@ module.exports = {
     bookmarkLabel: "书签",
     tribeLabel: "部落",
     marketLabel: "市场",
+    shopLabel: "SHOPS",
+    shopProductLabel: "SHOP PRODUCTS",
+    mapLabel: "MAPS",
+    jobLabel: "JOBS",
+    forumLabel: "FORUMS",
+    projectLabel: "PROJECTS",
+    bankWalletLabel: "钱包",
+    bankClaimLabel: "UBI CLAIMS",
+    voteLabel: "VOTES",
+    contactLabel: "CONTACTS",
+    pubLabel: "PUBS",
     cvLabel: "简历",
     submit: "提交",
     subjectLabel: "主题",
@@ -429,7 +440,7 @@ module.exports = {
     walletTitle: "钱包",
     walletTotalCostLine: ({ totalCost }) => `总费用:ECO ${totalCost}`,
     walletTransactionId: "交易 ID",
-    walletTxId: "交易 ID",
+    walletTxId: "交易ID",
     walletType: "类型",
     walletUser: "用户名",
     walletPass: "密码",
@@ -628,6 +639,7 @@ module.exports = {
     favoritesFilterBookmarks: "书签",
     favoritesFilterDocuments: "文档",
     favoritesFilterImages: "图片",
+    favoritesFilterMaps: "地图",
     favoritesFilterVideos: "视频",
     favoritesRemoveButton: "取消收藏",
     favoritesNoItems: "还没有收藏。",
@@ -655,7 +667,7 @@ module.exports = {
     viewAvatar: "查看头像",
     viewCV: "查看简历",
     suggestedSectionTitle: "推荐",
-    topkarmaSectionTitle: "最高 Karma",
+    topkarmaSectionTitle: "最高因缘值",
     topactivitySectionTitle: "最高活跃度",
     blockedSectionTitle: "已屏蔽",
     gallerySectionTitle: "画廊",
@@ -667,6 +679,7 @@ module.exports = {
     oasisId: "ID",
     noInhabitantsFound: "尚未找到居民。",
     inhabitantActivityLevel: "活跃等级",
+    deviceLabel: "设备",
     parliamentTitle: "议会",
     parliamentDescription: "探索政府形式和集体管理法律。",
     parliamentFilterGovernment: "政府",
@@ -726,7 +739,7 @@ module.exports = {
     parliamentThDate: "提议日期",
     parliamentThSlogan: "口号",
     parliamentThMethod: "方式",
-    parliamentThKarma: "Karma",
+    parliamentThKarma: "因缘值",
     parliamentThSince: "注册时间",
     parliamentThVotes: "获得票数",
     parliamentThVoteAction: "投票",
@@ -1058,7 +1071,7 @@ module.exports = {
     eventBy: "作者",
     noevents: "没有可用的活动。",
     eventDate: "日期",
-    eventDateFormat: "DD:MM:YYYY HH:mm",
+    eventDateFormat: "YYYY年MM月DD日 HH:mm",
     eventAttendButton: "参加活动",
     eventUnattendButton: "取消参加",
     eventCreatedBy: "创建者",
@@ -1106,6 +1119,7 @@ module.exports = {
     transfersTo: "接收人",
     transfersFilterAll: "全部",
     transfersFilterMine: "我的",
+    transfersFilterUBI: "UBI",
     transfersFilterMarket: "市场",
     transfersFilterTop: "热门",
     transfersFilterPending: "待处理",
@@ -1130,6 +1144,7 @@ module.exports = {
     transfersConfirmButton: "确认转账",
     transfersNoItems: "未找到转账。",
     transfersMineSectionTitle: "你的转账",
+    transfersUBISectionTitle: "UBI 转账",
     transfersMarketSectionTitle: "市场转账",
     transfersTopSectionTitle: "热门转账",
     transfersPendingSectionTitle: "待处理转账",
@@ -1307,14 +1322,14 @@ module.exports = {
     imageTopSectionTitle: "热门图片",
     imageFavoritesSectionTitle: "收藏",
     imageGallerySectionTitle: "画廊",
-    imageMemeSectionTitle: "表情包",
+    imageMemeSectionTitle: "梗图",
     imageFilterAll: "全部",
     imageFilterMine: "我的",
     imageFilterRecent: "最近",
     imageFilterTop: "热门",
     imageFilterFavorites: "收藏",
     imageFilterGallery: "画廊",
-    imageFilterMeme: "表情包",
+    imageFilterMeme: "梗图",
     imageCreateButton: "上传图片",
     imageUpdateButton: "更新",
     imageDeleteButton: "删除",
@@ -1396,7 +1411,7 @@ module.exports = {
     typeEvent:            "活动",
     typeTransfer:         "转账",
     typeTask:             "任务",
-    typePixelia:          "PIXELIA",
+    typePixelia:          "像素画",
     typeForum:            "论坛",
     typeReport:           "报告",
     typeFeed:             "动态",
@@ -1406,7 +1421,7 @@ module.exports = {
     typeBanking:          "银行",
     typeBankWallet:       "银行/钱包",
     typeBankClaim:        "银行/UBI",
-    typeKarmaScore:       "KARMA",
+    typeKarmaScore:       "因缘值",
     typeParliament:       "议会",
     typeSpread:           "传播",
     typeParliamentCandidature: "议会 · 候选",
@@ -2072,6 +2087,8 @@ module.exports = {
     bankBack: '返回银行',
     bankViewTx: '查看交易',
     bankClaimNow: '立即领取',
+    bankClaimUBI: '领取 UBI!',
+    bankNoPendingUBI: '本期无待领取的 UBI 分配。',
     bankPubBalance: 'PUB 余额',
     bankEpoch: '纪元',
     bankPool: '资金池(本纪元)',
@@ -2120,7 +2137,71 @@ module.exports = {
     bankMyAddress: '你的地址',
     bankRemoveMyAddress: '移除我的地址',
     bankNotRemovableOasis: '无法在本地移除地址',
-    bankingFutureUBI: "预估 UBI 分配",
+    bankingFutureUBI: "UBI",
+    shopsTitle: "商店",
+    shopDescription: "发现和管理网络中的商店。",
+    shopTitle: "商店",
+    shopFilterAll: "全部",
+    shopFilterMine: "我的",
+    shopFilterRecent: "最近",
+    shopFilterTop: "热门",
+    shopFilterProducts: "产品",
+    shopFilterPrices: "价格",
+    shopFilterFavorites: "收藏",
+    shopUpload: "创建商店",
+    shopAllSectionTitle: "所有商店",
+    shopMineSectionTitle: "我的商店",
+    shopRecentSectionTitle: "最近商店",
+    shopTopSectionTitle: "热门商店",
+    shopProductsSectionTitle: "热门产品",
+    shopPricesSectionTitle: "按价格排列的产品",
+    shopFavoritesSectionTitle: "收藏",
+    shopCreateSectionTitle: "创建商店",
+    shopUpdateSectionTitle: "更新商店",
+    shopCreate: "创建",
+    shopUpdate: "更新",
+    shopDelete: "删除",
+    shopAddFavorite: "添加收藏",
+    shopRemoveFavorite: "取消收藏",
+    shopOpen: "开放",
+    shopClosed: "关闭",
+    shopOpenShop: "开放商店",
+    shopCloseShop: "关闭商店",
+    shopProducts: "产品",
+    shopProductAdd: "添加产品",
+    shopProductTitle: "产品",
+    shopProductUpdate: "更新产品",
+    shopProductPrice: "价格 (ECO)",
+    shopProductStock: "库存",
+    shopProductUntitled: "无标题产品",
+    shopUntitled: "无标题商店",
+    shopNoItems: "未找到商店。",
+    shopNoProducts: "暂无产品。",
+    shopOutOfStock: "已售罄",
+    shopBuy: "购买",
+    shopBackToShop: "返回商店",
+    shopShareUrl: "分享链接",
+    shopVisitShop: "访问商店",
+    shopStatus: "状态",
+    shopCreatedAt: "创建时间",
+    shopSearchPlaceholder: "搜索商店...",
+    shopUrl: "网址",
+    shopLocation: "位置",
+    shopTags: "标签",
+    shopVisibility: "可见性",
+    shopImage: "图片",
+    shopShortDescription: "简短描述",
+    shopShortDescriptionPlaceholder: "店铺卡片简短描述(最多160个字符)",
+    shopTitlePlaceholder: "您的店铺名称",
+    shopDescriptionPlaceholder: "您的店铺详细描述",
+    shopUrlPlaceholder: "https://您的店铺网址.com",
+    shopLocationPlaceholder: "城市,国家",
+    shopTagsPlaceholder: "标签1, 标签2, 标签3",
+    mapTitlePlaceholder: "地图标题",
+    shopProductFeatured: "推荐产品",
+    shopSendToMarket: "Send to Market",
+    typeShop: "商店",
+    typeShopProduct: "店铺商品",
     bankExchange: '交易所',
     bankExchangeCurrentValue: 'ECOin 价值(1小时)',
     bankTotalSupply: 'ECOin 总供应量',
@@ -2182,6 +2263,9 @@ module.exports = {
     statsAudio: "音频",
     statsVideo: "视频",
     statsDocument: "文档",
+    statsMap: "地图",
+    statsShop: "店铺",
+    statsShopProduct: "店铺商品",
     statsTransfer: "转账",
     statsAiExchange: "AI",
     statsPUBs: 'PUB',
@@ -2305,6 +2389,7 @@ module.exports = {
     marketItemDescription: "描述",
     marketItemDescriptionPlaceholder: "描述你要出售的商品",
     marketItemStatus: "状态",
+    marketShopLabel: "商店",
     marketItemCondition: "状况",
     marketItemPrice: "价格",
     marketItemTags: "标签",
@@ -2585,6 +2670,8 @@ module.exports = {
     modulesTasksDescription: "发现和管理任务的模块。",
     modulesMarketLabel: "市场",
     modulesMarketDescription: "交换商品或服务的模块。",
+    modulesShopsLabel: "商店",
+    modulesShopsDescription: "管理和发现商店的模块。",
     modulesTribesLabel: "部落",
     modulesTribesDescription: "探索或创建部落(群组)的模块。",
     modulesVotationsLabel: "投票",
@@ -2628,7 +2715,7 @@ module.exports = {
     connectAndFollow: "连接",
     deviceSourceLabel: "设备来源",
     modulesPresetTitle: "常用配置",
-    modulesPreset_minimal: "最小化",
+    modulesPreset_minimal: "极简",
     modulesPreset_basic: "基础",
     modulesPreset_social: "社交",
     modulesPreset_economy: "经济",
@@ -2640,16 +2727,66 @@ module.exports = {
     feedSuccessMsg: "动态发布成功!",
     dominantOpinionLabel: "主流观点",
     uploadMedia: "上传媒体(最大:50MB)",
-    feedViewDetails: "查看详情",
     feedOpenDiscussion: "公开讨论",
     feedDetailTitle: "动态",
     feedPostComment: "发表评论",
-    eventFileLabel: "附加图片",
-    taskFileLabel: "附加图片",
-    courtsRespondentRequired: "需要被告 ID",
     courtsRespondentInvalid: "必须是有效的 SSB ID (@...ed25519)",
-    noComments: "还没有评论"
-
-     //END
+    noComments: "还没有评论",
+    mapsLabel: "地图",
+    mapTitle: "地图",
+    mapDescription: "在您的网络中探索和管理离线地图。",
+    mapMineSectionTitle: "我的地图",
+    mapCreateSectionTitle: "创建地图",
+    mapUpdateSectionTitle: "更新地图",
+    mapAllSectionTitle: "地图",
+    mapRecentSectionTitle: "最近的地图",
+    mapFavoritesSectionTitle: "收藏",
+    mapFilterAll: "全部",
+    mapFilterMine: "我的",
+    mapFilterRecent: "最近",
+    mapFilterFavorites: "收藏",
+    mapUploadButton: "创建地图",
+    mapCreateButton: "创建地图",
+    mapUpdateButton: "更新",
+    mapDeleteButton: "删除",
+    mapAddFavoriteButton: "添加收藏",
+    mapRemoveFavoriteButton: "取消收藏",
+    mapLatLabel: "纬度",
+    mapLatPlaceholder: "例如 39.9042",
+    mapLngLabel: "经度",
+    mapLngPlaceholder: "例如 116.4074",
+    mapDescriptionLabel: "描述",
+    mapDescriptionPlaceholder: "描述地图或位置...",
+    mapTypeLabel: "地图类型",
+    mapTypeSingle: "单一(仅初始位置)",
+    mapTypeOpen: "开放(任何人可添加标记)",
+    mapTypeClosed: "封闭(仅创建者可添加标记)",
+    mapTagsLabel: "标签",
+    mapTagsPlaceholder: "输入以逗号分隔的标签",
+    mapUrlLabel: "地图链接",
+    mapPickCoordLabel: "或在网格上选择位置:",
+    mapMarkersLabel: "标记",
+    mapMarkersTitle: "标记",
+    mapMarkerDefault: "标记",
+    mapMarkerLatLabel: "标记纬度",
+    mapMarkerLngLabel: "标记经度",
+    mapMarkerLabelField: "标记名称",
+    mapMarkerLabelPlaceholder: "描述此标记...",
+    markerImageLabel: "标记图片",
+    mapAddMarkerTitle: "添加标记",
+    mapAddMarkerButton: "添加标记",
+    mapCleanMarkerButton: "Clean Marker",
+    mapApplyZoom: "应用缩放",
+    mapSearchPlaceholder: "搜索描述、标签、作者...",
+    mapSearchButton: "搜索",
+    mapUpdatedAt: "已更新",
+    mapNoMatch: "没有匹配的地图。",
+    noMaps: "没有可用的地图。",
+    mapLocationTitle: "位置",
+    mapVisitLabel: "访问地图",
+    typeMap: "地图",
+    typeMapMarker: "地图标记",
+    modulesMapLabel: "地图",
+    modulesMapDescription: "管理和分享离线地图的模块。"
     }
 };

+ 11 - 2
src/client/middleware.js

@@ -50,8 +50,16 @@ module.exports = ({ host, port, middleware, allowHost }) => {
   });
 
   app.use(mount("/assets", assets));
-  
-  // pdf viewer
+
+  const maptiles = new Koa();
+  maptiles.use(koaStatic(join(__dirname, "..", "maps", "tiles")));
+  app.use(mount("/maptiles", maptiles));
+
+  const mapcache = new Koa();
+  mapcache.use(koaStatic(join(__dirname, "..", "maps", "cache")));
+  app.use(mount("/mapcache", mapcache));
+
+
   app.use(mount("/js", koaStatic(path.join(__dirname, 'public/js'))));
   app.use(koaStatic(path.join(__dirname, 'public')));
 
@@ -66,6 +74,7 @@ module.exports = ({ host, port, middleware, allowHost }) => {
       "img-src 'self'",
       "media-src 'self' blob:",
       "worker-src 'self' blob:",
+      "frame-src 'self'",
       "form-action 'self'",
       "object-src 'none'",
       "base-uri 'none'",

+ 31 - 1
src/configs/banking-epochs.json

@@ -1 +1,31 @@
-[]
+[
+  {
+    "id": "2026-14",
+    "pool": 0,
+    "weightsSum": 1.91,
+    "rules": {
+      "epochKind": "WEEKLY",
+      "alpha": 0.2,
+      "reserveMin": 500,
+      "capPerEpoch": 2000,
+      "caps": {
+        "M_max": 3,
+        "T_max": 1.5,
+        "P_max": 2,
+        "cap_user_epoch": 50,
+        "w_min": 0.2,
+        "w_max": 6
+      },
+      "coeffs": {
+        "a1": 0.6,
+        "a2": 0.4,
+        "a3": 0.3,
+        "a4": 0.5,
+        "b1": 0.5,
+        "b2": 1
+      },
+      "graceDays": 14
+    },
+    "hash": "60b41f21ce26af434a87ab74a006d607805661c00f6740610d68a97e972a1720"
+  }
+]

+ 4 - 2
src/configs/config-manager.js

@@ -29,8 +29,8 @@ if (!fs.existsSync(configFilePath)) {
       "eventsMod": "on",
       "tasksMod": "on",
       "marketMod": "on",
-      "tribesMod": "on",
       "votesMod": "on",
+      "tribesMod": "on",
       "reportsMod": "on",
       "opinionsMod": "on",
       "transfersMod": "on",
@@ -40,11 +40,13 @@ if (!fs.existsSync(configFilePath)) {
       "aiMod": "on",
       "forumMod": "on",
       "jobsMod": "on",
+      "shopsMod": "on",
       "projectsMod": "on",
       "bankingMod": "on",
       "parliamentMod": "on",
       "courtsMod": "on",
-      "favoritesMod": "on"
+      "favoritesMod": "on",
+      "mapsMod": "on"
     },
     "wallet": {
       "url": "http://localhost:7474",

+ 4 - 2
src/configs/oasis-config.json

@@ -34,11 +34,13 @@
     "aiMod": "on",
     "forumMod": "on",
     "jobsMod": "on",
+    "shopsMod": "on",
     "projectsMod": "on",
     "bankingMod": "on",
     "parliamentMod": "on",
     "courtsMod": "on",
-    "favoritesMod": "on"
+    "favoritesMod": "on",
+    "mapsMod": "on"
   },
   "wallet": {
     "url": "http://localhost:7474",
@@ -59,4 +61,4 @@
   },
   "homePage": "activity",
   "language": "en"
-}
+}

+ 223 - 0
src/maps/map_renderer.js

@@ -0,0 +1,223 @@
+const { execFileSync } = require("child_process");
+const path = require("path");
+const fs = require("fs");
+const crypto = require("crypto");
+
+const BASE_MAP = path.join(__dirname, "..", "client", "assets", "images", "worldmap-z2.png");
+const CACHE_DIR = path.join(__dirname, "cache");
+const TILES_DIR = path.join(__dirname, "tiles");
+const MAP_W = 1024;
+const MAP_H = 1024;
+
+const latLngToPx = (lat, lng) => {
+  const latRad = lat * Math.PI / 180;
+  const x = Math.round((lng + 180) / 360 * MAP_W);
+  const y = Math.round((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * MAP_H);
+  return { x: Math.max(12, Math.min(MAP_W - 12, x)), y: Math.max(12, Math.min(MAP_H - 12, y)) };
+};
+
+const pxToLatLng = (px, py) => {
+  const lng = px / MAP_W * 360 - 180;
+  const n = Math.PI - 2 * Math.PI * py / MAP_H;
+  const lat = 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
+  return { lat: Math.round(lat * 100) / 100, lng: Math.round(lng * 100) / 100 };
+};
+
+const getMaxTileZoom = () => {
+  try {
+    const dirs = fs.readdirSync(TILES_DIR).filter(d => /^\d+$/.test(d) && fs.existsSync(path.join(TILES_DIR, d, '0_0.png')));
+    return dirs.length ? Math.max(...dirs.map(Number)) : 0;
+  } catch (_) { return 0; }
+};
+
+const getViewportBounds = (centerLat, centerLng, zoom) => {
+  const effectiveZ = Math.min(zoom, getMaxTileZoom());
+  const n = Math.pow(2, effectiveZ);
+  const tileSize = 256;
+  const worldPx = n * tileSize;
+  const scale = Math.pow(2, zoom - effectiveZ);
+  const vw = MAP_W / scale;
+  const vh = MAP_H / scale;
+
+  const latRad = centerLat * Math.PI / 180;
+  const cx = (centerLng + 180) / 360 * worldPx;
+  const cy = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * worldPx;
+
+  const x0 = cx - vw / 2;
+  const y0 = cy - vh / 2;
+  const x1 = cx + vw / 2;
+  const y1 = cy + vh / 2;
+
+  const pxToLng = (px) => px / worldPx * 360 - 180;
+  const pxToLat = (py) => { const nn = Math.PI - 2 * Math.PI * py / worldPx; return 180 / Math.PI * Math.atan(0.5 * (Math.exp(nn) - Math.exp(-nn))); };
+
+  return {
+    latMin: Math.max(-85, pxToLat(Math.min(y1, worldPx - 1))),
+    latMax: Math.min(85, pxToLat(Math.max(y0, 0))),
+    lngMin: Math.max(-180, pxToLng(Math.max(x0, 0))),
+    lngMax: Math.min(180, pxToLng(Math.min(x1, worldPx - 1)))
+  };
+};
+
+const renderMapWithPins = (markers, mainIdx) => {
+  const pins = (Array.isArray(markers) ? markers : [])
+    .filter((m) => m && typeof m.lat === "number" && typeof m.lng === "number")
+    .map((m, i) => ({ ...latLngToPx(m.lat, m.lng), main: i === (mainIdx || 0) }));
+
+  const hash = crypto.createHash("md5")
+    .update(pins.map((p) => `${p.x},${p.y},${p.main}`).join(";"))
+    .digest("hex")
+    .slice(0, 12);
+
+  const outFile = path.join(CACHE_DIR, `map_${hash}.png`);
+
+  if (fs.existsSync(outFile)) return `map_${hash}.png`;
+
+  const script = `
+from PIL import Image, ImageDraw
+import sys, json
+
+pins = json.loads(sys.argv[1])
+im = Image.open(sys.argv[2]).copy()
+draw = ImageDraw.Draw(im)
+
+for p in pins:
+    x, y, main = p['x'], p['y'], p.get('main', False)
+    sw = 3 if main else 2
+    sh = 18 if main else 13
+    clr = '#e74c3c' if main else '#3498db'
+    dark = '#c0392b' if main else '#2980b9'
+    draw.polygon([(x, y + 2), (x - sw, y - sh + sw * 2), (x + sw, y - sh + sw * 2)], fill=clr)
+    draw.ellipse([x - sw - 1, y - sh - sw, x + sw + 1, y - sh + sw], fill=dark, outline='white', width=1)
+
+im.save(sys.argv[3], optimize=True)
+`;
+
+  try {
+    fs.mkdirSync(CACHE_DIR, { recursive: true });
+    execFileSync("python3", [
+      "-c", script,
+      JSON.stringify(pins),
+      BASE_MAP,
+      outFile
+    ], { timeout: 10000 });
+  } catch (e) {
+    return null;
+  }
+
+  return `map_${hash}.png`;
+};
+
+const renderZoomedMapWithPins = (centerLat, centerLng, zoom, markers, mainIdx) => {
+  const maxZ = getMaxTileZoom();
+  if (!maxZ || zoom <= 2) return renderMapWithPins(markers, mainIdx);
+
+  const effectiveZ = Math.min(zoom, maxZ);
+  const scale = Math.pow(2, zoom - effectiveZ);
+  const n = Math.pow(2, effectiveZ);
+  const tileSize = 256;
+  const worldPx = n * tileSize;
+
+  const latRad = centerLat * Math.PI / 180;
+  const cx = (centerLng + 180) / 360 * worldPx;
+  const cy = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * worldPx;
+
+  const vw = MAP_W / scale;
+  const vh = MAP_H / scale;
+  const x0 = cx - vw / 2;
+  const y0 = cy - vh / 2;
+
+  const pinData = (Array.isArray(markers) ? markers : [])
+    .filter((m) => m && typeof m.lat === "number" && typeof m.lng === "number")
+    .map((m, i) => {
+      const latR = m.lat * Math.PI / 180;
+      const wx = (m.lng + 180) / 360 * worldPx;
+      const wy = (1 - Math.log(Math.tan(latR) + 1 / Math.cos(latR)) / Math.PI) / 2 * worldPx;
+      return { px: (wx - x0) * scale, py: (wy - y0) * scale, main: i === (mainIdx || 0) };
+    });
+
+  const hashInput = `z${zoom}_${Math.round(centerLat * 100)}_${Math.round(centerLng * 100)}_` + pinData.map(p => `${Math.round(p.px)},${Math.round(p.py)},${p.main}`).join(";");
+  const hash = crypto.createHash("md5").update(hashInput).digest("hex").slice(0, 12);
+  const outFile = path.join(CACHE_DIR, `map_${hash}.png`);
+
+  if (fs.existsSync(outFile)) return `map_${hash}.png`;
+
+  const txMin = Math.max(0, Math.floor(x0 / tileSize));
+  const txMax = Math.min(n - 1, Math.floor((x0 + vw) / tileSize));
+  const tyMin = Math.max(0, Math.floor(y0 / tileSize));
+  const tyMax = Math.min(n - 1, Math.floor((y0 + vh) / tileSize));
+
+  const tiles = [];
+  for (let tx = txMin; tx <= txMax; tx++) {
+    for (let ty = tyMin; ty <= tyMax; ty++) {
+      const tp = path.join(TILES_DIR, String(effectiveZ), `${tx}_${ty}.png`);
+      tiles.push({ tx, ty, tp, exists: fs.existsSync(tp) });
+    }
+  }
+
+  const script = `
+from PIL import Image, ImageDraw
+import sys, json, os
+
+args = json.loads(sys.argv[1])
+out_file = sys.argv[2]
+
+tile_size = 256
+tx_min = args['txMin']
+ty_min = args['tyMin']
+tx_max = args['txMax']
+ty_max = args['tyMax']
+x0 = args['x0']
+y0 = args['y0']
+vw = args['vw']
+vh = args['vh']
+scale = args['scale']
+pins = args['pins']
+tiles = args['tiles']
+
+canvas_w = (tx_max - tx_min + 1) * tile_size
+canvas_h = (ty_max - ty_min + 1) * tile_size
+canvas = Image.new('RGB', (canvas_w, canvas_h), (170, 211, 223))
+
+for t in tiles:
+    if t['exists']:
+        try:
+            tile = Image.open(t['tp']).convert('RGB')
+            canvas.paste(tile, ((t['tx'] - tx_min) * tile_size, (t['ty'] - ty_min) * tile_size))
+        except:
+            pass
+
+crop_x = x0 - tx_min * tile_size
+crop_y = y0 - ty_min * tile_size
+cropped = canvas.crop((int(crop_x), int(crop_y), int(crop_x + vw), int(crop_y + vh)))
+result = cropped.resize((1024, 1024), Image.LANCZOS)
+
+draw = ImageDraw.Draw(result)
+for p in pins:
+    px, py, main = p['px'], p['py'], p.get('main', False)
+    if -20 <= px <= 1044 and -20 <= py <= 1044:
+        sw = 3 if main else 2
+        sh = 18 if main else 13
+        clr = '#e74c3c' if main else '#3498db'
+        dark = '#c0392b' if main else '#2980b9'
+        draw.polygon([(px, py + 2), (px - sw, py - sh + sw * 2), (px + sw, py - sh + sw * 2)], fill=clr)
+        draw.ellipse([px - sw - 1, py - sh - sw, px + sw + 1, py - sh + sw], fill=dark, outline='white', width=1)
+
+result.save(out_file, optimize=True)
+`;
+
+  try {
+    fs.mkdirSync(CACHE_DIR, { recursive: true });
+    execFileSync("python3", [
+      "-c", script,
+      JSON.stringify({ txMin, tyMin, txMax, tyMax, x0, y0, vw, vh, scale, pins: pinData, tiles }),
+      outFile
+    ], { timeout: 15000 });
+  } catch (e) {
+    return renderMapWithPins(markers, mainIdx);
+  }
+
+  return `map_${hash}.png`;
+};
+
+module.exports = { renderMapWithPins, renderZoomedMapWithPins, getViewportBounds, getMaxTileZoom, latLngToPx, pxToLatLng, MAP_W, MAP_H };

binární
src/maps/tiles/0/0_0.png


binární
src/maps/tiles/1/0_0.png


binární
src/maps/tiles/1/0_1.png


binární
src/maps/tiles/1/1_0.png


binární
src/maps/tiles/1/1_1.png


binární
src/maps/tiles/2/0_0.png


binární
src/maps/tiles/2/0_1.png


binární
src/maps/tiles/2/0_2.png


binární
src/maps/tiles/2/0_3.png


binární
src/maps/tiles/2/1_0.png


binární
src/maps/tiles/2/1_1.png


binární
src/maps/tiles/2/1_2.png


binární
src/maps/tiles/2/1_3.png


binární
src/maps/tiles/2/2_0.png


binární
src/maps/tiles/2/2_1.png


binární
src/maps/tiles/2/2_2.png


binární
src/maps/tiles/2/2_3.png


binární
src/maps/tiles/2/3_0.png


binární
src/maps/tiles/2/3_1.png


binární
src/maps/tiles/2/3_2.png


binární
src/maps/tiles/2/3_3.png


binární
src/maps/tiles/3/0_0.png


binární
src/maps/tiles/3/0_1.png


binární
src/maps/tiles/3/0_2.png


binární
src/maps/tiles/3/0_3.png


binární
src/maps/tiles/3/0_4.png


binární
src/maps/tiles/3/0_5.png


binární
src/maps/tiles/3/0_6.png


binární
src/maps/tiles/3/0_7.png


binární
src/maps/tiles/3/1_0.png


binární
src/maps/tiles/3/1_1.png


binární
src/maps/tiles/3/1_2.png


binární
src/maps/tiles/3/1_3.png


binární
src/maps/tiles/3/1_4.png


binární
src/maps/tiles/3/1_5.png


binární
src/maps/tiles/3/1_6.png


binární
src/maps/tiles/3/1_7.png


binární
src/maps/tiles/3/2_0.png


binární
src/maps/tiles/3/2_1.png


binární
src/maps/tiles/3/2_2.png


binární
src/maps/tiles/3/2_3.png


binární
src/maps/tiles/3/2_4.png


binární
src/maps/tiles/3/2_5.png


binární
src/maps/tiles/3/2_6.png


binární
src/maps/tiles/3/2_7.png


binární
src/maps/tiles/3/3_0.png


binární
src/maps/tiles/3/3_1.png


binární
src/maps/tiles/3/3_2.png


binární
src/maps/tiles/3/3_3.png


binární
src/maps/tiles/3/3_4.png


binární
src/maps/tiles/3/3_5.png


binární
src/maps/tiles/3/3_6.png


binární
src/maps/tiles/3/3_7.png


binární
src/maps/tiles/3/4_0.png


binární
src/maps/tiles/3/4_1.png


binární
src/maps/tiles/3/4_2.png


binární
src/maps/tiles/3/4_3.png


binární
src/maps/tiles/3/4_4.png


binární
src/maps/tiles/3/4_5.png


binární
src/maps/tiles/3/4_6.png


binární
src/maps/tiles/3/4_7.png


binární
src/maps/tiles/3/5_0.png


binární
src/maps/tiles/3/5_1.png


binární
src/maps/tiles/3/5_2.png


binární
src/maps/tiles/3/5_3.png


binární
src/maps/tiles/3/5_4.png


binární
src/maps/tiles/3/5_5.png


binární
src/maps/tiles/3/5_6.png


binární
src/maps/tiles/3/5_7.png


binární
src/maps/tiles/3/6_0.png


binární
src/maps/tiles/3/6_1.png


binární
src/maps/tiles/3/6_2.png


binární
src/maps/tiles/3/6_3.png


+ 0 - 0
src/maps/tiles/3/6_4.png


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů