# How to Create a Multi-Language Site This guide shows you how to set up and manage a multi-language website with FolderWeb. ## Overview FolderWeb supports multiple languages through: - Language prefixes in URLs - Language-specific content files - Translated slugs and metadata - Translation files for UI strings ## Configuration ### Step 1: Configure Available Languages Create or edit `custom/config.ini`: ```ini [languages] default = "en" available = "en,no,fr" ``` - **default**: The primary language (no URL prefix) - **available**: Comma-separated list of all supported languages ### Step 2: Create Translation Files Create translation files for each language in `custom/languages/`: ```bash mkdir -p custom/languages ``` **English** (`custom/languages/en.ini`): ```ini home = "Home" read_more = "Read more" categories = "Categories" tags = "Tags" footer_text = "Made with FolderWeb" footer_handcoded = "Generated in" footer_page_time = "ms" ``` **Norwegian** (`custom/languages/no.ini`): ```ini home = "Hjem" read_more = "Les mer" categories = "Kategorier" tags = "Stikkord" footer_text = "Laget med FolderWeb" footer_handcoded = "Generert på" footer_page_time = "ms" ``` **French** (`custom/languages/fr.ini`): ```ini home = "Accueil" read_more = "Lire la suite" categories = "Catégories" tags = "Étiquettes" footer_text = "Créé avec FolderWeb" footer_handcoded = "Généré en" footer_page_time = "ms" ``` ## URL Structure With the configuration above: - **English** (default): `yoursite.com/about/` - **Norwegian**: `yoursite.com/no/about/` - **French**: `yoursite.com/fr/about/` The default language never has a URL prefix. ## Creating Language-Specific Content ### Method 1: Separate Files Per Language Use language suffixes in filenames: `filename.{lang}.ext` **Example structure**: ``` content/about/ ├── index.md # Default language (English) ├── index.no.md # Norwegian version └── index.fr.md # French version ``` **Rules**: - Language-specific files (`.lang.ext`) show only for that language - Default files (no language suffix) show only if no language variant exists - Files are automatically filtered based on current language ### Example Content **content/about/index.md** (English): ```markdown # About Us We are a company dedicated to simplicity. ``` **content/about/index.no.md** (Norwegian): ```markdown # Om Oss Vi er et selskap dedikert til enkelhet. ``` **content/about/index.fr.md** (French): ```markdown # À Propos Nous sommes une entreprise dédiée à la simplicité. ``` Now when users visit: - `/about/` → Shows English (index.md) - `/no/about/` → Shows Norwegian (index.no.md) - `/fr/about/` → Shows French (index.fr.md) ### Method 2: Language-Specific Folders For blog posts and articles, you can create separate folders: ``` content/blog/ ├── 2025-11-01-english-post/ │ └── index.md ├── 2025-11-01-norsk-innlegg/ │ └── index.no.md └── 2025-11-01-article-francais/ └── index.fr.md ``` ## Translated Slugs and Titles Use `metadata.ini` to provide translated slugs and metadata: **content/about/metadata.ini**: ```ini ; Default (English) title = "About Us" slug = "about" [no] title = "Om Oss" slug = "om-oss" [fr] title = "À Propos" slug = "a-propos" ``` Now URLs become: - English: `/about/` - Norwegian: `/no/om-oss/` - French: `/fr/a-propos/` The actual folder is still named `about/`, but FolderWeb maps the translated slug to the real folder. ## Blog Posts with Translations **Structure**: ``` content/blog/ └── 2025-11-02-my-post/ ├── index.md ├── index.no.md ├── index.fr.md ├── cover.jpg └── metadata.ini ``` **metadata.ini**: ```ini ; Default language title = "My First Post" summary = "An introduction to multilingual blogging." date = "2025-11-02" [no] title = "Mitt Første Innlegg" summary = "En introduksjon til flerspråklig blogging." [fr] title = "Mon Premier Article" summary = "Une introduction au blogging multilingue." ``` **Important**: Date is global, cover image is shared across languages. ## Navigation and Menus Navigation is automatically built with translations. In `metadata.ini` for each top-level directory: **content/blog/metadata.ini**: ```ini menu = true menu_order = 1 title = "Blog" [no] title = "Blogg" [fr] title = "Blog" ``` **content/about/metadata.ini**: ```ini menu = true menu_order = 2 title = "About" [no] title = "Om" [fr] title = "À Propos" ``` Navigation automatically includes language prefix in URLs. ## Using Translations in Templates ### In Default Templates Translations are automatically available as `$translations` array: ```php = $translations['home'] ?>
= $translations['footer_text'] ?>
``` ### In Custom Templates Access translations the same way: ```php ``` ### Access Current Language ```php currentLang === 'no'): ?>Dette er norsk innhold.
This is English content.
``` ## Language Switcher Create a language switcher in your custom base template: ```php ``` Style it: ```css .language-switcher { display: flex; gap: 0.5rem; } .language-switcher a { padding: 0.25rem 0.75rem; border-radius: var(--border-radius); text-decoration: none; } .language-switcher a.active { background: var(--color-primary); color: white; } ``` ## List Views with Multiple Languages When displaying blog listings, FolderWeb automatically filters items by language: ``` content/blog/ ├── 2025-11-01-english-article/ │ └── index.md # Shows in English ├── 2025-11-02-norsk-artikkel/ │ └── index.no.md # Shows only in Norwegian └── 2025-11-03-universal/ ├── index.md # Shows in English ├── index.no.md # Shows in Norwegian └── index.fr.md # Shows in French ``` When viewing `/blog/`: - Shows "english-article" and "universal" When viewing `/no/blog/`: - Shows "norsk-artikkel" and "universal" When viewing `/fr/blog/`: - Shows only "universal" ## Handling Missing Translations ### Default Fallback If a translation is missing, FolderWeb uses the default language automatically. ### Show Different Content You can use PHP in your content files: ```php currentLang === 'en'): ?> # Welcome This page is only in English. # Under Construction This page is not yet translated. ``` ## SEO Considerations ### Add hreflang Tags In your custom base template: ```php availableLangs as $lang): ?> defaultLang ? 'https://yoursite.com/' . trim($ctx->requestPath, '/') : 'https://yoursite.com/' . $lang . '/' . trim($ctx->requestPath, '/'); ?> ``` ### Language-Specific Metadata Add language attributes: ```php ``` ## Testing Your Multi-Language Site 1. **Visit default language**: `http://localhost:8000/about/` 2. **Visit Norwegian**: `http://localhost:8000/no/about/` 3. **Visit French**: `http://localhost:8000/fr/about/` 4. **Check navigation**: Ensure links include language prefix 5. **Test translation strings**: Verify UI text changes per language 6. **Check blog listings**: Confirm language-specific posts appear correctly ## Common Patterns ### Blog in Multiple Languages Structure: ``` content/blog/ ├── metadata.ini # List template config └── [date]-[slug]/ ├── index.{lang}.md # One file per language ├── cover.jpg # Shared assets └── metadata.ini # Translated metadata ``` ### Documentation in Multiple Languages Structure: ``` content/docs/ ├── metadata.ini # Template config ├── 00-intro.md # Default language ├── 00-intro.no.md # Norwegian ├── 01-setup.md ├── 01-setup.no.md └── ... ``` ### Mixed Content Strategy Not everything needs translation. You can have: - English-only blog posts (no language suffix) - Multi-language main pages (with language suffixes) - Shared images and assets ## Related - [Metadata Reference](../reference/metadata.md) - [Configuration Reference](../reference/configuration.md) - [Template Variables Reference](../reference/templates.md)