AkurAI Build
Menu

Public repository / master

AkurAI-Framework / crates/i18n/tests/load_dir.rs

Latest change 4310a8373c9b2e01b559c8a42bd21b36b0fb950e - prefer 'is' as the default locale when present in i18n catalogs by Ólafur Búi Ólafsson

//! Integration tests that exercise `Catalogs::load_dir` against real files.
//! Uses `CARGO_TARGET_TMPDIR` (set by Cargo for integration test binaries).

use akurai_i18n::Catalogs;
use std::path::{Path, PathBuf};

fn tmp_dir(name: &str) -> PathBuf {
    // `env!` reads the compile-time env var set by Cargo when compiling
    // integration tests — this is the idiomatic way to get CARGO_TARGET_TMPDIR.
    let dir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(name);
    std::fs::create_dir_all(&dir).unwrap();
    dir
}

fn write(dir: &Path, filename: &str, content: &str) {
    std::fs::write(dir.join(filename), content).unwrap();
}

// ── basic load ────────────────────────────────────────────────────────────────

#[test]
fn loads_en_and_is_catalogs() {
    let dir = tmp_dir("load_basic");
    write(
        &dir,
        "en.json",
        r#"{"hello":"Hello","greeting":"Hello, {name}!"}"#,
    );
    write(
        &dir,
        "is.json",
        r#"{"hello":"Halló","greeting":"Halló, {name}!"}"#,
    );

    let cats = Catalogs::load_dir(&dir).unwrap();

    assert_eq!(cats.translate("en", "hello"), "Hello");
    assert_eq!(cats.translate("is", "hello"), "Halló");
    assert_eq!(cats.default_locale(), "is");
}

// ── translate hit ─────────────────────────────────────────────────────────────

#[test]
fn translate_hit_in_requested_locale() {
    let dir = tmp_dir("translate_hit");
    write(&dir, "en.json", r#"{"key":"English"}"#);
    write(&dir, "is.json", r#"{"key":"Íslenska"}"#);

    let cats = Catalogs::load_dir(&dir).unwrap();
    assert_eq!(cats.translate("is", "key"), "Íslenska");
}

// ── translate miss → fallback to default locale ───────────────────────────────

#[test]
fn translate_miss_falls_back_to_default_locale() {
    let dir = tmp_dir("translate_fallback_locale");
    write(&dir, "is.json", r#"{"only_in_is":"Icelandic only"}"#);
    write(&dir, "en.json", r#"{}"#); // key absent

    let cats = Catalogs::load_dir(&dir).unwrap();
    assert_eq!(cats.translate("en", "only_in_is"), "Icelandic only");
}

// ── translate miss → fallback to key ─────────────────────────────────────────

#[test]
fn translate_miss_falls_back_to_key() {
    let dir = tmp_dir("translate_fallback_key");
    write(&dir, "en.json", r#"{}"#);

    let cats = Catalogs::load_dir(&dir).unwrap();
    assert_eq!(cats.translate("en", "app.title"), "app.title");
    assert_eq!(cats.translate("fr", "app.title"), "app.title");
}

// ── placeholder interpolation ─────────────────────────────────────────────────

#[test]
fn translate_args_interpolates_placeholder() {
    let dir = tmp_dir("translate_args");
    write(
        &dir,
        "en.json",
        r#"{"greeting":"Hello, {name}! You have {count} messages."}"#,
    );

    let cats = Catalogs::load_dir(&dir).unwrap();
    let result = cats.translate_args("en", "greeting", &[("name", "Óli"), ("count", "5")]);
    assert_eq!(result, "Hello, Óli! You have 5 messages.");
}

#[test]
fn translate_args_unknown_placeholder_left_verbatim() {
    let dir = tmp_dir("translate_args_unknown");
    write(&dir, "en.json", r#"{"msg":"Hello, {name}!"}"#);

    let cats = Catalogs::load_dir(&dir).unwrap();
    // no args supplied → placeholder is preserved
    let result = cats.translate_args("en", "msg", &[]);
    assert_eq!(result, "Hello, {name}!");
}

// ── default locale selection ──────────────────────────────────────────────────

#[test]
fn default_locale_is_en_when_present() {
    let dir = tmp_dir("default_en");
    write(&dir, "en.json", r#"{"x":"X"}"#);
    write(&dir, "fr.json", r#"{"x":"X-fr"}"#);

    let cats = Catalogs::load_dir(&dir).unwrap();
    assert_eq!(cats.default_locale(), "en");
}

#[test]
fn default_locale_is_first_alphabetically_when_no_en_or_is() {
    let dir = tmp_dir("default_alpha");
    let _ = std::fs::remove_dir_all(&dir);
    std::fs::create_dir_all(&dir).unwrap();
    write(&dir, "de.json", r#"{"x":"X-de"}"#);
    write(&dir, "fr.json", r#"{"x":"X-fr"}"#);

    let cats = Catalogs::load_dir(&dir).unwrap();
    // "de" < "fr" alphabetically
    assert_eq!(cats.default_locale(), "de");
}

// ── non-json files are ignored ────────────────────────────────────────────────

#[test]
fn ignores_non_json_files() {
    let dir = tmp_dir("ignore_non_json");
    write(&dir, "en.json", r#"{"hello":"Hello"}"#);
    write(&dir, "README.txt", "not a catalog");

    let cats = Catalogs::load_dir(&dir).unwrap();
    assert_eq!(cats.translate("en", "hello"), "Hello");
}

// ── error cases ───────────────────────────────────────────────────────────────

#[test]
fn empty_dir_returns_error() {
    let dir = tmp_dir("empty_dir");
    let err = Catalogs::load_dir(&dir).unwrap_err();
    assert!(err.message.contains("no *.json"), "got: {}", err.message);
}

#[test]
fn invalid_json_returns_error() {
    let dir = tmp_dir("bad_json");
    write(&dir, "en.json", r#"not json"#);
    assert!(Catalogs::load_dir(&dir).is_err());
}

#[test]
fn non_string_value_returns_error() {
    let dir = tmp_dir("bad_value");
    write(&dir, "en.json", r#"{"key": 42}"#);
    let err = Catalogs::load_dir(&dir).unwrap_err();
    assert!(
        err.message.contains("must be a JSON string"),
        "got: {}",
        err.message
    );
}