diff options
author | Daniel Silverstone <dsilvers@digital-scurf.org> | 2016-08-20 15:03:37 +0100 |
---|---|---|
committer | Daniel Silverstone <dsilvers@digital-scurf.org> | 2016-08-20 15:03:37 +0100 |
commit | d3776c0db410fd625794c20e2b2303546deac387 (patch) | |
tree | 7115844bf2026e1a0772f80263e4e974df9a8dc0 | |
parent | 6634c309286656a51a5ae5cb5a36c4e9a99ab4a4 (diff) | |
download | tongue-d3776c0db410fd625794c20e2b2303546deac387.tar.bz2 |
Basic language codex support
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | lib/tongue.lua | 2 | ||||
-rw-r--r-- | lib/tongue/codex.lua | 121 | ||||
-rw-r--r-- | test/test-tongue.codex.lua | 101 | ||||
-rw-r--r-- | test/test-tongue.lua | 4 |
5 files changed, 229 insertions, 1 deletions
@@ -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) |