packages feed

espial-0.0.39: test/I18nSpec.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}

module I18nSpec (spec) where

import Data.Aeson qualified as A
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KM
import Data.ByteString qualified as BS
import I18n (loadTranslations, translate')
import TestImport

-- | Dotted key paths for every string leaf in a JSON value, mirroring the
-- flattening done by I18n.loadTranslations so the discovered keys match what
-- `translate` actually looks up.
collectKeys :: Text -> A.Value -> [Text]
collectKeys prefix = \case
  A.String _ -> [prefix]
  A.Object o -> concatMap (\(k, v) -> collectKeys (joinKey prefix (Key.toText k)) v) (KM.toList o)
  _ -> []
  where
    joinKey p k = if null p then k else p <> "." <> k

spec :: Spec
spec = describe "I18n translations" $
  it "every key discovered from en/translation.json resolves to a translation for every I18nLang" $ do
    enJson <- BS.readFile "static/locales/en/translation.json"
    enValue <- case A.decodeStrict' enJson of
      Nothing -> expectationFailure "failed to parse static/locales/en/translation.json" >> pure A.Null
      Just v -> pure v
    let enKeys = collectKeys "" enValue
    enKeys `shouldSatisfy` (not . null)

    (_, translations) <- loadTranslations "static"

    let ns = I18nNs "translation"
        
        misses =
          [ (lang, key)
            | lang <- [minBound .. maxBound],
              key <- enKeys,
              let translated = translate' translations lang ns (I18nKey key)
              in maybe True null translated
          ]

    misses `shouldBe` []