async fn export_path(&self, path: &str, template_path: &str) -> Result<(), ServerError> {
        // We assume we've already built the app, which would have populated this
        let html_shell = self.html_shell.as_ref().unwrap();

        let path_prefix = get_path_prefix_server();
        // We need the encoded path to reference flattened build artifacts
        // But we don't create a flattened system with exporting, everything is properly
        // created in a directory structure
        let path_encoded = urlencoding::encode(path).to_string();
        // All initial load pages should be written into their own folders, which
        // prevents a situation of a template root page outside the directory for the
        // rest of that template's pages (see #73). The `.html` file extension is
        // added when this variable is used (for contrast to the `.json`s)
        let initial_load_path = if path.ends_with("index") {
            // However, if it's already an index page, we don't want `index/index.html`
            path.to_string()
        } else {
            format!("{}/index", &path)
        };

        // Get the template itself
        let template = self.entities.get(template_path);
        let template = match template {
            Some(template) => template,
            None => {
                return Err(ServeError::PageNotFound {
                    path: template_path.to_string(),
                }
                .into())
            }
        };

        // Create a locale detection file for it if we're using i18n
        // These just send the app shell, which will perform a redirect as necessary
        // Notably, these also include fallback redirectors if either Wasm or JS is
        // disabled (or both)
        if self.locales.using_i18n && !template.is_capsule {
            self.immutable_store
                .write(
                    &format!("exported/{}.html", &initial_load_path),
                    &html_shell
                        .clone()
                        .locale_redirection_fallback(
                            &format!("{}/{}/{}", path_prefix, self.locales.default, &path),
                            &self.global_state,
                        )
                        .to_string(),
                )
                .await?;
        }
        // Check if that template uses build state (in which case it should have a JSON
        // file)
        let has_state = template.uses_build_state();
        if self.locales.using_i18n {
            // Loop through all the app's locales
            for locale in self.locales.get_all() {
                let page_data = self
                    .get_static_page_data(
                        &format!("{}-{}", locale, &path_encoded),
                        has_state,
                        template.is_capsule,
                    )
                    .await?;

                // Don't create initial load pages for widgets
                if !template.is_capsule {
                    // Create a full HTML file from those that can be served for initial loads
                    // The build process writes these with a dummy default locale even though we're
                    // not using i18n
                    let full_html = html_shell
                        .clone()
                        .page_data(&page_data, &self.global_state, locale)
                        .to_string();
                    self.immutable_store
                        .write(
                            &format!("exported/{}/{}.html", locale, initial_load_path),
                            &full_html,
                        )
                        .await?;
                }

                // Serialize the page data to JSON and write it as a partial (fetched by the app
                // shell for subsequent loads)
                let partial_page_data = PageDataPartial {
                    state: page_data.state,
                    head: page_data.head,
                };
                let partial = serde_json::to_string(&partial_page_data).unwrap();
                self.immutable_store
                    .write(
                        &format!("exported/.perseus/page/{}/{}.json", locale, &path),
                        &partial,
                    )
                    .await?;
            }
        } else {
            let page_data = self
                .get_static_page_data(
                    &format!("{}-{}", self.locales.default, &path_encoded),
                    has_state,
                    template.is_capsule,
                )
                .await?;

            // Don't create initial load pages for widgets
            if !template.is_capsule {
                // Create a full HTML file from those that can be served for initial loads
                // The build process writes these with a dummy default locale even though we're
                // not using i18n
                let full_html = html_shell
                    .clone()
                    .page_data(&page_data, &self.global_state, "xx-XX")
                    .to_string();
                // We don't add an extension because this will be queried directly by the
                // browser
                self.immutable_store
                    .write(&format!("exported/{}.html", initial_load_path), &full_html)
                    .await?;
            }

            // Serialize the page data to JSON and write it as a partial (fetched by the app
            // shell for subsequent loads)
            let partial_page_data = PageDataPartial {
                state: page_data.state,
                head: page_data.head,
            };
            let partial = serde_json::to_string(&partial_page_data).unwrap();
            self.immutable_store
                .write(
                    &format!(
                        "exported/.perseus/page/{}/{}.json",
                        self.locales.default, &path
                    ),
                    &partial,
                )
                .await?;
        }

        Ok(())
    }
This exports for all locales, or for none if the app doesn't use i18n.
packages/perseus/src/turbine/export.rs