diff --git a/README.md b/README.md index 17bca479..27557abd 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,16 @@ Vercel Postgres can be switched to another Postgres-compatible, pooling provider 1. Ensure connection string is set to "Transaction Mode" via port `6543` 2. Disable SSL by setting `DISABLE_POSTGRES_SSL = 1` +💬   I18N +- + +Partial internationalization (non-admin, user-facing text) provided for a handful of languages. If you'd like to add support for a new language, [open a PR](https://github.com/sambecker/exif-photo-blog/compare) using [`ES_EN`](https://github.com/sambecker/exif-photo-blog) for reference. + +### Supported Languages +- `ES_ES` +- `PT_BR` +- `PT_PT` + 📖  FAQ - #### How do I receive template updates? diff --git a/src/app/AppViewSwitcher.tsx b/src/app/AppViewSwitcher.tsx index 3ba9247d..cf03b5b8 100644 --- a/src/app/AppViewSwitcher.tsx +++ b/src/app/AppViewSwitcher.tsx @@ -9,6 +9,7 @@ import { import IconSearch from '../components/icons/IconSearch'; import { useAppState } from '@/state/AppState'; import { + APP_TEXT, GRID_HOMEPAGE_ENABLED, SHOW_KEYBOARD_SHORTCUT_TOOLTIPS, } from './config'; @@ -66,7 +67,7 @@ export default function AppViewSwitcher({ hrefRef={refHrefFeed} active={currentSelection === 'feed'} tooltip={{...SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && { - content: 'Feed', + content: APP_TEXT.nav.feed, keyCommand: KEY_COMMANDS.feed, }}} noPadding @@ -79,7 +80,7 @@ export default function AppViewSwitcher({ hrefRef={refHrefGrid} active={currentSelection === 'grid'} tooltip={{...SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && { - content: 'Grid', + content: APP_TEXT.nav.grid, keyCommand: KEY_COMMANDS.grid, }}} noPadding @@ -103,7 +104,7 @@ export default function AppViewSwitcher({ noPadding tooltip={{ ...!isAdminMenuOpen && SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && { - content: 'Admin Menu', + content: APP_TEXT.nav.admin, keyCommand: KEY_COMMANDS.admin, }, }} @@ -116,7 +117,7 @@ export default function AppViewSwitcher({ />} tooltip={{ ...!isAdminMenuOpen && SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && { - content: 'Admin Menu', + content: APP_TEXT.nav.admin, keyCommand: KEY_COMMANDS.admin, }, }} @@ -128,7 +129,7 @@ export default function AppViewSwitcher({ icon={} onClick={() => setIsCommandKOpen?.(true)} tooltip={{...SHOW_KEYBOARD_SHORTCUT_TOOLTIPS && { - content: 'Search', + content: APP_TEXT.nav.search, keyCommandModifier: KEY_COMMANDS.search[0], keyCommand: KEY_COMMANDS.search[1], }}} diff --git a/src/app/config.ts b/src/app/config.ts index fcda18b7..eef94bf4 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -5,6 +5,7 @@ import { import { getOrderedCategoriesFromString } from '@/category'; import type { StorageType } from '@/platforms/storage'; import { makeUrlAbsolute, shortenUrl } from '@/utility/url'; +import { getContentForLanguage } from '@/i18n'; // HARD-CODED GLOBAL CONFIGURATION @@ -98,6 +99,10 @@ const SITE_DOMAIN_SHORT = shortenUrl(SITE_DOMAIN); // SITE META +export const APP_TEXT = await getContentForLanguage( + process.env.NEXT_PUBLIC_LANGUAGE, +); + export const NAV_TITLE = process.env.NEXT_PUBLIC_NAV_TITLE; diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 00000000..1d1b65e4 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,17 @@ +import US_EN from './languages/us-en'; + +export type I18N = typeof US_EN; + +export const LANGUAGES: Record< + string, + (() => Promise>) | undefined +> = { + 'pt-br': () => import('./languages/pt-br').then(module => module.default), +}; + +export const getContentForLanguage = async ( + language = '', +): Promise => ({ + ...US_EN, + ...await LANGUAGES[language.toLocaleLowerCase()]?.(), +}); diff --git a/src/i18n/languages/pt-br.ts b/src/i18n/languages/pt-br.ts new file mode 100644 index 00000000..b0473f29 --- /dev/null +++ b/src/i18n/languages/pt-br.ts @@ -0,0 +1,21 @@ +import { I18N } from '..'; + +const language: Partial = { + core: { + photo: 'Foto', + photoPlural: 'Fotos', + }, + nav: { + home: 'Início', + feed: 'Feed', + grid: 'Grade', + admin: 'Menu de Admin', + search: 'Pesquisar', + prev: 'Anterior', + prevShort: 'Ant', + next: 'Próximo', + nextShort: 'Prox', + }, +}; + +export default language; diff --git a/src/i18n/languages/us-en.ts b/src/i18n/languages/us-en.ts new file mode 100644 index 00000000..86c99252 --- /dev/null +++ b/src/i18n/languages/us-en.ts @@ -0,0 +1,52 @@ +const language = { + core: { + photo: 'Photo', + photoPlural: 'Photos', + }, + nav: { + home: 'Home', + feed: 'Feed', + grid: 'Grid', + admin: 'Admin', + search: 'Search', + prev: 'Previous', + prevShort: 'Prev', + next: 'Next', + nextShort: 'Next', + }, + categories: { + camera: 'Camera', + cameraPlural: 'Cameras', + lens: 'Lens', + lensPlural: 'Lenses', + tag: 'Tag', + tagPlural: 'Tags', + recipe: 'Recipe', + recipePlural: 'Recipes', + film: 'Film', + filmPlural: 'Films', + focalLength: 'Focal Length', + focalLengthPlural: 'Focal Lengths', + }, + footer: { + repo: 'Made with', + system: 'System', + light: 'Light', + dark: 'Dark', + }, + auth: { + signIn: 'Sign in', + signOut: 'Sign out', + email: 'Admin Email', + password: 'Admin Password', + }, + tooltips: { + '35mm': '35mm Equivalent', + imageViewer: 'Open Image Viewer', + sharePhoto: 'Share Photo', + recipeInfo: 'Recipe Info', + recipeCopy: 'Copy Recipe Text', + }, +}; + +export default language;