summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2016-08-20 15:03:37 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2016-08-20 15:03:37 +0100
commitd3776c0db410fd625794c20e2b2303546deac387 (patch)
tree7115844bf2026e1a0772f80263e4e974df9a8dc0
parent6634c309286656a51a5ae5cb5a36c4e9a99ab4a4 (diff)
downloadtongue-d3776c0db410fd625794c20e2b2303546deac387.tar.bz2
Basic language codex support
-rw-r--r--Makefile2
-rw-r--r--lib/tongue.lua2
-rw-r--r--lib/tongue/codex.lua121
-rw-r--r--test/test-tongue.codex.lua101
-rw-r--r--test/test-tongue.lua4
5 files changed, 229 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 36621b6..1c985a6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
all: test doc
-MODULES := tongue tongue.langpack tongue.util tongue.transliteration
+MODULES := tongue tongue.langpack tongue.util tongue.transliteration tongue.codex
LUA_VER := 5.1
PREFIX ?= /usr/local
diff --git a/lib/tongue.lua b/lib/tongue.lua
index 7e77107..23ed5f9 100644
--- a/lib/tongue.lua
+++ b/lib/tongue.lua
@@ -15,6 +15,7 @@ local _ABI = 1
local VERSION = "Tongue Version " .. tostring(_VERSION)
+local codex = require "tongue.codex"
local langpack = require "tongue.langpack"
local transliteration = require "tongue.transliteration"
local util = require "tongue.util"
@@ -24,6 +25,7 @@ return {
VERSION = VERSION,
_ABI = _ABI,
ABI = ABI,
+ codex = codex,
langpack = langpack,
transliteration = transliteration,
translit = transliteration,
diff --git a/lib/tongue/codex.lua b/lib/tongue/codex.lua
new file mode 100644
index 0000000..fe8cc53
--- /dev/null
+++ b/lib/tongue/codex.lua
@@ -0,0 +1,121 @@
+-- lib/tongue/codex.lua
+--
+-- Lua I18N library 'Tongue' - Language Codexes
+--
+-- Copyright 2016 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+-- For licence terms, see COPYING
+--
+
+--- Language Codexes comprise zero or more language packs which together
+-- represent a multilingual translation for a single domain.
+--
+-- Codexes fundamentally consist of a number of loaders, some logic for
+-- managing the packs, and a mechanism for creating new blank packs on demand.
+--
+-- @module tongue.codex
+
+local codex = {}
+
+--- Tongue Language Codex.
+--
+-- A Tongue Language Codex is the entry point for applications into Tongue.
+-- Codexes are given loaders which can load language packs into the codex and
+-- they automatically invoke the loaders when needed, to reduce unnecessary
+-- disc access.
+--
+-- @type codex
+
+--- Add a loader to the codex.
+--
+-- Loaders are added to a list. The first loader added to a codex will be
+-- expected to construct the language pack, later loaders will be given the
+-- language pack to add to. If the loader determines that it needs to create a
+-- language pack which depends on another language pack, then it can request
+-- the pack from the codex. No protection is given to this causing loops, that
+-- is up to the loaders.
+--
+-- @tparam function loader The loader function to add to the codex.
+-- @function add_loader
+function codex:add_loader(loader)
+ self.loaders[#self.loaders + 1] = loader
+end
+
+--- Retrieve the given language pack from the codex.
+--
+-- Note, if the language pack has not yet been loaded, or if new loaders have
+-- been added since the language pack was loaded, then loaders may be invoked
+-- to create or augment the language pack before it is returned.
+--
+-- @tparam string lang The base language of the desired pack.
+-- @tparam ?string sublang The (optional) sub-language of the desired pack.
+-- @treturn langpack The language pack from the codex for the given language.
+-- @function get_langpack
+function codex:get_langpack(lang, sublang)
+ local langentry = lang .. (sublang and ("_" .. sublang) or "")
+ -- If we lack a packentry, make it
+ if not self.packs[langentry] then
+ self.packs[langentry] = { loaders_used = 0,
+ pack = self:_newPack(lang, sublang) }
+ end
+ local packentry = self.packs[langentry]
+ -- If our packentry is insufficiently loaded, load more
+ while packentry.loaders_used < #self.loaders do
+ local loader_n = packentry.loaders_used + 1
+ local loader = self.loaders[loader_n]
+ loader(codex, packentry.pack, langentry)
+ packentry.loaders_used = loader_n
+ end
+ -- And finally return the loaded pack
+ return packentry.pack
+end
+
+--- Expand a translation using the codex.
+--
+-- Note, this can cause loaders to be invoked if the language in question has
+-- not yet been loaded.
+--
+-- @tparam string lang The base language of the desired expansion
+-- @tparam ?string sublang The (optional) sub-language of the desired expansion
+-- @tparam string token The token to expand
+-- @tparam table args The token's arguments for expansion
+-- @function expand
+function codex:expand(lang, sublang, token, args)
+ local pack = self:get_langpack(lang, sublang)
+ return pack:expand(token, args or {})
+end
+
+local codex_mt = {__index=codex}
+
+---
+-- @section tongue.codex
+
+--- Create a new language codex.
+--
+-- Codexes require only one important function to be created -- namely a
+-- function which when called with language and sub-language will create an
+-- instance of `langpack`. If you do not pass a function to do this, then
+-- Tongue's default behaviour will be to parent sub-languages to their base
+-- language, and otherwise to not parent languages. This means, for example,
+-- en/GB will be parented to en.
+--
+-- @tparam ?function newpack Function for creating a new langpack instance.
+-- @treturn codex
+-- @function create
+local function createCodex(newpack)
+ if not newpack then
+ function newpack(codex, lang, sublang)
+ local langpack = require "tongue.langpack"
+ local parent = nil
+ if sublang then
+ parent = codex:get_langpack(lang, nil)
+ end
+ return langpack.create(lang, sublang, parent)
+ end
+ end
+ return setmetatable({_newPack=newpack, packs={}, loaders={}}, codex_mt)
+end
+
+return {
+ create = createCodex,
+}
diff --git a/test/test-tongue.codex.lua b/test/test-tongue.codex.lua
new file mode 100644
index 0000000..e94958e
--- /dev/null
+++ b/test/test-tongue.codex.lua
@@ -0,0 +1,101 @@
+-- test/test-tongue.codex.lua
+--
+-- Lua I18N library 'Tongue' -- Tests for the codex module
+--
+-- Copyright 2016 Daniel Silverstone <dsilvers@digital-scurf.org>
+--
+-- For Licence terms, see COPYING
+--
+
+-- Step one, start coverage
+
+pcall(require, 'luacov')
+
+local tongue = require 'tongue'
+
+local testnames = {}
+
+local real_assert = assert
+local total_asserts = 0
+local function assert(...)
+ local retval = real_assert(...)
+ total_asserts = total_asserts + 1
+ return retval
+end
+
+local function add_test(suite, name, value)
+ rawset(suite, name, value)
+ testnames[#testnames+1] = name
+end
+
+local suite = setmetatable({}, {__newindex = add_test})
+
+function suite.createCodexBasicCodex()
+ local codex = assert(tongue.codex.create(), "Cannot create a codex")
+ local pack = assert(codex:get_langpack("en"), "Cannot get a langpack")
+ assert(pack:expand("Foo", {}), "Unable to perform an expansion")
+end
+
+function suite.createCodexVerifyParentage()
+ local codex = assert(tongue.codex.create(), "Cannot create a codex")
+ local pack1 = assert(codex:get_langpack("en"), "Cannot get a langpack")
+ local pack2 = assert(codex:get_langpack("en", "gb"), "Cannot get a langpack")
+ assert(pack2.parent == pack1, "Default pack generator not parenting properly")
+end
+
+function suite.createCodexExternalCreator()
+ local onepack = tongue.langpack.create("en")
+ local function createpack(codex, lang, sublang)
+ return onepack
+ end
+ local codex = assert(tongue.codex.create(createpack), "Cannot create a codex")
+ local pack1 = assert(codex:get_langpack("en"), "Cannot get a langpack")
+ local pack2 = assert(codex:get_langpack("en", "gb"), "Cannot get a langpack")
+ assert(pack2 == pack1, "Our pack creator not running")
+end
+
+function suite.createCodexPreAddLoader()
+ local codex = assert(tongue.codex.create(createpack), "Cannot create a codex")
+ local loaded = {}
+ local function loader(codex, langpack, langentry)
+ loaded[langentry] = true
+ langpack:add_token("FOO", "BAR")
+ end
+ codex:add_loader(loader)
+ assert(codex:expand("en", nil, "FOO") == "BAR", "Expansion failed")
+ assert(loaded['en'], "Didn't load 'en'")
+ assert(codex:expand("en", "GB", "FOO") == "BAR", "Expansion failed")
+ assert(loaded['en_GB'], "Didn't load 'en_GB'")
+end
+
+function suite.createCodexPostAddLoader()
+ local codex = assert(tongue.codex.create(createpack), "Cannot create a codex")
+ local loaded = {}
+ local function loader(codex, langpack, langentry)
+ loaded[langentry] = true
+ langpack:add_token("FOO", "BAR")
+ end
+ assert(codex:expand("en", nil, "FOO") ~= "BAR", "Expansion succeeded?")
+ assert(codex:expand("en", "GB", "FOO") ~= "BAR", "Expansion succeeded?")
+ codex:add_loader(loader)
+ assert(codex:expand("en", nil, "FOO") == "BAR", "Expansion failed")
+ assert(loaded['en'], "Didn't load 'en'")
+ assert(codex:expand("en", "GB", "FOO") == "BAR", "Expansion failed")
+ assert(loaded['en_GB'], "Didn't load 'en_GB'")
+end
+
+local count_ok = 0
+for _, testname in ipairs(testnames) do
+-- print("Run: " .. testname)
+ local ok, err = xpcall(suite[testname], debug.traceback)
+ if not ok then
+ print(err)
+ print()
+ else
+ count_ok = count_ok + 1
+ end
+end
+
+print(tostring(count_ok) .. "/" .. tostring(#testnames) .. " [" .. tostring(total_asserts) .. "] OK")
+
+os.exit(count_ok == #testnames and 0 or 1)
diff --git a/test/test-tongue.lua b/test/test-tongue.lua
index 6cfad31..07f89aa 100644
--- a/test/test-tongue.lua
+++ b/test/test-tongue.lua
@@ -57,6 +57,10 @@ function suite.util_module_present()
assert(tongue.util, "Util module not present")
end
+function suite.codex_module_present()
+ assert(tongue.codex, "Codex module not present")
+end
+
local count_ok = 0
for _, testname in ipairs(testnames) do
-- print("Run: " .. testname)