diff --git a/package-lock.json b/package-lock.json index 9ccaa85..642101d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lynn", - "version": "25.2.0", + "version": "26.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lynn", - "version": "25.2.0", + "version": "26.0.0", "dependencies": { "@date-io/dayjs": "^2.16.0", "@eisberg-labs/mui-copy-field": "^4.0.2", @@ -19,6 +19,8 @@ "dayjs": "^1.11.7", "eorzea-time": "^3.0.0", "i18next": "^25.2.1", + "i18next-browser-languagedetector": "^8.2.0", + "i18next-resources-to-backend": "^1.2.1", "leaflet": "^1.9.4", "leaflet-draw": "^1.0.4", "lynn-eorzea-weather": "^3.3.0", @@ -10960,6 +10962,24 @@ } } }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", + "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-resources-to-backend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.1.tgz", + "integrity": "sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", diff --git a/package.json b/package.json index 8e623b1..fb0c273 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lynn", - "version": "26.0.0", + "version": "27.0.0", "private": true, "dependencies": { "@date-io/dayjs": "^2.16.0", @@ -14,6 +14,8 @@ "dayjs": "^1.11.7", "eorzea-time": "^3.0.0", "i18next": "^25.2.1", + "i18next-browser-languagedetector": "^8.2.0", + "i18next-resources-to-backend": "^1.2.1", "leaflet": "^1.9.4", "leaflet-draw": "^1.0.4", "lynn-eorzea-weather": "^3.3.0", diff --git a/public/assets/maps/markers/ocSoulShard.png b/public/assets/maps/markers/ocSoulShard.png new file mode 100644 index 0000000..f251f5c Binary files /dev/null and b/public/assets/maps/markers/ocSoulShard.png differ diff --git a/public/assets/weathericons/060239.png b/public/assets/weathericons/IllusoryDisturbances.png similarity index 100% rename from public/assets/weathericons/060239.png rename to public/assets/weathericons/IllusoryDisturbances.png diff --git a/src/App.jsx b/src/App.jsx index b9ac45b..0e3d8a1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,7 @@ import './App.css'; import { Helmet } from 'react-helmet'; import React, { useEffect } from 'react'; +import { I18nextProvider } from 'react-i18next'; import createTheme from '@mui/material/styles/createTheme'; import ThemeProvider from '@mui/material/styles/ThemeProvider'; @@ -10,6 +11,8 @@ import { useParams, } from 'react-router-dom'; +import i18n from './i18n'; + import MainPageComponent from './MainPageComponent'; import ForecastMainComponent from './forecast/ForecastMainComponent'; import BAMainComponent from './ba/BAMainComponent'; @@ -375,23 +378,22 @@ function App() { }); return ( - - - - FFXIV Field Operations Assistant - forays.info - - - - - - - - - - - - - + + + + + FFXIV Field Operations Assistant - forays.info + + + + + + + + + + + ); } diff --git a/src/FooterComponent.jsx b/src/FooterComponent.jsx index 0bd2e2d..f95c83d 100644 --- a/src/FooterComponent.jsx +++ b/src/FooterComponent.jsx @@ -7,9 +7,12 @@ import SvgIcon from '@mui/material/SvgIcon'; import Typography from '@mui/material/Typography'; import GitHubIcon from '@mui/icons-material/GitHub'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; export default function FooterComponent({ includePadding = true }) { const navigate = useNavigate(); + const { t } = useTranslation('common'); + return ( - FINAL FANTASY XIV © 2010 - 2025 SQUARE ENIX CO., LTD. FINAL FANTASY, FINAL FANTASY - XIV, and FFXIV are registered trademarks or trademarks of Square Enix Holdings Co., - Ltd. All material used under license. + {t('footer.copyright')} @@ -72,7 +73,7 @@ export default function FooterComponent({ includePadding = true }) { }} > - {`Version ${process.env.REACT_APP_VERSION}`} + {t('footer.version', { ver: process.env.REACT_APP_VERSION })} diff --git a/src/MainPageComponent.jsx b/src/MainPageComponent.jsx index 4dd3249..676669c 100644 --- a/src/MainPageComponent.jsx +++ b/src/MainPageComponent.jsx @@ -4,8 +4,11 @@ import Paper from '@mui/material/Paper'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import { Helmet } from 'react-helmet'; +import { useTranslation, Trans } from 'react-i18next'; export default function MainPageComponent() { + const { t } = useTranslation('common'); + function getSiteName() { const host = window.location.hostname; @@ -84,46 +87,44 @@ export default function MainPageComponent() { - Welcome to - {' '} - {getSiteName()} - , a home for tools to help with Final Fantasy XIV side - content - including the Occult Crescent (Forked Tower), Eureka (The Baldesion - Arsenal), and Bozja (Delubrum Reginae Savage). Find out how to build and gather - materials for Lost Actions and Logos Actions, what to bring for The Baldesion - Arsenal or Delubrum Reginae Savage runs, when the next Cassie or Crab spawn is, - and more! + - Looking for a group to run Forked Tower, BA, or DRS with? Most of the content on this site - was designed for runs on - {' '} - - The Help Lines - - - {' '} - on Primal. + + The Help Lines + , + ]} + /> - Built by Lynn Kaneko @ Exodus. Issues/suggestions? You can reach out to me - on Discord (@lynnkaneko). + - Home page artwork by - {' '} - - {pageTheme.imageCredit} - ! - + + {pageTheme.imageCredit} + ! + , + ]} + values={{ + artist: pageTheme.imageCredit, + }} + /> @@ -148,13 +149,11 @@ export default function MainPageComponent() { - {getSiteName()} - {' '} - - FFXIV Field Operations Assistant + {t('main.header.title', { sitename: getSiteName() })} - FFXIV Field Operations Assistant + diff --git a/src/SettingsPopoverComponent.jsx b/src/SettingsPopoverComponent.jsx index cdbd06f..0ae973d 100644 --- a/src/SettingsPopoverComponent.jsx +++ b/src/SettingsPopoverComponent.jsx @@ -1,4 +1,5 @@ import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -8,16 +9,36 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import Stack from '@mui/material/Stack'; -import ToggleButton from '@mui/material/ToggleButton'; -import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import Typography from '@mui/material/Typography'; import SettingsApplicationsIcon from '@mui/icons-material/SettingsApplications'; +import { + FormControlLabel, Radio, RadioGroup, +} from '@mui/material'; +import { changeLanguage } from './i18n'; import UniversalisRegionPicker from './UniversalisRegionPicker'; export default function SettingsPopoverComponent({ setColorMode }) { + const { t } = useTranslation('common'); const [open, setOpen] = React.useState(false); + /** + const getFlag = () => { + const language = localStorage.getItem('i18nextLng').substring(0, 2); + + switch (language) { + case 'fr': + return '🇫🇷'; + case 'de': + return '🇩🇪'; + case 'jp': + return '🇯🇵'; + default: + return '🇨🇦'; + } + }; + */ + const handleSettingsButtonClick = () => { setOpen(true); }; @@ -38,13 +59,15 @@ export default function SettingsPopoverComponent({ setColorMode }) { onClick={(e) => handleSettingsButtonClick(e)} > + { /* {getFlag()} */ } - Settings + {t('settings.title')} ); const [theme, setTheme] = React.useState(localStorage.getItem('theme')); + const [language, setLanguage] = React.useState(localStorage.getItem('language') || 'en'); const handleThemeChange = useCallback((event, newTheme) => { if (newTheme) { @@ -54,40 +77,55 @@ export default function SettingsPopoverComponent({ setColorMode }) { } }, [localStorage, setTheme]); + const handleLanguageChange = useCallback((event, newLanguage) => { + if (newLanguage) { + setLanguage(newLanguage); + changeLanguage(newLanguage); + } + }, [setLanguage, changeLanguage]); + return ( <> {settingsButton} - Settings + {t('settings.title')} - Site Theme - {t('settings.theme_title')} + - - Light - - - Dark - - - System - - - Universalis Region + } label={t('settings.themes.light')} /> + } label={t('settings.themes.dark')} /> + } label={t('settings.themes.system')} /> + + + {t('settings.language_title')} + + + } label="🇨🇦 English" /> + } disabled label="🇫🇷 Français" /> + } label="🇩🇪 Deutsch" /> + } disabled label="🇯🇵 日本語" /> + + + {t('settings.universalis_region_title')} - ); } diff --git a/src/SidebarComponent.jsx b/src/SidebarComponent.jsx index c4d0a13..068b9ff 100644 --- a/src/SidebarComponent.jsx +++ b/src/SidebarComponent.jsx @@ -15,6 +15,7 @@ import { styled, useTheme } from '@mui/material/styles'; import { useNavigate } from 'react-router-dom'; import { Scrollbars } from 'react-custom-scrollbars-2'; import { v4 as uuidv4 } from 'uuid'; +import { Trans } from 'react-i18next'; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; @@ -84,7 +85,9 @@ function SidebarComponent({ - OCCULT CRESCENT + + + { /* - Occult Crescent Maps + - Phantom Jobs + - BOZJA AND DRS + + + - DRS Holsters + - Holster Creator + - Bozja Maps + - Lost Action Info + - Fragment Farm Times + - EUREKA AND BA + + + - BA Loadouts + - BA Portal Map + - Eureka Maps + - Logos Action Info + - Loadout Creator + - NM Spawn Times + - Logogram/Box Farm Times + - FORAYS + + + - Expeditionary Forecast + - Advanced Weather Finder + - ABOUT + + + - Lynn Kaneko @ Exodus + ); @@ -262,12 +273,14 @@ function SidebarComponent({ - Home + - ALL + + + {[ ResultsFilter.ALL, @@ -288,13 +301,17 @@ function SidebarComponent({ ) : } - {resultsfilter.name} + + + ))} - EUREKA NMS + + + {[ ResultsFilter.EUREKA_NMS, @@ -320,13 +337,17 @@ function SidebarComponent({ ) : } - {resultsfilter.name} + + + ))} - EUREKA FARMS + + + {[ ResultsFilter.EUREKA_FARMS, @@ -352,13 +373,15 @@ function SidebarComponent({ ) : } - {resultsfilter.name} + ))} - BOZJA FRAGMENT FARMS + + + {[ ResultsFilter.FRAGMENT_FARM, @@ -384,7 +407,7 @@ function SidebarComponent({ ) : } - {resultsfilter.name} + ))} diff --git a/src/TimeAndWeatherPopoverComponent.jsx b/src/TimeAndWeatherPopoverComponent.jsx index 60a12fa..2b991f0 100644 --- a/src/TimeAndWeatherPopoverComponent.jsx +++ b/src/TimeAndWeatherPopoverComponent.jsx @@ -16,10 +16,12 @@ import EorzeaWeather from 'lynn-eorzea-weather'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { v4 as uuidv4 } from 'uuid'; import dayjs from 'dayjs'; +import { useTranslation } from 'react-i18next'; import UpcomingSpawnCalculator from './forecast/lib/UpcomingSpawnCalculator'; export default function TimeAndWeatherPopoverComponent() { const [open, setOpen] = React.useState(false); + const { t } = useTranslation(); const handleTimeWidgetClick = () => { setOpen(true); @@ -31,31 +33,31 @@ export default function TimeAndWeatherPopoverComponent() { const regionsToFetchWeatherFor = [ { - name: 'The Occult Crescent: South Horn', + name: t('occult.southhorn.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_SOUTH_HORN, }, { - name: 'The Bozjan Southern Front', + name: t('bozja.bsf.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_BOZJAN_SOUTHERN_FRONT, }, { - name: 'Zadnor', + name: t('bozja.zadnor.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_ZADNOR, }, { - name: 'Eureka Anemos', + name: t('eureka.anemos.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_EUREKA_ANEMOS, }, { - name: 'Eureka Pagos', + name: t('eureka.pagos.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_EUREKA_PAGOS, }, { - name: 'Eureka Pyros', + name: t('eureka.pyros.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_EUREKA_PYROS, }, { - name: 'Eureka Hydatos', + name: t('eureka.hydatos.full', { ns: 'zones' }), zone: EorzeaWeather.ZONE_EUREKA_HYDATOS, }, ]; @@ -103,6 +105,9 @@ export default function TimeAndWeatherPopoverComponent() { + + {item.condition} + {dayjs(item.time.getTime()) .format('h:mm:ss A')} @@ -187,7 +192,7 @@ export default function TimeAndWeatherPopoverComponent() { {time} - Current Time + {t('weather-popover.current-time')} {time} @@ -195,7 +200,7 @@ export default function TimeAndWeatherPopoverComponent() { ET - Current Weather + {t('weather-popover.current-weather')} } spacing={1}> @@ -215,7 +220,7 @@ export default function TimeAndWeatherPopoverComponent() { diff --git a/src/changelog.json b/src/changelog.json index 780484b..e9167a1 100644 --- a/src/changelog.json +++ b/src/changelog.json @@ -1,5 +1,13 @@ { "changes": [ + { + "version": "27.0", + "timestamp": "2025-07-09", + "changes": [ + "Added German language support for Occult Crescent map", + "Other bugfixes" + ] + }, { "version": "26.0", "timestamp": "2025-06-07", diff --git a/src/drs/BozjaLostActionHelperComponent.jsx b/src/drs/BozjaLostActionHelperComponent.jsx index 20cf18f..cdd5045 100644 --- a/src/drs/BozjaLostActionHelperComponent.jsx +++ b/src/drs/BozjaLostActionHelperComponent.jsx @@ -4,6 +4,7 @@ import Box from '@mui/material/Box'; import Container from '@mui/material/Container'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; +import { Trans } from 'react-i18next'; import BozjaLostActionSelectorComponent from './BozjaLostActionSelectorComponent'; import BozjaLostActionHelperDataComponent from './BozjaLostActionHelperDataComponent'; @@ -34,9 +35,9 @@ export default function BozjaLostActionHelperComponent({ lostAction }) { - Bozja Lost Action Helper + - Action: + - Appraised from + import('./DRSNewHolsterSelectorComponent')); export default function DRSNewHolsterMainComponent({ holster, encodedHolster, resetTimer, }) { + const { t } = useTranslation('bsf'); + return ( - DRS Holster Helper + {t('drs.holster-helper.title')} Loading...}> } onClick={handleReset} > - Start Over + {holster && firstHolster && holster.name @@ -265,7 +268,7 @@ export default function DRSNewHolsterSelectorComponent({ startIcon={} onClick={handleBack} > - Back + )} @@ -289,10 +292,13 @@ export default function DRSNewHolsterSelectorComponent({ {holsterMapping.hosts[selectedHost].lastUpdated ? ( - (Last Updated: - {' '} - {holsterMapping.hosts[selectedHost].lastUpdated} - ) + ) : null} @@ -438,14 +444,16 @@ export default function DRSNewHolsterSelectorComponent({ const displayStepperIntroText = (() => ( - Use this tool to see what Lost Actions you should bring on your DRS run. + { t('drs.holster-helper.intro.p1') } - Are you a DRS host? Use the - {' '} - DRS Run Holster Creator - {' '} - to get your holsters added to this page! + DRS Run Holster Creator, + ]} + /> )); diff --git a/src/forecast/lib/ResultsFilter.js b/src/forecast/lib/ResultsFilter.js index 737647e..e6fa37e 100644 --- a/src/forecast/lib/ResultsFilter.js +++ b/src/forecast/lib/ResultsFilter.js @@ -6,6 +6,7 @@ export default class ResultsFilter { // Eureka NMs static COPYCAT_CASSIE = new ResultsFilter({ name: 'Copycat Cassie', + key: 'sidebar.forecast.cassie', type: FarmType.EUREKA_NM, zone: EorzeaWeather.ZONE_EUREKA_PAGOS, requiredWeather: [Weather.BLIZZARDS], @@ -28,6 +29,7 @@ export default class ResultsFilter { static KING_ARTHO = new ResultsFilter({ name: 'King Artho', + key: 'sidebar.forecast.crab', type: FarmType.EUREKA_NM, zone: EorzeaWeather.ZONE_EUREKA_PAGOS, requiredWeather: [Weather.FOG], @@ -50,6 +52,7 @@ export default class ResultsFilter { static SKOLL = new ResultsFilter({ name: 'Skoll', + key: 'sidebar.forecast.skoll', type: FarmType.EUREKA_NM, zone: EorzeaWeather.ZONE_EUREKA_PYROS, requiredWeather: [Weather.BLIZZARDS], @@ -72,6 +75,7 @@ export default class ResultsFilter { static PAZUZU = new ResultsFilter({ name: 'Pazuzu', + key: 'sidebar.forecast.pazuzu', type: FarmType.EUREKA_NM, zone: EorzeaWeather.ZONE_EUREKA_ANEMOS, requiredWeather: [Weather.GALES], @@ -94,6 +98,7 @@ export default class ResultsFilter { static PENTHESILEA = new ResultsFilter({ name: 'Penthesilea', + key: 'sidebar.forecast.penny', type: FarmType.EUREKA_NM, zone: EorzeaWeather.ZONE_EUREKA_PYROS, requiredWeather: [Weather.HEAT_WAVES], @@ -117,6 +122,7 @@ export default class ResultsFilter { // Eureka Farms static COLD_WARPED_LOCKBOX = new ResultsFilter({ name: 'Cold-Warped Lockbox', + key: 'sidebar.forecast.coldbox', type: FarmType.EUREKA_FARM, zone: EorzeaWeather.ZONE_EUREKA_PAGOS, requiredWeather: [Weather.THUNDER], @@ -149,6 +155,7 @@ export default class ResultsFilter { static HEAT_WARPED_LOCKBOX = new ResultsFilter({ name: 'Heat-Warped Lockbox', + key: 'sidebar.forecast.heatbox', type: FarmType.EUREKA_FARM, zone: EorzeaWeather.ZONE_EUREKA_PYROS, requiredWeather: [Weather.UMBRAL_WIND], @@ -171,6 +178,7 @@ export default class ResultsFilter { static OFFENSIVE_LOGOGRAM = new ResultsFilter({ name: 'Offensive Logogram', + key: 'sidebar.forecast.offensive', type: FarmType.EUREKA_FARM, zone: EorzeaWeather.ZONE_EUREKA_HYDATOS, requiredWeather: [Weather.SNOW], @@ -193,6 +201,7 @@ export default class ResultsFilter { static CONCEPTUAL_LOGOGRAM = new ResultsFilter({ name: 'Conceptual Logogram', + key: 'sidebar.forecast.conceptual', type: FarmType.EUREKA_FARM, zone: EorzeaWeather.ZONE_EUREKA_HYDATOS, requiredWeather: [Weather.SHOWERS, Weather.THUNDERSTORMS], @@ -220,6 +229,7 @@ export default class ResultsFilter { static MITIGATIVE_LOGOGRAM = new ResultsFilter({ name: 'Mitigative Logogram', + key: 'sidebar.forecast.mitigative', type: FarmType.EUREKA_FARM, zone: EorzeaWeather.ZONE_EUREKA_PYROS, requiredWeather: [Weather.THUNDER, Weather.HEAT_WAVES], @@ -253,6 +263,7 @@ export default class ResultsFilter { static PREPARATION_FRAGMENT = new ResultsFilter({ name: 'Preparation', + key: 'sidebar.forecast.preparation', type: FarmType.FRAGMENT_FARM, zone: EorzeaWeather.ZONE_BOZJAN_SOUTHERN_FRONT, requiredWeather: [Weather.THUNDER], @@ -273,6 +284,7 @@ export default class ResultsFilter { static CARE_FRAGMENT = new ResultsFilter({ name: 'Care', + key: 'sidebar.forecast.care', type: FarmType.FRAGMENT_FARM, zone: EorzeaWeather.ZONE_BOZJAN_SOUTHERN_FRONT, requiredWeather: [Weather.DUST_STORMS, Weather.WIND], @@ -298,6 +310,7 @@ export default class ResultsFilter { static SUPPORT_FRAGMENT = new ResultsFilter({ name: 'Support', + key: 'sidebar.forecast.support', type: FarmType.FRAGMENT_FARM, zone: EorzeaWeather.ZONE_BOZJAN_SOUTHERN_FRONT, requiredWeather: [Weather.SNOW, Weather.WIND], @@ -322,6 +335,7 @@ export default class ResultsFilter { static HISTORY_FRAGMENT = new ResultsFilter({ name: 'History', + key: 'sidebar.forecast.history', type: FarmType.FRAGMENT_FARM, zone: EorzeaWeather.ZONE_ZADNOR, requiredWeather: [Weather.SNOW, Weather.WIND], @@ -346,6 +360,7 @@ export default class ResultsFilter { static ARTISTRY_FRAGMENT = new ResultsFilter({ name: 'Artistry', + key: 'sidebar.forecast.artistry', type: FarmType.FRAGMENT_FARM, zone: EorzeaWeather.ZONE_ZADNOR, requiredWeather: [Weather.THUNDER, Weather.RAIN], @@ -372,6 +387,7 @@ export default class ResultsFilter { // Collections static EUREKA_NMS = new ResultsFilter({ name: 'All Eureka NMs', + key: 'sidebar.forecast.all-eureka-nms', type: FarmType.EUREKA_NM, zone: null, requiredWeather: null, @@ -388,6 +404,7 @@ export default class ResultsFilter { static FRAGMENT_FARM = new ResultsFilter({ name: 'All Bozja Fragment Farms', + key: 'sidebar.forecast.all-bozja-farms', type: FarmType.FRAGMENT_FARM, zone: null, requiredWeather: null, @@ -404,6 +421,7 @@ export default class ResultsFilter { static EUREKA_FARMS = new ResultsFilter({ name: 'All Eureka Farms', + key: 'sidebar.forecast.all-eureka-farms', type: FarmType.EUREKA_FARM, zone: null, requiredWeather: null, @@ -420,6 +438,7 @@ export default class ResultsFilter { static ALL = new ResultsFilter({ name: 'All Upcoming Spawns', + key: 'sidebar.forecast.all-spawns', type: FarmType.ALL, zone: null, requiredWeather: null, @@ -482,6 +501,7 @@ export default class ResultsFilter { constructor(data) { this.name = data.name; + this.key = data.key; this.type = data.type; this.zone = data.zone; this.requiredWeather = data.requiredWeather; diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 0000000..1cc3570 --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,36 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import resourcesToBackend from 'i18next-resources-to-backend'; + +const localStorageLanguage = localStorage.getItem('language'); + +// Define the namespaces you want to use +const namespaces = ['common', 'map', 'zones', 'bsf', 'occult']; +const defaultNamespace = 'common'; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .use(resourcesToBackend((language, namespace) => import(`./locales/${language}/${namespace}.json`))) + .init({ + lng: localStorageLanguage, + fallbackLng: 'en', + debug: false, + ns: namespaces, + defaultNS: defaultNamespace, + detection: { + order: ['localStorage', 'navigator'], + caches: ['localStorage'], + }, + interpolation: { + escapeValue: false, // React already does escaping + }, + }); + +export const changeLanguage = (lng) => { + localStorage.setItem('language', lng); + i18n.changeLanguage(lng); +}; + +export default i18n; diff --git a/src/index.js b/src/index.js index 7c86394..a4a1441 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ import { createRoot, hydrateRoot } from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import './i18n'; const rootElement = document.getElementById('root'); if (rootElement.hasChildNodes()) { diff --git a/src/locales/de/common.json b/src/locales/de/common.json new file mode 100644 index 0000000..2551290 --- /dev/null +++ b/src/locales/de/common.json @@ -0,0 +1,23 @@ +{ + "sidebar": { + "occult": { + "header": "KRESZENTIA", + "map": "Kreszentia-Karten", + "job": "Phantomjobs" + } + }, + "settings": { + "title": "Einstellungen", + "theme_title": "Site Theme", + "themes": { + "light": "Hell", + "dark": "Dunkel", + "system": "System" + }, + "language_title": "Sprache", + "universalis_region_title": "Universalis-Welt" + }, + "common": { + "close": "Schließen" + } +} \ No newline at end of file diff --git a/src/locales/de/map.json b/src/locales/de/map.json new file mode 100644 index 0000000..0874316 --- /dev/null +++ b/src/locales/de/map.json @@ -0,0 +1,219 @@ +{ + "map": { + "regions": { + "southhorn": { + "name": "Kreszentia (Südexpedition)", + "short": "Südliches Kreszentia", + "namedLocation": { + "1": "Stille Laube", + "2": "Falscher Kreuzgang" + }, + "aetheryte": { + "1": "Basislager der Expedition", + "2": "Zuflucht des Wanderers", + "3": "Kristallgrotte", + "4": "Holderwuchs", + "5": "Felsfenn" + }, + "trader": { + "1": "Antiquarin der Expedition", + "2": "Händler der Expedition", + "3": "Rüstmeister der Expedition" + }, + "repair": "Reparatur", + "fate": { + "pot1": { + "name": "Freude im Pott" + }, + "pot2": { + "name": "Wunder im Pott" + }, + "1": { + "name": "Kampf um Leben und Schweiß", + "boss": "Entfeuchter" + }, + "2": { + "name": "Tückische Schönheit", + "boss": "Exekutor" + }, + "3": { + "name": "Brocken statt Barren", + "boss": "Goldener Grabstein" + }, + "4": { + "name": "Aspho … asphy … scheintot!", + "boss": "Entwickelter Avis" + }, + "5": { + "name": "Geißel Kreszentias", + "boss": "Loploth" + }, + "6": { + "name": "Die Wogen glätte", + "boss": "Nammu" + }, + "7": { + "name": "Dämonenvogel am Spieß", + "boss": "Roch" + }, + "8": { + "name": "Los, Sisyphos, Donnerblitz!", + "boss": "Sisyphos" + }, + "9": { + "name": "Wächter des Lebens", + "boss": "Inselbeobachter" + }, + "10": { + "name": "Seelen sammeln für den bösen Zwec", + "boss": "Seelensammler" + }, + "11": { + "name": "Kampf um Leben und Boden unter den Füßen", + "boss": "Schlickschlamm" + } + }, + "ce": { + "1": { + "name": "Die steinerne Staffel", + "boss": "Megalo-Ritter" + }, + "2": { + "name": "Das finsterste Feuer", + "boss": "Irrwisch" + }, + "3": { + "name": "Gefürchtetes Gebrüll", + "boss": "Ungezügelter Löwe", + "drop": { + "oracle": "Seher-Seelensplitter" + } + }, + "4": { + "name": "Das messerscharfe Schicksal", + "boss": "Todesklaue" + }, + "5": { + "name": "Keine Gnade dem Gefieder", + "boss": "Neo Garula", + "spawn": "Kreszentia-Garula" + }, + "6": { + "name": "Das schwarze Regiment", + "boss": "Schwarzer Stern", + "drop": { + "ranger": "Jäger-Seelensplitter", + "note": "Chronikeintrag „Schwarze Chocobos“" + }, + "spawn": "Kreszentia-Panther" + }, + "7": { + "name": "Tödliche Schönheit", + "boss": "Kristalldrache" + }, + "8": { + "name": "Jäger aus alten Legenden", + "boss": "Nymeischer Petalodus", + "drop": { + "note": "Chronikeintrag „Nymeischer Petalodus“ " + }, + "spawn": "Niederer Kreszentia-Petalodus" + }, + "9": { + "name": "Zorn auf zwei Beinen", + "boss": "Kreszenter Berserker", + "drop": { + "berserker": "Berserker-Seelensplitter", + "note": "Chronikeintrag „Kreszenter Berserker“ " + } + }, + "10": { + "name": "Von uralten Magien", + "boss": "Mystisches Idol", + "drop": { + "note": "Chronikeintrag „Mystisches Idol“" + }, + "spawn": "Kreszentia-Byblos" + }, + "11": { + "name": "Das Feilschen ums Verfluchte", + "boss": "Münzkröte", + "drop": { + "note": "Chronikeintrag „Münzkröte“" + } + }, + "12": { + "name": "Mit absoluter Sicherheit", + "boss": "Kommandopott" + }, + "13": { + "name": "Ausgebessert und verbessert", + "boss": "Restaurierter Löwe" + }, + "14": { + "name": "Das versiegelte Unheil", + "boss": "Klosterdämon", + "drop": { + "note": "Chronikeintrag „Klosterdämon“" + }, + "spawn": "Kreszentia-Tintenfleck" + }, + "15": { + "name": "Die Geißel des Geistes", + "boss": "Gedankenschinder", + "spawn": "Kreszentia-Mönch" + } + }, + "surveyPoint": { + "7": "#7: Die verlorene Festung", + "9": "#9: Trughalde", + "12": "#12: Heidklipp", + "13": "#13: Die unterirdische Stadt", + "15": "#15: Die Stadt der Schemen", + "18": "#18: Holderwuchs", + "19": "#19: Die Leeren Stufen", + "21": "#21: Die verfallenen Hochhäuser", + "22": "#22: Die alte Werkstätte", + "23": "#23: Zuflucht des Wanderers", + "24": "#24: Die Feuerstätte" + }, + "demiatma": { + "orpiment": "Demi-Atma Orpiment", + "caputMortuum": "Demi-Atma Caput Mortuum", + "realgar": "Demi-Atma Realgar", + "malachite": "Demi-Atma Malachit", + "azurite": "Demi-Atma Azurit", + "verdigris": "Demi-Atma Verdigris" + } + } + }, + "category": { + "general": "Allgemein", + "encounters": "Begegnungen", + "enemies": "Gegner", + "progression": "Fortschritt" + }, + "layer": { + "namedLocations": "Benannte Orte", + "aetherytes": "Ätheryten", + "traders": "Händler", + "repair": "Reparatur", + "treasureCoffers": "Schatztruhen", + "carrots": "Wonnemöhrchen", + "fates": "FATEs", + "magicPotFates": "Magietopf-FATEs", + "surveyPoints": "Prüfstelle", + "criticalEngagements": "Kritische Begegnungen", + "magicPotCoffers": "Magietopf-Schatztruhen", + "mobLevels": "Gegner-Stufen", + "demiatmaAreas": "Demiatma-Gebiete" + }, + "regionHeaders": { + "occult": "Kreszentia", + "southhorn": "Südliches Kreszentia" + }, + "captions": { + "ce": "Kritische Begegnungen" + } + } +} \ No newline at end of file diff --git a/src/locales/de/occult.json b/src/locales/de/occult.json new file mode 100644 index 0000000..077404a --- /dev/null +++ b/src/locales/de/occult.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/src/locales/de/zones.json b/src/locales/de/zones.json new file mode 100644 index 0000000..aba1ff3 --- /dev/null +++ b/src/locales/de/zones.json @@ -0,0 +1,39 @@ +{ + "occult": { + "name": "Kreszentia", + "southhorn": { + "full": "Kreszentia (Südexpedition)", + "short": "Südliches Kreszentia" + } + }, + "bozja": { + "name": "Bozja", + "bsf": { + "full": "The Bozjan Southern Front", + "short": "Bozja" + }, + "zadnor": { + "full": "Zadnor", + "short": "Zadnor" + } + }, + "eureka": { + "name": "Eureka", + "hydatos": { + "full": "Eureka Hydatos", + "short": "Hydatos" + }, + "pyros": { + "full": "Eureka Pyros", + "short": "Pyros" + }, + "pagos": { + "full": "Eureka Pagos", + "short": "Pagos" + }, + "anemos": { + "full": "Eureka Anemos", + "short": "Anemos" + } + } +} diff --git a/src/locales/en/bsf.json b/src/locales/en/bsf.json new file mode 100644 index 0000000..87eb846 --- /dev/null +++ b/src/locales/en/bsf.json @@ -0,0 +1,60 @@ +{ + "drs": { + "holster-helper": { + "title": "DRS Holster Helper", + "window-title": "FFXIV Field Operations Assistant - forays.info", + "window-title-viewing-holster": "DRS Holster - {{type}} - forays.info", + "custom": "Custom", + "intro": { + "p1": "Use this tool to see what Lost Actions you should bring on your DRS run.", + "p2": "Are you a DRS host? Use the <0>DRS Run Holster Creator to get your holsters added to this page!" + }, + "last-updated": "(Last Updated: {{updated}})", + "steps": { + "1": { + "label": "Select host", + "description": "Select your run's host" + }, + "2": { + "label": "Select run type", + "description": "Select your run's type." + }, + "3": { + "label": "Select role", + "description": "What role are you playing in DRS?" + }, + "4": { + "label": "Select holster", + "description": "Pick a holster." + } + } + } + }, + "lost-action-helper": { + "title": "Bozja Lost Action Helper", + "action-label": "Action: ", + "appraised-label": "Appraised from" + }, + "lost-action": { + "lost-focus": { + "name": "Lost Focus", + "text": "Grants a stack of Boost, up to a maximum of 16.\nBoost Bonus: Increases potency of next weaponskill by 15% per stack.\nEffect ends upon using another lost action.\nShares a recast timer with all other weaponskills and spells." + }, + "lost-font-of-magic": { + "name": "Lost Font of Magic", + "text": "Increases damage dealt by 70%, draining MP while in use.\nSpirit of the Veteran Effect: Grants Spell Shield to self.\nSpell Shield Effect: Reduces magic damage taken by 50%.\nDuration: 15s\nCan only be executed while in combat." + }, + "lost-font-of-power": { + "name": "Lost Font of Power", + "text": "Increases damage dealt by 30% and critical hit rate by 40%.\nSpirit of the Irregular Effect: Damage bonus is increased to 40%.\nSpirit of the Platebearer Effect: Grants Solid Shield to self.\nSolid Shield Effect: Reduces phyiscal damage taken by 50%.\nDuration: 15s\nCan only be executed while in combat." + }, + "lost-slash": { + "name": "Lost Slash", + "text": "Delivers an attack with a potency of 800 to all enemies in a cone before you. When critical damage is dealt, potency is tripled.\nThis action does not share a recast timer with any other actions. Furthermore, the recast timer cannot be affected by other actions." + }, + "lost-death": { + "name": "Lost Death", + "text": "KOs target. The less the target's HP, the greater the chance of success.\nSpirit of the Ordained Effect: Chance of success is increased.\nThis action does not share a recast timer with any other actions. Furthermore, the recast timer cannot be affected by other actions." + } + } +} diff --git a/src/locales/en/common.json b/src/locales/en/common.json new file mode 100644 index 0000000..ed2ab21 --- /dev/null +++ b/src/locales/en/common.json @@ -0,0 +1,115 @@ +{ + "main": { + "title": "FFXIV Field Operations Assistant", + "header": { + "title": "{{sitename}} - FFXIV Field Operations Assistant" + }, + "intro": { + "p1": "Welcome to {{sitename}}, a home for tools to help with Final Fantasy XIV side content - including the Occult Crescent (Forked Tower), Eureka (The Baldesion Arsenal), and Bozja (Delubrum Reginae Savage). Find out how to build and gather materials for Lost Actions and Logos Actions, what to bring for The Baldesion Arsenal or Delubrum Reginae Savage runs, when the next Cassie or Crab spawn is, and more!", + "p2": "Looking for a group to run Forked Tower, BA, or DRS with? Most of the content on this site was designed for runs on <0>The Help Lines on Primal.", + "p3": "Built by Lynn Kaneko @ Exodus. Issues or suggestions? You can reach out to me on Discord (@lynnkaneko).", + "p4": "Home page artwork by <0>{{artist}}!" + } + }, + "footer": { + "copyright": "FINAL FANTASY XIV © 2010 - 2025 SQUARE ENIX CO., LTD. FINAL FANTASY, FINAL FANTASY XIV, and FFXIV are registered trademarks or trademarks of Square Enix Holdings Co., Ltd. All material used under license.", + "version": "Version {{ver}}" + }, + "sidebar": { + "occult": { + "header": "OCCULT CRESCENT", + "map": "Occult Crescent Maps", + "job": "Phantom Jobs" + }, + "bozja": { + "header": "BOZJA AND DRS", + "holster": "DRS Holsters", + "holstercreator": "Hoslter Creator", + "map": "Bozja Maps", + "lostaction": "Lost Action Info", + "fragmentfarm": "Fragment Farm Times" + }, + "eureka": { + "header": "EUREKA AND BA", + "baloadout": "BA Loadouts", + "bamap": "BA Portal Map", + "map": "Eureka Maps", + "logosaction": "Logos Action Info", + "loadout": "Loadout Creator", + "nm": "NM Spawn Times", + "farm": "Logogram/Box Farm Times" + }, + "forays": { + "header": "FORAYS", + "forecast": "Expeditionary Forecast", + "weather": "Advanced Weather Finder" + }, + "about": { + "header": "ABOUT", + "lynn": "Lynn Kaneko @ Exodus" + }, + "forecast": { + "header": { + "all": "ALL", + "eurekanm": "EUREKA NMS", + "eurekafarm": "EUREKA FARMS", + "bozjafarm": "BOZJA FRAGMENT FARMS" + }, + "cassie": "Copycat Cassie", + "crab": "King Artho", + "skoll": "Skoll", + "pazuzu": "Pazuzu", + "penny": "Penthesilea", + "coldbox": "Cold-Warped Lockbox", + "heatbox": "Heat-Warped Lockbox", + "offensive": "Offensive Logogram", + "conceptual": "Conceptual Logogram", + "mitigative": "Mitigative Logogram", + "preparation": "Preparation", + "care": "Care", + "support": "Support", + "history": "History", + "artistry": "Artistry", + "all-eureka-nms": "All Eureka NMs", + "all-bozja-farms": "All Bozja Fragment Farms", + "all-eureka-farms": "All Eureka Farms", + "all-spawns": "All Upcoming Spawns" + } + }, + "weather-popover": { + "current-time": "Current Time", + "current-weather": "Current Weather" + }, + "skill-type": { + "ability": "Ability", + "weaponskill": "Weaponskill", + "spell": "Spell" + }, + "action-detail-label": { + "duration": "<0>Duration: <1>{{duration}}", + "cast": "<0>Cast: <1>{{cast}}s", + "recast": "<0>Recast: <1>{{recast}}s", + "mp": "<0>MP: <1>{{mp}}", + "charges": "<0>Charges: <1>{{charges}}", + "rank": "Rank: {{rank}}", + "usable-by": "Usable by: ", + "unlock": "Unlock:" + }, + "settings": { + "title": "Settings", + "theme_title": "Site Theme", + "themes": { + "light": "Light", + "dark": "Dark", + "system": "System" + }, + "language_title": "Language", + "universalis_region_title": "Universalis Region" + }, + "common": { + "home": "Home", + "close": "Close", + "back": "Back", + "start-over": "Start Over" + } +} \ No newline at end of file diff --git a/src/locales/en/eureka.json b/src/locales/en/eureka.json new file mode 100644 index 0000000..0db3279 --- /dev/null +++ b/src/locales/en/eureka.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/src/locales/en/map.json b/src/locales/en/map.json new file mode 100644 index 0000000..a1db084 --- /dev/null +++ b/src/locales/en/map.json @@ -0,0 +1,572 @@ +{ + "map": { + "regions": { + "southhorn": { + "namedLocation": { + "1": "Solitary Pavilion", + "2": "Occult Cloister" + }, + "aetheryte": { + "1": "Expedition Base Camp", + "2": "The Wanderer's Haven", + "3": "Crystallized Caverns", + "4": "Stonemarsh", + "5": "Eldergrowth" + }, + "trader": { + "1": "Expedition Antiquarian", + "2": "Expedition Trader", + "3": "Expedition Armorer" + }, + "repair": "Repair", + "treasureCoffer": { + "bronze": "Bronze Treasure Coffer Spawn Location", + "silver": "Silver Treasure Coffer Spawn Location", + "pot": "Magic Pot Coffer Spawn Location", + "carrot": "Half-Eaten Carrot Spawn Location", + "carrot_unv": "Half-Eaten Carrot Spawn Location (Unverified)" + }, + "fate": { + "pot1": { + "name": "Persistent Pots" + }, + "pot2": { + "name": "Pleading Pots" + }, + "1": { + "name": "A Delicate Balance", + "boss": "Dehumidifier" + }, + "2": { + "name": "Fatal Allure", + "boss": "Execrator" + }, + "3": { + "name": "The Golden Guardian", + "boss": "Gilded Headstone" + }, + "4": { + "name": "Brain Drain", + "boss": "Advanced Aevis" + }, + "5": { + "name": "King of the Crescent", + "boss": "Ropross" + }, + "6": { + "name": "Rough Waters", + "boss": "Nammu" + }, + "7": { + "name": "The Winged Terror", + "boss": "Giant Bird" + }, + "8": { + "name": "An Unending Duty", + "boss": "Sisyphus" + }, + "9": { + "name": "A Prying Eye", + "boss": "Observer" + }, + "10": { + "name": "Serving Darkness", + "boss": "Lifereaper" + }, + "11": { + "name": "Sworn to Soil", + "boss": "Mad Mudarch" + } + }, + "ce": { + "1": { + "name": "Company of Stone", + "boss": "Megaloknight" + }, + "2": { + "name": "Flame of Dusk", + "boss": "Hinkypunk" + }, + "3": { + "name": "On the Hunt", + "boss": "Lion Rampant", + "drop": { + "oracle": "Oracle's Soul Shard" + }, + "spawn": "Crescent Fan" + }, + "4": { + "name": "Crawling Death", + "boss": "Death Claw" + }, + "5": { + "name": "Noise Complaint", + "boss": "Neo Garula", + "spawn": "Crescent Garula" + }, + "6": { + "name": "The Black Regiment", + "boss": "Black Star", + "drop": { + "ranger": "Ranger's Soul Shard", + "note": "Notes on Black Chocobos" + }, + "spawn": "Crescent Panther" + }, + "7": { + "name": "Trial by Claw", + "boss": "Crystal Dragon" + }, + "8": { + "name": "Shark Attack", + "boss": "Nymian Petalodus", + "drop": { + "note": "Notes on the Nymian Petalodus" + }, + "spawn": "Crescent Petalodite" + }, + "9": { + "name": "The Unbridled", + "boss": "Crescent Berserker", + "drop": { + "berserker": "Berserker's Soul Shard", + "note": "Notes on the Crescent Berserker" + } + }, + "10": { + "name": "From Times Bygone", + "boss": "Mythic Idol", + "drop": { + "note": "Notes on the Mythic Idol" + }, + "spawn": "Crescent Byblos" + }, + "11": { + "name": "Cursed Concern", + "boss": "Trade Tortoise", + "drop": { + "note": "Notes on the Trade Tortoise" + } + }, + "12": { + "name": "With Extreme Prejudice", + "boss": "Command Urn" + }, + "13": { + "name": "Eternal Watch", + "boss": "Repaired Lion" + }, + "14": { + "name": "Calamity Bound", + "boss": "Cloister Demon", + "drop": { + "note": "Notes on the Cloister Demon" + }, + "spawn": "Crescent Inkstain" + }, + "15": { + "name": "Scourge of the Mind", + "boss": "Mysterious Mindflayer", + "spawn": "Crescent Monk" + } + }, + "surveyPoint": { + "7": "#7: The Lost Citadel", + "9": "#9: The Vanishing Slope", + "12": "#12: Heathcliff", + "13": "#13: The Fell Warren", + "15": "#15: The Shadowed City", + "18": "#18: Eldergrowth", + "19": "#19: The Abandoned Ascent", + "21": "#21: The Silent Streets", + "22": "#22: The Ancient Stoneworks", + "23": "#23: The Wanderer's Haven", + "24": "#24: The Brazier" + }, + "demiatma": { + "orpiment": "Orpiment Demiatma", + "caputMortuum": "Caput Mortuum Demiatma", + "realgar": "Realgar Demiatma", + "malachite": "Malachite Demiatma", + "azurite": "Azurite Demiatma", + "verdigris": "Verdigris Demiatma" + } + }, + "bsf": { + "namedLocation": { + "1": "Orphaned Ruins", + "2": "Firelight's Coffin", + "3": "Castrum Lacus Litore", + "4": "The Alermuc Climb", + "5": "Old Bozja", + "6": "Southern Entrenchment" + }, + "lostFindsCache": "Lost Finds Cache", + "trader": { + "quartermaster": "Resistance Quartermaster", + "historian": "Resistance Historian", + "supplier": "Resistance Supplier", + "appraiser": "Resistance Appraiser", + "locksmith": "Resistance Locksmith" + }, + "aetheryte": { + "1": "Utya's Aegis", + "2": "Olana's Stand", + "3": "Lunya's Stand", + "4": "Camp Steva" + }, + "skirmish": { + "1": { + "name": "All Pets Are Off" + }, + "2": { + "name": "Brought to Heal" + }, + "3": { + "name": "Can Carnivorous Plants Bloom Even on a Battlefield?" + }, + "4": { + "name": "Conflicting with the First Law" + }, + "5": { + "name": "More Machine Now than Man" + }, + "6": { + "name": "None of Them Knew They Were Robots" + }, + "7": { + "name": "Seeq and Destroy" + }, + "8": { + "name": "Sneak and Spell" + }, + "9": { + "name": "The Beasts Must Die" + }, + "10": { + "name": "Unrest for the Wicked" + }, + "11": { + "name": "Heavy Boots of Lead" + }, + "12": { + "name": "Help Wanted" + }, + "13": { + "name": "No Camping Allowed" + }, + "14": { + "name": "Parts and Recreation" + }, + "15": { + "name": "Pyromancer Supreme" + }, + "16": { + "name": "Red (Chocobo) Alert" + }, + "17": { + "name": "Scavengers of Man's Sorrow" + }, + "18": { + "name": "The Element of Supplies" + }, + "19": { + "name": "The Monster Mash" + }, + "20": { + "name": "Unicorn Flakes" + }, + "21": { + "name": "Demonstrably Demonic" + }, + "22": { + "name": "Desperately Seeking Something" + }, + "23": { + "name": "For Absent Friends" + }, + "24": { + "name": "I'm a Mechanical Man" + }, + "25": { + "name": "Let Slip the Dogs of War" + }, + "26": { + "name": "Murder Death Kill" + }, + "27": { + "name": "My Family and Other Animals" + }, + "28": { + "name": "Of Steel and Flame" + }, + "29": { + "name": "Supplies Party" + }, + "30": { + "name": "The War Against the Machines" + }, + "31": { + "name": "The Wild Bunch" + }, + "32": { + "name": "Waste the Rainbow" + } + }, + "ce": { + "1": { + "name": "Kill It with Fire", + "boss": "Peerifool", + "drop": { + "note": "Field Notes on Clarricie", + "fabric": "Garlean Synthetic Fabric" + }, + "spawn": "All Pets Are Off" + }, + "2": { + "name": "The Baying of the Hound(s)", + "boss": "Canus Dirus", + "spawn": "4th Legion Death Claw" + }, + "3": { + "name": "The Shadow of Death's Hand", + "boss": "Akbaba", + "drop": { + "note": "Field Notes on Xeven" + }, + "spawn": "4th Legion Roader" + }, + "4": { + "name": "Vigil for the Lost", + "boss": "Vigilia", + "spawn": "More Machine Now than Man" + }, + "5": { + "name": "Patriot Games", + "boss": "Patriot", + "spawn": "The Fires of War" + }, + "6": { + "name": "The Final Furlong", + "boss": "Spartoi", + "drop": { + "note": "Field Notes on Llofii" + }, + "spawn": "Unicorn Flakes" + }, + "7": { + "name": "The Fires of War", + "boss": "Pyrobolus Mater", + "spawn": "4th Legion Gunship" + }, + "8": { + "name": "The Hunt for Red Choctober", + "boss": "Red Comet", + "spawn": "Red (Chocobo) Alert" + }, + "9": { + "name": "Metal Fox Chaos", + "boss": "Dáinsleif", + "drop": { + "resolve": "10x Forgotten Fragment of Resolve" + }, + "spawn": "Of Steel and Flame" + }, + "10": { + "name": "Rise of the Robots", + "boss": "Mark XIII-X Magitek Laborer", + "drop": { + "note": "Field Notes on Sicinius" + }, + "spawn": "I'm a Mechanical Man" + }, + "11": { + "name": "Trampled under Hoof", + "boss": "Eale", + "drop": { + "resolve": "10x Forgotten Fragment of Resolve" + }, + "spawn": "4th Legion Armored Weapon" + }, + "12": { + "name": "Where Strode the Behemoth", + "boss": "Chlevnik", + "drop": { + "resolve": "10x Forgotten Fragment of Resolve" + }, + "spawn": "Trampled under Hoof" + } + }, + "duel": { + "1": { + "name": "Aces High" + }, + "2": { + "name": "Beast of Man" + }, + "3": { + "name": "And the Flames Went Higher" + } + } + }, + "hydatos": { + "namedLocation": { + "1": "The Aetherbridge Foundation", + "2": "The Western Columns", + "3": "The Central Columns", + "4": "The Eastern Columns", + "5": "The Crystal Dragon's Bloom" + }, + "aetheryte": { + "1": "Central Point", + "2": "Unverified Research", + "3": "The Dormitory" + }, + "elemental": "Elemental Spawn Location", + "logosManipulator": "Logos Manipulator", + "magiaMelder": "Magia Melder", + "trader": { + "trader": "Trader", + "shop": "Shop", + "engineer": "Expedition Engineer", + "alchemist": "Expedition Alchemist", + "drake": "Drake", + "gear": "Eureka Weapons & Gear" + }, + "repair": "Repair", + "coffer": "Bunny Coffer Spawn Location", + "nm": { + "50": { + "name": "Lv50 Khalamari", + "title": "I Ink, Therefore I Am", + "boss": "Khalamari", + "spawn": "Xzomit" + }, + "bunny": { + "name": "Bunny", + "title": "Drink Me", + "boss": "Bunny FATE" + }, + "51": { + "name": "Lv51 Stegodon", + "title": "From Tusk till Dawn", + "boss": "Stegodon", + "spawn": "Hydatos Primelephas" + }, + "52": { + "name": "Lv52 Molech", + "title": "Bullheaded Berserker", + "boss": "Molech", + "spawn": "Val Nullchu" + }, + "53": { + "name": "Lv53 Piasa", + "title": "Mad, Bad, and Fabulous to Know", + "boss": "Piasa", + "spawn": "Vivid Gastornis" + }, + "54": { + "name": "Lv54 Frostmane", + "title": "Fearful Symmetry", + "boss": "Frostmane", + "spawn": "Northern Tiger" + }, + "55": { + "name": "Lv55 Daphne", + "title": "Crawling Chaos", + "boss": "Daphne", + "spawn": "Dark Void Monk" + }, + "56": { + "name": "Lv56 King Goldemar", + "title": "Duty-free", + "boss": "King Goldemar", + "spawn": "Hydatos Wraith" + }, + "57": { + "name": "Lv57 Leuke", + "title": "Leukewarm Reception", + "boss": "Leuke", + "spawn": "Tigerhawk" + }, + "58": { + "name": "Lv58 Barong", + "title": "Robber Barong", + "boss": "Barong", + "spawn": "Laboratory Lion" + }, + "59": { + "name": "Lv59 Ceto", + "title": "Stone-cold Killer", + "boss": "Ceto", + "spawn": "Hydatos Delphine" + }, + "60": { + "name": "Lv60 Provenance Watcher", + "title": "Crystalline Provenance", + "boss": "Provenance Watcher", + "spawn": "Crystal Claw" + }, + "ovni": { + "name": "Ovni", + "title": "I Don't Want to Believe", + "boss": "Ovni" + }, + "support": { + "name": "Support", + "title": "The Baldesion Arsenal: Expedition Support", + "boss": "Tristitia" + } + } + } + }, + "category": { + "general": "General", + "encounters": "Encounters", + "enemies": "Enemies", + "progression": "Progression" + }, + "layer": { + "namedLocations": "Named Locations", + "aetherytes": "Aetherytes", + "traders": "Traders", + "repair": "Repair", + "treasureCoffers": "Treasure Coffers", + "carrots": "Half-Eaten Carrots", + "fates": "FATEs", + "magicPotFates": "Magic Pot FATEs", + "criticalEngagements": "Critical Engagements", + "magicPotCoffers": "Magic Pot Coffers", + "mobLevels": "Mob Levels", + "surveyPoints": "Survey Points", + "demiatmaAreas": "Demiatma Areas", + "shops": "Shops", + "skirmishes": "Skirmishes", + "duels": "Duels", + "starMobs": "Star Mobs", + "magitekMobs": "Magitek Mobs", + "fragmentMobs": "Fragment Mobs", + "quests": "Quests", + "fieldNotes": "Field Notes", + "lostFindCaches": "Lost Finds Caches", + "elementals": "Elementals", + "logosManipulators": "Logos Manipulators", + "magiaMelders": "Magia Melders", + "nms": "NMs", + "bunnyCoffers": "Bunny Coffers", + "baPortals": "BA Portals", + "mobs": "Mobs", + "nmPrepMobs": "NM Prep Mobs", + "sprites": "Sprites" + }, + "captions": { + "ce": "Critical Engagement", + "boss": "Boss", + "spawnedBy": "Spawned by", + "rewards": "Rewards", + "unlocksAt": "Unlocks at", + "level": "Level", + "nm": "Notorious Monster" + } + } +} \ No newline at end of file diff --git a/src/locales/en/occult.json b/src/locales/en/occult.json new file mode 100644 index 0000000..e4e4d89 --- /dev/null +++ b/src/locales/en/occult.json @@ -0,0 +1,343 @@ +{ + "job-helper": { + "title": "Occult Crescent Phantom Job Helper", + "label": "Phantom Job: ", + "total-exp": "Total EXP: {{exp}}", + "leveling-up": "Leveling Up", + "freelancer-leveling": "Freelancer is a special Phantom Job. Instead of leveling up via Phantom Job experience points, Freelancer gains levels based on how many Phantom Jobs you've mastered. Freelancer starts at Level 1, and gains a level each time you master a Phantom Job.", + "enhancement": "Lv {{level}} Trait: {{effect}}", + "support-actions": "Support Actions", + "none": "None" + }, + "phantom-job": { + "freelancer": { + "name": "Freelancer", + "text": "Reminiscences of journeys just begun and those long ended embody the freelancer's myriad supportive techniques.", + "unlock": "Unlocked by default." + }, + "knight": { + "name": "Knight", + "text": "Emulate these ancient memories of a loyal, honorable knight and defend your comrades from harm.", + "unlock": "South Horn intro quests." + }, + "monk": { + "name": "Monk", + "text": "These memories of a monk's unflinching discipline and quiet meditation offer a path to unparalleled combat mastery.", + "unlock": "South Horn intro quests." + }, + "time-mage": { + "name": "Time Mage", + "text": "The memories of this former time mage reveal much about the laws of time and how to manipulate them to your advantage.", + "unlock": "Purchase - Time Mage's Soul Shard - 1,000 Silver Pieces from the Expedition Antiquarian." + }, + "bard": { + "name": "Bard", + "text": "These memories echo with the vivid imagery of a bard's uplifting song. Utilize them to employ the very same ballads in battle.", + "unlock": "South Horn intro quests." + }, + "geomancer": { + "name": "Geomancer", + "text": "This geomancer's recollections brim with boundless understanding of nature's laws. Access them to employ weather-dependent techniques.", + "unlock": "Purchase - Geomancer's Soul Shard - 1,600 Gold Pieces from the Expedition Antiquarian." + }, + "ranger": { + "name": "Ranger", + "text": "Channel these memories of a proud, passionate ranger and learn to call forth the forces of nature in battle.", + "unlock": "Unlocked from the Ranger's Soul Shard, acquired from the Critical Engagement \"The Black Regiment.\"" + }, + "berserker": { + "name": "Berserker", + "text": "Savagery and unfailing courage characterize the recollections of this former berserker. These techniques entail great risk and even greater reward.", + "unlock": "Unlocked from the Berserker's Soul Shard, acquired from the Critical Engagement \"The Unbridled.\"" + }, + "chemist": { + "name": "Chemist", + "text": "This chemist's memories offer knowledge of ancient medicinal theory, including restorative techniques sure to turn the tide of battle in your favor.", + "unlock": "Purchase - Chemist's Soul Shard - 1,000 Silver Pieces from the Expedition Antiquarian." + }, + "cannoneer": { + "name": "Cannoneer", + "text": "A mastery of ballistics both scholarly and practical infuse this cannoneer's recollections. Emulate them to bombard foes with powerful projectiles.", + "unlock": "Purchase - Cannoneer's Soul Shard - 1,000 Silver Pieces from the Expedition Antiquarian." + }, + "samurai": { + "name": "Samurai", + "text": "Rigorous training and a strict code of chivalry personify these recollections of an ancient samurai. Access them to fell foes in a single deadly strike.", + "unlock": "Purchase - Samurai's Soul Shard - 1,600 Gold Pieces from the Expedition Antiquarian." + }, + "thief": { + "name": "Thief", + "text": "These recollections of a dextrous, principled thief grant access to survival techniques that will doubtless prove useful upon the isle.", + "unlock": "Purchase - Thief's Soul Shard - 1,600 Gold Pieces from the Expedition Antiquarian." + }, + "oracle": { + "name": "Oracle", + "text": "Endowed with an oracle's supernatural senses and prophetic insights, these memories promise mastery over a wealth of supportive skills.", + "unlock": "Unlocked from the Oracle's Soul Shard, acquired from the Critical Engagement \"On The Hunt.\"" + } + }, + "support-action": { + "fr1": { + "name": "Occult Resuscitation", + "description": "Restores 30% of maximum HP." + }, + "fr2": { + "name": "Occult Treasuresight", + "description": "Detects the number of treasure coffers presently on the Occult Crescent." + }, + "kn1": { + "name": "Phantom Guard", + "description": "Reduces damage taken by 60%.", + "enhancement": { + "4": "Improves Phantom Guard's damage reduction effect to 90%." + } + }, + "kn2": { + "name": "Pray", + "description": "Gradually restores HP.\nCure Potency: 3,000\nAdditional Effect: Grants Enduring Fortitude to self and nearby party members when executed within proximity of a knowledge crystal.\nEnduring Fortitude Effect: Reduces target's damage taken by 10% (duration: 30m).", + "enhancement": { + "5": "Increases the healing potency of Pray to 5,000." + } + }, + "kn3": { + "name": "Occult Heal", + "description": "Restores target's HP.\nCure Potency: 30,000" + }, + "kn4": { + "name": "Pledge", + "description": "Grants 2 stacks of Pledge to self or target party member.\nPledge Effect: Renders target impervious to most attacks." + }, + "mo1": { + "name": "Phantom Kick", + "description": "Leap towards your target, delivering an attack with a potency of 100 to all enemies in a straight line before you.\nPotency scales with item level.\nAdditional Effect: Grants a stack of Phantom Kick up to a maximum of 3, increasing damage dealt by 5%.", + "enhancement": { + "4": "Improves Phantom Kick's damage increase to 7%.", + "5": "Improves Phantom Kick's damage increase to 9%." + } + }, + "mo2": { + "name": "Occult Counter", + "description": "Delivers an attack with a potency of 150 to all enemies in a cone before you.\nPotency scales with item level.\nCan only be executed immediately after parrying an attack." + }, + "mo3": { + "name": "Counterstance", + "description": "Increases parry rate by 100%.\nAdditional Effect: Grants Fleetfooted to self and nearby party members when executed within proximity of a knowledge crystal.\nFleetfooted Effect: Increases movement speed for 30m." + }, + "mo4": { + "name": "Occult Chakra", + "description": "Restores 30% of maximum HP and MP.\nAdditional Effect: When HP is less than 30%, restores 70% of maximum HP.\nAdditional Effect: When MP is less than 30%, restores 70% of maximum MP." + }, + "ti1": { + "name": "Occult Slowga", + "description": "Afflict target and all enemies nearby it with Slow +80%." + }, + "ti2": { + "name": "Occult Comet", + "description": "Deals unaspected damage with a potency of 500 to target and all enemies nearby it.\nPotency scales with item level." + }, + "ti3": { + "name": "Occult Mage Masher", + "description": "Lowers target's magic attack power by 10%." + }, + "ti4": { + "name": "Occult Dispel", + "description": "Removes one beneficial status from target, with some exceptions." + }, + "ti5": { + "name": "Occult Quick", + "description": "Reduces weaponskill cast time and recast time, spell cast time and recast time, and auto-attack delay of self or target party member by 10%, and reduces cast times for spells by 10 seconds.\nAdditional Effect: Greatly increases movement speed for 10s." + }, + "ba1": { + "name": "Offensive Aria", + "description": "Increases damage dealt by self and nearby party members by 1%.", + "enhancement": { + "2": "Improves Offensive Aria's damage increase to 2%.", + "3": "Improves Offensive Aria's damage increase to 3%.", + "4": "Improves Offensive Aria's damage increase to 4%." + } + }, + "ba2": { + "name": "Romeo's Ballad", + "description": "Freezes time for all nearby enemies readying an attack.\nAdditional Effect: Grants Romeo's Ballad to self and nearby party members when executed within proximity of a knowledge crystal\nRomeo's Ballad Effect: Increases Phantom EXP earned through battle by 10% for 30m." + }, + "ba3": { + "name": "Mighty March", + "description": "Gradually restores own HP and the HP of all nearby party members.\nCure Potency: 3,000\nAdditional Effect: Increases maximum HP by 20% and restores the amount increased.", + "enhancement": { + "4": "Improves Mighty March's Cure potency to 3,500." + } + }, + "ba4": { + "name": "Hero's Rime", + "description": "Increases damage dealt and healing potency of self and nearby party members by 10%, while reducing damage taken by 10%.\nEffect cannot be stacked with Offensive Aria." + }, + "ge1": { + "name": "Battle Bell", + "description": "Grants Battle Bell to self or target player.\nBattle Bell Effect: Grants a stack of Battle's Clangor upon taking damage, up to a maximum of 8\nBattle's Clangor Effect: Increases damage dealt by 3% for 30s.", + "enhancement": { + "5": "Increases the effectiveness of Battle Bell to 5%." + } + }, + "ge2": { + "name": "Weather", + "description": "Grants a beneficial effect to self and nearby party members based on the weather.\nSunbath Effect: Restores own HP and the HP of nearby party members. Cure Potency: 50,000.\nCloudy Caress: Increases HP recovery via healing actions for self and nearby party members by 30%.\nBlessed Rain Effect: Creates a barrier around self and nearby party members that absorbs 30,000 damage.\nMisty Mirage Effect: Increases evasion for self and nearby party members by 40%.\nHasty Mirage Effect: Increases movement speed for self and nearby party members by 20%.\nAetherial Gain Effect: Increases damage dealt by self and nearby party members by 10%." + }, + "ge3": { + "name": "Ringing Respite", + "description": "Grants Ringing Respite to self or target player.\nRinging Respite Effect: Heals self or target player when damage is taken.\nCure Potency: 2,000", + "enhancement": { + "5": "Increases the effectiveness of Ringing Respite to 3,000." + } + }, + "ge4": { + "name": "Suspend", + "description": "Grants Suspend to self or target party member, becoming lighter than air and immune to certain mechanics." + }, + "ra1": { + "name": "Phantom Aim", + "description": "Increases critical hit rate and direct hit rate by 15%, and guarantees that all attacks land.", + "enhancement": { + "3": "Improves Phantom Aim's critical hit rate and direct hit rate increase to 30%, and guarantees all attacks land.", + "5": "Improves Phantom Aim's critical hit rate and direct hit rate increase to 50%, and guarantees all attacks land." + } + }, + "ra2": { + "name": "Occult Featherfoot", + "description": "Quickly dash 15 yalms forward." + }, + "ra3": { + "name": "Occult Falcon", + "description": "Delivers an attack with a potency of 10 to all enemies at the designated area.\nPotency scales with item level.\nAdditional Effect: Stun\nAdditional Effect: Triggers traps at the designated area." + }, + "ra4": { + "name": "Occult Unicorn", + "description": "Creates a barrier around self and all party members near you that absorbs 40,000 damage." + }, + "be1": { + "name": "Rage", + "description": "Become enraged, delivering an attack with a potency of 150 to all enemies in a cone before you.\nPotency scales with item level.\nImmune to certain status effects, as well as most knockback and draw-in effects." + }, + "be2": { + "name": "Deadly Blow", + "description": "Delivers an attack with a potency of 200.\nPotency scales with item level.\nAdditional Effect: Potency increases by up to 250 as your HP decreases.\nAdditional Effect: Increases potency up to 2,000 based on the amount of damage taken while under the effect of Pent-up Rage." + }, + "ch1": { + "name": "Occult Potion", + "description": "Consume an Occult Potion to completely restore HP of self or target.\nCan only be executed while you possess an Occult Potion." + }, + "ch2": { + "name": "Occult Ether", + "description": "Consume an Occult Potion to completely restore MP of self or target.\nCan only be executed while you possess an Occult Potion." + }, + "ch3": { + "name": "Revive", + "description": "Resurrects target to a weakened state.\nCan also be cast on targets with Resurrection Restriction." + }, + "ch4": { + "name": "Occult Elixir", + "description": "Consume an Occult Elixir to completely restore HP and MP of self and nearby party members.\nCan only be executed while you possess an Occult Elixir." + }, + "ca1": { + "name": "Phantom Fire", + "description": "Deals unaspected damage with a potency of 210 to target and all enemies nearby it.\nPotency scales with item level.", + "enhancement": { + "5": "Grants a 5% chance to instantly kill targets when executing Phantom Fire, with some exceptions." + } + }, + "ca2": { + "name": "Holy Cannon", + "description": "Deals unaspected damage with a potency of 210 to target and all enemies nearby it.\nPotency increases to 500 when used against undead enemies.\nPotency scales with item level." + }, + "ca3": { + "name": "Dark Cannon", + "description": "Deals unaspected damage with a potency of 210 to target and all enemies nearby it.\nPotency scales with item level.\nAdditional Effect: Blind" + }, + "ca4": { + "name": "Shock Cannon", + "description": "Deals lightning damage with a potency of 210 to target and all enemies nearby it.\nPotency scales with item level.\nAdditional Effect: Paralysis\nShares a recast timer with Dark Cannon." + }, + "ca5": { + "name": "Silver Cannon", + "description": "Deals unaspected damage with a potency of 210 to target and all enemies nearby it.\nPotency scales with item level.\nAdditional Effect: Afflicts target with Silver Sickness, reducing damage target deals by 10% and increasing damage taken by 5%.\nShares a recast timer with Holy Cannon." + }, + "sa1": { + "name": "Mineuchi", + "description": "Stuns target." + }, + "sa2": { + "name": "Shirahadori", + "description": "Renders you impervious to physical damage for one attack." + }, + "sa3": { + "name": "Iainuki", + "description": "Delivers an attack with a potency of 300 to all enemies in a cone before you.\nPotency scales with item level.\nAdditional Effect: 5% chance to kill target instantly, with some exceptions.", + "enhancement": { + "5": "Increases the potency of Iainuki to 500, and increases chance to instantly kill target to 10%." + } + }, + "sa4": { + "name": "Zeninage", + "description": "Consume an Occult Coffer to deliver a guaranteed strike with a potency of 1,500.\nPotency scales with item level.\nCan only be executed while you possess an Occult Coffer." + }, + "th1": { + "name": "Occult Sprint", + "description": "Greatly increases movement speed." + }, + "th2": { + "name": "Steal", + "description": "Increases the chance of additional items being dropped by target if Steal is dealt before, or as, the finishing blow." + }, + "th3": { + "name": "Vigilance", + "description": "Grants Vigilance.\nEffect changes to Foreseen Offense when entering combat.\nForeseen Offense Effect: Increases critical hit rate by 60% for 20s.\nCannot be executed while in combat." + }, + "th4": { + "name": "Trap Detection", + "description": "Reveals all traps and secret passages within a 15-yalm radius.\nCan only be executed when inside a dungeon." + }, + "th5": { + "name": "Pilfer Weapon", + "description": "Lowers target's physical attack power by 10%." + }, + "th6": { + "name": "Lockpicker", + "description": "Allows you to open locked doors in dungeons." + }, + "or1": { + "name": "Predict", + "description": "Grants Prediction of Judgment, Prediction of Cleansing, Prediction of Blessing, or Prediction of Starfall, allowing the execution of an action depending on the prophecy granted.\nPhantom Judgment Action Effect: Deals unaspected damage to nearby enemies while healing self and nearby party members\nCleansing Action Effect: Deals unaspected damage to nearby enemies and freezes time\nBlessing Action Effect: Heals self and nearby party members, as well as granting Regen\nStarfall Action Effect: Deals unaspected damage to self and nearby enemies\nWhen the effect of a prediction expires, a new prophecy is granted. If all predictions expire without a commensurate action being used, the prophecy changes to False Prediction, causing damage over time to self with a potency of 50,000." + }, + "or1a": { + "name": "Phantom Judgment", + "description": "Deals unaspected damage with a potency of 400 to all nearby enemies.\nPotency scales with item level.\nAdditional Effect: Restores own HP and the HP of all nearby party members.\nCure Potency: 40,000" + }, + "or1b": { + "name": "Cleansing", + "description": "Deals unaspected damage with a potency of 500 to all nearby enemies.\nPotency scales with item level.\nAdditional Effect: Freezes time for nearby enemies readying an attack" + }, + "or1c": { + "name": "Blessing", + "description": "Restores own HP and the HP of all nearby party members.\nCure Potency: 70,000\nAdditional Effect: Regen\nCure Potency: 3,000\nDuration: 15s" + }, + "or1d": { + "name": "Starfall", + "description": "Deals unaspected damage to self totaling up to 90% of maximum HP, and to all nearby enemies with a potency of 1,000.\nPotency against enemies increases according to your item level." + }, + "or2": { + "name": "Recuperation", + "description": "Grants Recuperation to self or target party member.\nWhen effect expires, removes a single detrimental effect from target and all nearby party members.\nRecuperation changes to Fortified Recuperation if effect is not triggered.\nFortified Recuperation Effect: Removes a detrimental effect from target and nearby party members if afflicted with a detrimental effect\nDuration: 3s\nShares a recast timer with Phantom Doom." + }, + "or3": { + "name": "Phantom Doom", + "description": "Chance to afflict target with Phantom Doom.\nTarget is incapacitated when effect expires.\nShares a recast timer with Recuperation.\nHas no effect on targets outside of combat.\nEffect ends upon target leaving combat" + }, + "or4": { + "name": "Phantom Rejuvenation", + "description": "Grants Phantom Rejuvenation to self or target party member.\nPhantom Rejuvenation Effect: Restores HP of target and nearby party members within 30 yalms when effect expires\nCure Potency: 90,000" + }, + "or5": { + "name": "Invulnerability", + "description": "Prevents attacks from reducing a target party member's HP to less than 1." + } + } +} diff --git a/src/locales/en/zones.json b/src/locales/en/zones.json new file mode 100644 index 0000000..4e5baa9 --- /dev/null +++ b/src/locales/en/zones.json @@ -0,0 +1,39 @@ +{ + "occult": { + "name": "Occult Crescent", + "southhorn": { + "full": "The Occult Crescent: South Horn", + "short": "South Horn" + } + }, + "bozja": { + "name": "Bozja", + "bsf": { + "full": "The Bozjan Southern Front", + "short": "Bozja" + }, + "zadnor": { + "full": "Zadnor", + "short": "Zadnor" + } + }, + "eureka": { + "name": "Eureka", + "hydatos": { + "full": "Eureka Hydatos", + "short": "Hydatos" + }, + "pyros": { + "full": "Eureka Pyros", + "short": "Pyros" + }, + "pagos": { + "full": "Eureka Pagos", + "short": "Pagos" + }, + "anemos": { + "full": "Eureka Anemos", + "short": "Anemos" + } + } +} diff --git a/src/locales/fr/map.json b/src/locales/fr/map.json new file mode 100644 index 0000000..2da9f46 --- /dev/null +++ b/src/locales/fr/map.json @@ -0,0 +1,201 @@ +{ + "map": { + "regions": { + "southhorn": { + "namedLocation": { + "1": "Pavillon solitaire", + "2": "Cloître occulte" + }, + "aetheryte": { + "1": "Camp de base", + "2": "Refuge du Vagabond", + "3": "Cavernes de cristal", + "4": "Lagune lapidaire", + "5": "Vieilleracine" + }, + "trader": { + "1": "Numismate de l'expédition", + "2": "Fournisseur de l'expédition", + "3": "Équipementier de l'expédition" + }, + "repair": "Réparations", + "treasureCoffer": { + "bronze": "Bronze Treasure Coffer Spawn Location", + "silver": "Silver Treasure Coffer Spawn Location", + "pot": "Magic Pot Coffer Spawn Location", + "carrot": "Half-Eaten Carrot Spawn Location", + "carrot_unv": "Half-Eaten Carrot Spawn Location (Unverified)" + }, + "fate": { + "pot1": { + "name": "Persistent Pots" + }, + "pot2": { + "name": "Pleading Pots" + }, + "1": { + "name": "Défi : humidité zéro", + "boss": "Déshumidificateur" + }, + "2": { + "name": "Défi : la belle mort", + "boss": "Bourrelle" + }, + "3": { + "name": "Défi : tombe vingt-quatre carats", + "boss": "Pierre tombale dorée" + }, + "4": { + "name": "Défi : le poison de l'évolution", + "boss": "Eibis évolué" + }, + "5": { + "name": "Défi : la bête sadique", + "boss": "Ropross" + }, + "6": { + "name": "Défi : pas de vagues", + "boss": "Nammu" + }, + "7": { + "name": "Défi : je fus zu", + "boss": "Oiseau géant" + }, + "8": { + "name": "Défi : pierre de tonnerre", + "boss": "Sisyphos" + }, + "9": { + "name": "Défi : un œil sur l'île", + "boss": "Insulobservateur" + }, + "10": { + "name": "Défi : collecte mortelle", + "boss": "Collecteur de vies" + }, + "11": { + "name": "Défi : rester de boue", + "boss": "Boue démente" + } + }, + "ce": { + "1": { + "name": "L'armée des argileux", + "boss": "Mégalochevalier" + }, + "2": { + "name": "Défi : les ailes de pierre", + "boss": "Follet folâtre" + }, + "3": { + "name": "Défi : à rugir debout", + "boss": "Lion rampant", + "drop": { + "oracle": "Éclat d'âme de devin" + }, + "spawn": "Ventilateur de Lunule" + }, + "4": { + "name": "Défi : face de griffes", + "boss": "Griffe de mort" + }, + "5": { + "name": "Défi : mammouth fâché", + "boss": "Néo-garula", + "spawn": "Garula de Lunule" + }, + "6": { + "name": "Plumes d'encre", + "boss": "Étoile noire", + "drop": { + "ranger": "Éclat d'âme de rôdeur", + "note": "Article sur les chocobos noirs " + }, + "spawn": "Coeurl de Lunule" + }, + "7": { + "name": "Défi : écailles de cristal", + "boss": "Dragon cristallin" + }, + "8": { + "name": "Défi : homo selachus", + "boss": "Petalodus de Nym", + "drop": { + "note": "Article sur le petalodus de Nym " + }, + "spawn": "Petalodus inférieur de Lunule" + }, + "9": { + "name": "Défi : le rageux", + "boss": "Berserker de Lunule", + "drop": { + "berserker": "Éclat d'âme de berserker", + "note": "Article sur le berserker de Lunule " + } + }, + "10": { + "name": "Défi : idole surprise", + "boss": "Idole mythique", + "drop": { + "note": "Article sur l'idole mythique " + }, + "spawn": "Byblos de Lunule" + }, + "11": { + "name": "Défi : être aux pièces", + "boss": "Tortue à pièces", + "drop": { + "note": "Article sur la tortue à pièces " + } + }, + "12": { + "name": "Cordon de sécurité", + "boss": "Urne de commande" + }, + "13": { + "name": "Défi : comme un lion nouveau", + "boss": "Lion réparé" + }, + "14": { + "name": "Défi : Cloître à perpétuité", + "boss": "Démon du Cloître", + "drop": { + "note": "Article sur le démon du Cloître" + }, + "spawn": "Tache d'encre de Lunule" + }, + "15": { + "name": "Défi : l'encéphalophage", + "boss": "Ensorceleur", + "spawn": "Moine de Lunule" + } + }, + "surveyPoint": { + "7": "#7: Forteresse perdue", + "9": "#9: Versant des Visions", + "12": "#12: Quiètes collines", + "13": "#13: Cité dédaléenne", + "15": "#15: Cité des Interdits", + "18": "#18: Vieilleracine", + "19": "#19: L'Ascension archaïque", + "21": "#21: Immeubles d'habitation élevés", + "22": "#22: Atelier à golems", + "23": "#23: Refuge du Vagabond", + "24": "#24: Grand brasier céleste" + }, + "demiatma": { + "orpiment": "Demi-âtma ambre", + "caputMortuum": "Demi-âtma améthyste", + "realgar": "Demi-âtma corail", + "malachite": "Demi-âtma émeraude", + "azurite": "Demi-âtma saphir", + "verdigris": "Demi-âtma turquoise" + } + } + }, + "layer": { + "traders": "Échangeur", + "repair": "Réparations" + } + } +} \ No newline at end of file diff --git a/src/map/FullscreenMapComponent.jsx b/src/map/FullscreenMapComponent.jsx index 68e8e02..519c2e4 100644 --- a/src/map/FullscreenMapComponent.jsx +++ b/src/map/FullscreenMapComponent.jsx @@ -13,6 +13,7 @@ import { useMapEvents, } from 'react-leaflet'; import L from 'leaflet'; +import { useTranslation } from 'react-i18next'; import TooltipBaseComponent from './TooltipBaseComponent'; import PolygonDrawingComponent from './PolygonDrawingComponent'; import PolygonCoordinatesDisplay from './PolygonCoordinatesDisplay'; @@ -105,6 +106,8 @@ export default function FullscreenMapComponent({ const annotations = []; const markerRef = useRef(null); + const { t } = useTranslation('map'); + const shouldDisplayPolygonDrawingComponent = false; // State for polygon drawing @@ -163,7 +166,7 @@ export default function FullscreenMapComponent({ /> - {displayLabels ? marker.name : ''} + {displayLabels && marker.name ? t(`map.regions.${marker.name}`) : ''} { mapData[markerType].circle && ( diff --git a/src/map/MapContainerComponent.jsx b/src/map/MapContainerComponent.jsx index c67a687..fc289ef 100644 --- a/src/map/MapContainerComponent.jsx +++ b/src/map/MapContainerComponent.jsx @@ -15,6 +15,7 @@ import AbcIcon from '@mui/icons-material/Abc'; import InfoIcon from '@mui/icons-material/Info'; import CancelIcon from '@mui/icons-material/Cancel'; import './MapContainerComponent.css'; +import { useTranslation } from 'react-i18next'; import FullscreenMapComponent from './FullscreenMapComponent'; import MapLayerSelectorComponent from './MapLayerSelectorComponent'; @@ -29,6 +30,7 @@ import FooterComponent from '../FooterComponent'; export default function MapContainerComponent({ mapId, inputSelectedLayers }) { const theme = useTheme(); + const { t } = useTranslation('map'); // TODO: Have a mapping so we can use multiple identifiers for each zone const mapData = {}; @@ -151,7 +153,7 @@ export default function MapContainerComponent({ mapId, inputSelectedLayers }) { - { mapData[selectedMapId].name } + { t(`${mapData[selectedMapId].name}.full`, { ns: 'zones' }) }