Version 1.1.0

willneedit [03-26-17 - 18:31]
Version 1.1.0

 * Added translations
 * Added retrieval of long info
 * Allow for character individual titles
 * Removed unneeded files
Filename
Bindings.xml
Modules/LongTxt.lua
Modules/Titles.lua
RPFlagger.lua
RPFlagger.txt
TODO
UI/ProfileEdit.lua
UI/RuleEdit.lua
UI/Settings.lua
lang/de.lua
lang/en.lua
diff --git a/Bindings.xml b/Bindings.xml
new file mode 100644
index 0000000..5bfc3de
--- /dev/null
+++ b/Bindings.xml
@@ -0,0 +1,9 @@
+<Bindings>
+	<Layer name="SI_KEYBINDINGS_LAYER_GENERAL">
+		<Category name="RPFlagger">
+		  <Action name="DISPLAY_LONGTXT">
+			<Down>RPFlagger:DisplayLongTxt()</Down>
+		  </Action>
+		</Category>
+	</Layer>
+</Bindings>
diff --git a/Modules/LongTxt.lua b/Modules/LongTxt.lua
new file mode 100644
index 0000000..ad124d2
--- /dev/null
+++ b/Modules/LongTxt.lua
@@ -0,0 +1,37 @@
+local DEBUG =
+function() end
+-- d
+
+local function _tr(str)
+	return str
+end
+
+local RPF = RPFlagger
+
+function RPF:GetLongText(characterName, note)
+	if not note or note == "" then return nil end
+	local start, stop, text = RPF:GetSingleTag(note, "TXT_" .. characterName)
+	if text then return text end
+
+	start, stop, text = RPF:GetSingleTag(note, "TXT")
+	return text
+end
+
+function RPF:DisplayLongTxt()
+	local dn = GetUnitDisplayName("reticleover")
+	local cn = GetUnitName("reticleover")
+
+	if not cn or cn == "" then return true end
+
+	local longtxt = self:GetCharacterRPInfo(dn, cn,
+		function(cn, note) return RPF:GetLongText(cn, note) end )
+
+	if not longtxt then
+		longtxt = zo_strformat(GetString(SI_RPF_LONGTXT_NONE), cn)
+	else
+		longtxt = zo_strformat(_tr("|c88ffffOn <<1>> you notice something...|r\n|c88ffff<<2>>|r"), cn, longtxt)
+	end
+
+	CHAT_SYSTEM:AddMessage(longtxt)
+	return true
+end
\ No newline at end of file
diff --git a/Modules/Titles.lua b/Modules/Titles.lua
index 182967e..4e6bac8 100644
--- a/Modules/Titles.lua
+++ b/Modules/Titles.lua
@@ -1,6 +1,6 @@
 local DEBUG =
--- function() end
-d
+function() end
+-- d

 local function _tr(str)
 	return str
@@ -8,6 +8,22 @@ end

 local RPF = RPFlagger

+function RPF:GetTitleText(characterName, note)
+	if not note or note == "" then return nil end
+	local start, stop, text = RPF:GetSingleTag(note, "RP_" .. characterName)
+	if text then return text end
+
+	start, stop, text = RPF:GetSingleTag(note, "RP")
+	return text
+end
+
+function RPF:GetReplacementTitle(accountName, characterName)
+	return self:GetCharacterRPInfo(accountName, characterName,
+		function(cn, note) return RPF:GetTitleText(cn, note) end )
+end
+
+
+-- Hook into the original (or already patched) GetTitle and GetUnitTitle functions
 local GetUnitTitle_original = GetUnitTitle
 local GetTitle_original = GetTitle

@@ -29,31 +45,15 @@ end

 local function GetUnitTitle_patched(unitTag)
 	local to, dn, cn = GetUnitInfo(unitTag)
-	local tr = RPF:GetReplacementTitle(dn)
+	local tr = RPF:GetReplacementTitle(dn, cn)
 	return tr or to
 end

 local function GetTitle_patched(index)
 	local to, dn, cn = GetPlayerInfo(index)
-	local tr = RPF:GetReplacementTitle(dn)
+	local tr = RPF:GetReplacementTitle(dn, cn)
 	return tr or to
 end

-function RPF:GetReplacementTitle(displayName)
-	local rn = ""
-	for i = 1, #self.guildList, 1 do
-		local guildId = self.guildList[i]
-		local memberIndex = GetGuildMemberIndexFromDisplayName(guildId, displayName)
-		if memberIndex then
-			local _, note = GetGuildMemberInfo(guildId, memberIndex)
-			local start, stop = string.find(note, "##RP: .* ##")
-			if start and stop then
-				return string.sub(note, start+6, stop-3)
-			end
-		end
-	end
-	return nil
-end
-
 GetTitle = GetTitle_patched
 GetUnitTitle = GetUnitTitle_patched
diff --git a/RPFlagger.lua b/RPFlagger.lua
index ee50605..5501764 100644
--- a/RPFlagger.lua
+++ b/RPFlagger.lua
@@ -13,6 +13,39 @@ RPFlagger.LAM = LibStub:GetLibrary("LibAddonMenu-2.0")
 RPFlagger.name = "RPFlagger"
 RPFlagger.loadedAddons = {}

+-- We can't rely on string.find and a pattern entirely, because we need a lazy match for
+-- start and end, rather than a greedy match
+function RPFlagger:GetSingleTag(text, tagName)
+	-- '-' seems to have a special meaning
+	local tagName = string.gsub(tagName, "-", ".")
+
+	local start = string.find(text, "##" .. tagName .. ": ")
+	if not start then return nil end
+
+	local stop = string.find(text, " ##", start+1)
+	if not stop then return nil end
+
+	start = start + string.len(tagName) + 4
+
+	return start, stop, string.sub(text, start, stop)
+end
+
+function RPFlagger:GetCharacterRPInfo(accountName, characterName, fun)
+	-- Targeted an NPC
+	if not accountName or accountName == "" then return nil end
+
+	for i = 1, #self.guildList, 1 do
+		local guildId = self.guildList[i]
+		local memberIndex = GetGuildMemberIndexFromDisplayName(guildId, accountName)
+		if memberIndex then
+			local _, note = GetGuildMemberInfo(guildId, memberIndex)
+			local rpInfoTxt = fun(characterName, note)
+			if rpInfoTxt then return rpInfoTxt end
+		end
+	end
+	return nil
+end
+
 function RPFlagger:help()
 	-- CHAT_SYSTEM:AddMessage("/im listrules - list the rules currently defined")
 	-- CHAT_SYSTEM:AddMessage("/im dryrun    - show what the currently defined rules would do to your inventory")
@@ -49,12 +82,17 @@ function RPFlagger:InitializeUI()
 	local mainPanel = {
 		{
 			type = "description",
-			text = _tr("To have a flag set for others in your guild to be shown, you need to enclose the text in '##RP: ##', for example, '##RP: God amongst mortals ##'. For retrieving purposes, the list in the settings below denote the order of your guilds which are scanned for the other player's flag"),
+			text =
+			GetString(SI_RPF_EXPLANATION1)
+			.. "\n\n" ..
+			GetString(SI_RPF_EXPLANATION2)
+			.. "\n\n" ..
+			GetString(SI_RPF_EXPLANATION3),
 		},
 		{
 			type = "submenu",
-			name = _tr("Settings"),
-			tooltip = _tr("Manage general settings"),	--(optional)
+			name = GetString(SI_RPF_SETTINGS),
+			tooltip = GetString(SI_RPF_SETTINGS_TT),	--(optional)
 			controls = Settings:GetControls(),
 		},
 	}
diff --git a/RPFlagger.txt b/RPFlagger.txt
index b43232a..9165833 100644
--- a/RPFlagger.txt
+++ b/RPFlagger.txt
@@ -2,7 +2,7 @@
 ## APIVersion: 100018
 ## OptionalDependsOn: LibAddonMenu-2.0
 ## SavedVariables: RPFSavedVars
-## Version: 1.0.0
+## Version: 1.1.0
 ## Author: iwontsay
 ## Description: iwontsay's RolePlay Flagger

@@ -28,7 +28,9 @@ RPFlagger.lua

 UI/Settings.lua
 Modules/Titles.lua
+Modules/LongTxt.lua

 lang/en.lua
 lang/$(language).lua

+Bindings.xml
diff --git a/TODO b/TODO
index 388c6be..004c6e1 100644
--- a/TODO
+++ b/TODO
@@ -1 +1,2 @@
  * Translations
+ * Add Support for ##RP-Character Name: Text ##
diff --git a/UI/ProfileEdit.lua b/UI/ProfileEdit.lua
deleted file mode 100644
index 88e5c2b..0000000
--- a/UI/ProfileEdit.lua
+++ /dev/null
@@ -1,199 +0,0 @@
-local DEBUG =
-function() end
--- d
-
-local function _tr(str)
-	return str
-end
-
-local IM = InventoryManager
-local PE = IM.UI.ProfileEdit
-
-PE.profileList = { }
-PE.reverseProfileList = { }
-PE.selectedProfile = 0
-PE.selectedName = ""
-
-function PE:GetControls()
-	return {
-		{
-			type = "dropdown",
-			name = GetString("IM_PE_PROFILES"),
-			width = "half",
-			choices = {  },
-			getFunc = function() return PE:GetSelectedProfile() end,
-			setFunc = function(value) PE:SetSelectedProfile(value) end,
-			reference = "IWONTSAY_IM_CHO_PROFILES",
-		},
-		{
-			type = "button",
-			name = GetString("IM_PE_LOADPROFILE"),
-			width = "half",
-			disabled = function() return PE:GetBtnLoadDisabled() end,
-			func = function() return PE:BtnLoadClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_PE_DELETEPROFILE"),
-			width = "half",
-			disabled = function() return PE:GetBtnDeleteDisabled() end,
-			func = function() return PE:BtnDeleteClicked() end,
-		},
-		{
-			type = "description",
-			text = "",
-			width = "half",
-		},
-		{
-			type = "editbox",
-			name = GetString("IM_PE_EDITPRNAME"),
-			tooltip = GetString("IM_PM_PROFILENAME_TOOLTIP"),
-			getFunc = function() return PE:GetProfileName() end,
-			setFunc = function(text) PE:SetProfileName(text) end,
-			isMultiline = false,
-			width = "half",
-		},
-		{
-			type = "button",
-			name = GetString("IM_PE_SAVEPROFILE"),
-			width = "half",
-			disabled = function() return PE:GetBtnSaveDisabled() end,
-			func = function() return PE:BtnSaveClicked() end,
-		},
-	}
-end
-
-function PE:GetSelectedProfile()
-	DEBUG("--- ProfileList:GetSelectedProfile()")
-	return PE.selectedName
-end
-
-function PE:SetSelectedProfile(value)
-	DEBUG("--- ProfileList:SetSelectedProfile()")
-	local profiles = IM.Profiles
-	PE.selectedProfile = PE.reverseProfileList[value]
-	PE.selectedName = (PE.selectedProfile and PE.selectedProfile > 0 and value) or ""
-end
-
-function PE:GetBtnLoadDisabled()
-	return #PE.profileList < 1 or (not PE.selectedProfile or PE.selectedProfile == 0)
-end
-
-function PE:GetBtnDeleteDisabled()
-	return #PE.profileList < 1 or (not PE.selectedProfile or PE.selectedProfile < 1)
-end
-
-function PE:BtnDeleteClicked()
-	local profiles = IM.Profiles
-	table.remove(profiles, PE.selectedProfile)
-	if PE.selectedProfile > #profiles then
-		PE.selectedProfile = #profiles
-	end
-	PE:UpdateProfileList(PE.selectedProfile)
-	IM:Save()
-end
-
-function PE:BtnLoadClicked()
-	local selProfile
-
-	if PE.selectedProfile > 0 then
-		selProfile = IM.Profiles[PE.selectedProfile]
-	else
-		selProfile = IM.presetProfiles[-PE.selectedProfile]
-	end
-
-	IM.currentRuleset.rules = selProfile["rules"]
-	IM.currentRuleset = IM.currentRuleset:New()
-
-	IM.settings = { }
-	for k,v in pairs(IM.charDefaults["settings"]) do
-		IM.settings[k] = v
-	end
-	for k,v in pairs(selProfile["settings"] or { }) do
-		IM.settings[k] = v
-	end
-
-	IM.UI.RuleEdit:UpdateRuleList()
-	IM:Save()
-end
-
-function PE:GetBtnSaveDisabled()
-	return PE.selectedName == ""
-end
-
-function PE:BtnSaveClicked()
-	local profiles = IM.Profiles
-	if not PE.reverseProfileList[PE.selectedName] then
-		PE.selectedProfile = #profiles + 1
-	end
-
-	profiles[PE.selectedProfile] = {
-		["name"] = PE.selectedName,
-		["rules"] = IM.currentRuleset:New()["rules"],
-		["settings"] = IM.settings,
-	}
-
-	PE:UpdateProfileList(PE.selectedProfile)
-	IM:Save()
-end
-
-function PE:GetProfileName()
-	return PE.selectedName or ""
-end
-
-function PE:SetProfileName(text)
-	PE.selectedName = text
-end
-
-function PE:UpdateProfileList(preselection)
-	DEBUG("--- UpdateProfileList()", preselection)
-
-	PE.profileList = { }
-	PE.reverseProfileList = { }
-
-	local _preselection = nil
-	local profiles
-	profiles = IM.presetProfiles
-
-	PE.profileList[1] = GetString("IM_RM_PRESETRULES")
-
-	if #profiles then
-		for i = 1, #profiles, 1 do
-			local tgt = #PE.profileList + 1
-			PE.profileList[tgt] = profiles[i]["name"]
-			PE.reverseProfileList[profiles[i]["name"]] = -i
-		end
-	end
-
-	PE.profileList[#PE.profileList + 1] = GetString("IM_RM_CUSTOMRULES")
-
-	profiles = IM.Profiles
-	if #profiles then
-		for i = 1, #profiles, 1 do
-			local tgt = #PE.profileList + 1
-			if preselection == i then _preselection = tgt end
-			PE.profileList[tgt] = profiles[i]["name"]
-			PE.reverseProfileList[profiles[i]["name"]] = i
-		end
-	end
-
-	IWONTSAY_IM_CHO_PROFILES:UpdateChoices(PE.profileList)
-
-	if #PE.profileList > 0 then
-		local seltxt = PE.profileList[_preselection or 1]
-		IWONTSAY_IM_CHO_PROFILES:UpdateValue(false, seltxt)
-		PE:SetSelectedProfile(seltxt)
-	end
-end
-
-
--- Called whenever the panel is first created.
-function PE:PopulateUI()
-	DEBUG("--- PE:PopulateUI")
-
-	-- fired because of someone else?
-	if not IWONTSAY_IM_CHO_PROFILES then return end
-
-	PE:UpdateProfileList()
-end
-
diff --git a/UI/RuleEdit.lua b/UI/RuleEdit.lua
deleted file mode 100644
index e91e0ac..0000000
--- a/UI/RuleEdit.lua
+++ /dev/null
@@ -1,433 +0,0 @@
-local DEBUG =
-function() end
--- d
-
-local function _tr(str)
-	return str
-end
-
-local IM = InventoryManager
-local RE = IM.UI.RuleEdit
-
-
-local function getChoiceboxLists(structure, fun)
-	local _new = { }
-	local _reverse = { }
-	local _order = { }
-	for i = 1, #structure, 1 do
-		n = structure[i][1]
-		_new[n] = fun(n, i)
-		_order[i] = _new[n]
-		_reverse[_new[n]] = n
-	end
-	return {
-		["forward"] = _new,
-		["reverse"] = _reverse,
-		["order"] = _order,
-		["seltext"] = _order[1],
-		["selvalue"] = _reverse[_order[1]]
-	}
-end
-
-local function getChoiceboxListsAssoc(structure)
-	local _new = { }
-	local _reverse = { }
-	local _order = { }
-	for i = 1, #structure, 1 do
-		local entry = structure[i]
-		_new[entry[2]] = entry[1]
-		_reverse[entry[1]] = entry[2]
-		_order[#_order + 1] = entry[1]
-	end
-	return {
-		["forward"] = _new,
-		["reverse"] = _reverse,
-		["order"] = _order,
-		["seltext"] = _order[1],
-		["selvalue"] = _reverse[_order[1]]
-	}
-end
-
-local function getSpecificFilterTypes(whichFilter)
-	local found = nil
-	for i = 1, #IM.filterorder, 1 do
-		if IM.filterorder[i][1] == whichFilter then
-			found = IM.filterorder[i][2]
-			break
-		end
-	end
-
-	return getChoiceboxLists(found, function(n) return zo_strformat(GetString(n), GetString("IM_FILTER_RULE_ANY")) end)
-end
-
-local function getSpecificTraitTypes(whichFilter, whichSubFilter)
-	local rs = IM.IM_Ruleset
-	local ttlist = IM.traitsorder[whichSubFilter] or IM.traitsorder[whichFilter] or { }
-
-	local _new = { }
-	local _reverse = { }
-	local _order = { }
-	local str
-	for i = 1, #ttlist, 1 do
-		if ttlist[i] <= 0 then
-			str = GetString("IM_META_TRAIT_TYPE", -ttlist[i])
-		else
-			str = GetString("SI_ITEMTRAITTYPE", ttlist[i])
-		end
-		_new[ttlist[i]] = str
-		_reverse[str] = ttlist[i]
-		_order[#_order + 1] = str
-	end
-	return {
-		["forward"] = _new,
-		["reverse"] = _reverse,
-		["order"] = _order,
-		["seltext"] = _order[1],
-		["selvalue"] = _reverse[_order[1]]
-	}
-end
-
-function RE:GetControls()
-
-	RE.filterTypesList = getChoiceboxLists(IM.filterorder, function(n) return GetString(n) end)
-	RE.actionList = getChoiceboxLists(IM.actionorder, function(n) return GetString("IM_ACTIONTXT", n) end)
-	RE.qualityList = getChoiceboxListsAssoc(IM.qualityorder)
-
-	RE.editingRule = IM.IM_Ruleset:NewRule()
-	local rule = RE.editingRule
-	RE:UpdateFilterSpecList(rule.filterType, rule.filterSubType)
-	RE:UpdateTraitList(rule.filterType, rule.filterSubType)
-
-	return {
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_CURRENTRULES"),
-			width = "half",
-			tooltip = GetString("IM_UI_LISTRULES_HEAD"),
-			choices = { "(invalid)" },
-			getFunc = function() return RE:GetSelectedRule() end,
-			setFunc = function(value) RE:SetSelectedRule(value) end,
-			reference = "IWONTSAY_IM_CHO_RULES",
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_DELETERULE"),
-			width = "half",
-			disabled = function() return RE:GetBtnDeleteDisabled() end,
-			func = function() return RE:BtnDeleteClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_MOVERULEUP"),
-			width = "half",
-			disabled = function() return RE:GetBtnMoveUpDisabled() end,
-			func = function() return RE:BtnMoveUpClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_ADDRULEBEFORE"),
-			width = "half",
-			func = function() return RE:BtnAddBeforeClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_MOVERULEDN"),
-			width = "half",
-			disabled = function() return RE:GetBtnMoveDownDisabled() end,
-			func = function() return RE:BtnMoveDownClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_ADDRULEAFTER"),
-			width = "half",
-			func = function() return RE:BtnAddAfterClicked() end,
-		},
-		{
-			type = "button",
-			name = GetString("IM_RE_REPLACERULE"),
-			width = "half",
-			disabled = function() return RE:GetBtnDeleteDisabled() end, -- Same condition as Delete
-			func = function() return RE:BtnReplaceClicked() end,
-		},
-		{
-			type = "description",
-			text = "",
-			width = "half",
-		},
-		{
-			type = "description",
-			text = GetString("IM_RE_DESC"),
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_ACTION"),
-			width = "half",
-			choices = RE.actionList["order"],
-			getFunc = function() return RE.actionList["forward"][RE.editingRule.action] end,
-			setFunc = function(value) RE.editingRule.action = RE.actionList["reverse"][value] end,
-		},
-		{
-			type = "description",
-			text = "",
-			width = "half",
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_GENTYPE"),
-			width = "half",
-			choices = RE.filterTypesList["order"],
-			getFunc = function() return RE.filterTypesList["forward"][RE.editingRule.filterType] end,
-			setFunc = function(value) RE:SetSelectedFilterType(value) end,
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_SPECTYPE"),
-			width = "half",
-			choices = { "(invalid)" },
-			getFunc = function() return RE.filterSubTypesList["forward"][RE.editingRule.filterSubType] end,
-			setFunc = function(value) RE:SetSelectedFilterSubType(value) end,
-			reference = "IWONTSAY_IM_CHO_FILTERSPEC",
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_TRAIT"),
-			width = "half",
-			choices = { "(invalid)" },
-			getFunc = function() return RE.traitList["forward"][RE.editingRule.traitType or 0] end,
-			setFunc = function(value) RE:SetSelectedTraitType(value) end,
-			reference = "IWONTSAY_IM_CHO_TRAIT",
-		},
-		{
-			type = "checkbox",
-			name = GetString("IM_RE_PARTOFSET"),
-			width = "half",
-			disabled = function() return RE:GetIsSetCheckDisabled() end,
-			getFunc = function() return RE.editingRule.isSet end,
-			setFunc = function(value) RE.editingRule.isSet = value end,
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_MINQUAL"),
-			width = "half",
-			choices = RE.qualityList["order"],
-			getFunc = function() return RE.qualityList["forward"][RE.editingRule.minQuality] end,
-			setFunc = function(value) RE.editingRule.minQuality = RE.qualityList["reverse"][value] end,
-		},
-		{
-			type = "dropdown",
-			name = GetString("IM_RE_MAXQUAL"),
-			width = "half",
-			choices = RE.qualityList["order"],
-			getFunc = function() return RE.qualityList["forward"][RE.editingRule.maxQuality] end,
-			setFunc = function(value) RE.editingRule.maxQuality = RE.qualityList["reverse"][value] end,
-		},
-		{
-			type = "checkbox",
-			name = GetString("IM_RE_STOLEN"),
-			width = "half",
-			getFunc = function() return RE.editingRule.stolen end,
-			setFunc = function(value) RE.editingRule.stolen = value end,
-		},
-		{
-			type = "checkbox",
-			name = GetString("IM_RE_WORTHLESS"),
-			width = "half",
-			getFunc = function() return RE.editingRule.worthless end,
-			setFunc = function(value) RE.editingRule.worthless = value end,
-		},
-	}
-end
-
-function RE:Update()
-	CALLBACK_MANAGER:FireCallbacks("LAM-RefreshPanel", RE.panel)
-end
-
-function RE:BtnAddBeforeClicked()
-	DEBUG("--- OnBtnAddBefore")
-	local rs = IM.currentRuleset.rules
-	RE.selectedRule = RE.selectedRule or 1
-	table.insert(rs, RE.selectedRule, RE.editingRule)
-	RE:UpdateRuleList(RE.selectedRule)
-	IM:Save()
-end
-
-function RE:BtnAddAfterClicked()
-	DEBUG("--- OnBtnAddAfter")
-	local rs = IM.currentRuleset.rules
-	RE.selectedRule = RE.selectedRule or 0
-	RE.selectedRule = RE.selectedRule + 1
-	table.insert(rs, RE.selectedRule, RE.editingRule)
-	RE:UpdateRuleList(RE.selectedRule)
-	IM:Save()
-end
-
-function RE:BtnDeleteClicked()
-	DEBUG("--- OnBtnDelete")
-	local rs = IM.currentRuleset.rules
-	table.remove(rs, RE.selectedRule)
-	if RE.selectedRule > #rs then
-		RE.selectedRule = #rs
-	end
-	RE:UpdateRuleList(RE.selectedRule)
-	IM:Save()
-end
-
-function RE:BtnReplaceClicked()
-	DEBUG("--- OnBtnAddAfter")
-	local rs = IM.currentRuleset.rules
-	RE.selectedRule = RE.selectedRule or 1
-	rs[RE.selectedRule] = RE.editingRule
-	RE:UpdateRuleList(RE.selectedRule)
-	IM:Save()
-end
-
-local function moveRule(direction)
-	local rs = IM.currentRuleset.rules
-	local tmp = rs[RE.selectedRule]
-	rs[RE.selectedRule] = rs[RE.selectedRule+direction]
-	rs[RE.selectedRule+direction] = tmp
-	RE.selectedRule = RE.selectedRule+direction
-	RE:UpdateRuleList(RE.selectedRule)
-	IM:Save()
-end
-
-function RE:BtnMoveUpClicked()
-	DEBUG("--- OnBtnMoveUp")
-	moveRule(-1)
-end
-
-function RE:BtnMoveDownClicked()
-	DEBUG("--- OnBtnMoveDown")
-	moveRule(1)
-end
-
-function RE:GetBtnDeleteDisabled()
-	DEBUG("--- GetBtnDeleteDisabled")
-	return not RE.selectedRule
-end
-
-function RE:GetBtnMoveUpDisabled()
-	DEBUG("--- GetBtnDeleteDisabled")
-	return not RE.selectedRule or RE.selectedRule == 1
-end
-
-function RE:GetBtnMoveDownDisabled()
-	DEBUG("--- GetBtnDeleteDisabled")
-	return not RE.selectedRule or RE.selectedRule == #RE.ruleList
-end
-
-function RE:UpdateTraitList(filterType, filterSubType)
-	DEBUG("--- UpdateTraitList", filterType, filterSubType)
-	RE.traitList = getSpecificTraitTypes(filterType, filterSubType)
-
-	local traitTxt = (RE.editingRule.traitType and RE.traitList["forward"][RE.editingRule.traitType]) or RE.traitList["seltext"]
-
-	if not IWONTSAY_IM_CHO_TRAIT then return end
-
-	IWONTSAY_IM_CHO_TRAIT:UpdateChoices(RE.traitList["order"]);
-	IWONTSAY_IM_CHO_TRAIT:UpdateValue(false, traitTxt);
-end
-
-function RE:UpdateFilterSpecList(whichFilter, preselection)
-	DEBUG("--- UpdateFilterSpecList()", whichFilter, preselection)
-	RE.filterSubTypesList = getSpecificFilterTypes(whichFilter)
-	local fst = preselection or RE.filterSubTypesList["selvalue"]
-	RE.editingRule.filterSubType = fst
-	if not IWONTSAY_IM_CHO_FILTERSPEC then return end
-
-	-- Go the long way of updating, since the UI is present
-	RE.editingRule.filterSubType = nil
-
-	IWONTSAY_IM_CHO_FILTERSPEC:UpdateChoices(RE.filterSubTypesList["order"]);
-	IWONTSAY_IM_CHO_FILTERSPEC:UpdateValue(false, RE.filterSubTypesList["forward"][fst])
-end
-
-function RE:UpdateRuleList(preselection)
-	DEBUG("--- UpdateRuleList()", preselection)
-
-	local rules = IM.currentRuleset.rules
-	RE.ruleList = { }
-	RE.reverseRuleList = { }
-	if #rules then
-		for i = 1, #rules, 1 do
-			RE.ruleList[i] = zo_strformat("<<1>>: <<2>>", i, rules[i]:ToString())
-			RE.reverseRuleList[RE.ruleList[i]] = i
-		end
-	end
-
-	if #RE.ruleList > 0 then
-		IWONTSAY_IM_CHO_RULES:UpdateChoices(RE.ruleList)
-		IWONTSAY_IM_CHO_RULES:UpdateValue(false, RE.ruleList[preselection or 1])
-	else
-		DEBUG(" -- Setting (empty)...")
-		IWONTSAY_IM_CHO_RULES:UpdateChoices({ GetString("IM_RE_EMPTY") })
-		IWONTSAY_IM_CHO_RULES:UpdateValue(false, GetString("IM_RE_EMPTY"))
-	end
-end
-
-function RE:GetIsSetCheckDisabled()
-	DEBUG("--- GetIsSetCheckDisabled")
-	local disabled =
-		RE.editingRule.filterType ~= "IM_FILTER_ANY" and
-		RE.editingRule.filterType ~= "IM_FILTER_WEAPON" and
-		RE.editingRule.filterType ~= "IM_FILTER_APPAREL"
-
-	if disabled then RE.editingRule.isSet = false end
-
-	return disabled
-end
-
-function RE:GetSelectedRule()
-	DEBUG("--- GetSelectedRule")
-	return (RE.ruleList and RE.selectedRule and RE.ruleList[RE.selectedRule]) or "(empty)"
-end
-
-function RE:SetSelectedRule(whichRuleText)
-	DEBUG("--- SetSelectedRule", whichRuleText)
-	RE.selectedRule = RE.reverseRuleList[whichRuleText]
-	local rule = IM.currentRuleset.rules[RE.selectedRule]
-	RE.editingRule = (rule and rule:New()) or RE.editingRule
-	rule = RE.editingRule
-
-	RE:UpdateFilterSpecList(rule.filterType, rule.filterSubType)
-
-end
-
-function RE:SetSelectedFilterType(whichFilterText)
-	DEBUG("--- SetSelectedFilterType", whichFilterText)
-	local whichFilter = RE.filterTypesList["reverse"][whichFilterText]
-
-	if RE.editingRule.filterType ~= whichFilter then
-		RE.editingRule.filterType = whichFilter
-		RE:UpdateFilterSpecList(whichFilter)
-	end
-end
-
-function RE:SetSelectedFilterSubType(value)
-	DEBUG("--- SetSelectedFilterSubType", value)
-	local whichSubFilter = RE.filterSubTypesList["reverse"][value]
-
-	if RE.editingRule.filterSubType ~= whichSubFilter then
-		RE.editingRule.filterSubType = whichSubFilter
-		RE:UpdateTraitList(RE.editingRule.filterType, RE.editingRule.filterSubType)
-	end
-end
-
-function RE:SetSelectedTraitType(value)
-	DEBUG("--- SetSelectedTraitType", value)
-	RE.editingRule.traitType = RE.traitList["reverse"][value]
-	if RE.editingRule.traitType == 0 then
-		RE.editingRule.traitType = nil
-	end
-end
-
--- Called whenever the panel is first created.
-function RE:PopulateUI()
-	DEBUG("--- PopulateUI")
-
-	-- fired because of someone else?
-	if not IWONTSAY_IM_CHO_FILTERSPEC then return end
-
-	RE:UpdateRuleList()
-end
-
diff --git a/UI/Settings.lua b/UI/Settings.lua
index 331d856..5a2d071 100644
--- a/UI/Settings.lua
+++ b/UI/Settings.lua
@@ -19,8 +19,8 @@ function SE:GetControls()
 	return {
 		{
 			type = "dropdown",
-			name = _tr("Guild Ordering"),
-			tooltip = _tr("Determine the ordering of the guilds which are scanned for the focused target's flag"),
+			name = GetString(SI_RPF_GUILDORDER),
+			tooltip = GetString(SI_RPF_GUILDORDER_TT),
 			choices = { "(invalid)" },
 			getFunc = function() return SE:GetSelectedEntry() end,
 			setFunc = function(value) SE:SetSelectedEntry(value) end,
@@ -28,14 +28,14 @@ function SE:GetControls()
 		},
 		{
 			type = "button",
-			name = _tr("Move entry up"),
+			name = GetString(SI_RPF_MOVEUP),
 			width = "half",
 			disabled = function() return SE:GetBtnMoveUpDisabled() end,
 			func = function() return SE:BtnMoveUpClicked() end,
 		},
 		{
 			type = "button",
-			name = _tr("Move entry down"),
+			name = GetString(SI_RPF_MOVEDN),
 			width = "half",
 			disabled = function() return SE:GetBtnMoveDnDisabled() end,
 			func = function() return SE:BtnMoveDnClicked() end,
diff --git a/lang/de.lua b/lang/de.lua
new file mode 100644
index 0000000..e2c704b
--- /dev/null
+++ b/lang/de.lua
@@ -0,0 +1,22 @@
+
+-- Check Behavior of GetString
+local lang = {
+	SI_BINDING_NAME_DISPLAY_LONGTXT = "Lange Charakterinfo lesen",
+
+	SI_RPF_LONGTXT_NONE = "|cff8888An <<1>> ist nichts Besonderes.|r",
+	SI_RPF_LONGTXT_INTRO = "|c88ffffAn <<1>> ist etwas zu bemerken...|r\n|c88ffff<<2>>|r",
+	SI_RPF_EXPLANATION1 = "Um einen Flag zu setzen, den andere in Ihrer Gilde sehen werden, müssen Sie den Text in '##RP: ##' einrahmen, zum Beispiel '##RP: Gott unter Sterblichen ##'. Für das Auslesen gibt diese Liste die Reihenfolge der Gilden an, die nach den Flags überprüft werden.",
+	SI_RPF_EXPLANATION2 = "Erkannte Markierungen sind: '##RP:', '##RP_<YourCharacterName>:', '##TXT:' and '##TXT_<YourCharacterName>', wobei '<YourCharacterName>' der Name des Charakters ist, für das Sie die Markierung setzen wollen.",
+	SI_RPF_EXPLANATION3 = "Texte in 'RP' ersetzen den Titel des Charakters, Texte in 'TXT' können abgerufen werden, wenn der Spieler, der Sie betrachtet, eine Taste drückt.",
+	SI_RPF_SETTINGS = "Einstellungen",
+	SI_RPF_SETTINGS_TT = "Generelle Einstellungen bearbeiten",
+	SI_RPF_GUILDORDER = "Gildenpriorität",
+	SI_RPF_GUILDORDER_TT = "Gibt die Reihenfolge der Gilden an, die nach Flags gescannt werden.",
+	SI_RPF_MOVEUP = "Nach oben bewegen",
+	SI_RPF_MOVEDN = "Nach unten bewegen",
+}
+
+for stringId, stringValue in pairs(lang) do
+   ZO_CreateStringId(stringId, stringValue)
+   SafeAddVersion(stringId, 1)
+end
diff --git a/lang/en.lua b/lang/en.lua
index 5fa14d9..16e97a2 100644
--- a/lang/en.lua
+++ b/lang/en.lua
@@ -1,8 +1,19 @@

 -- Check Behavior of GetString
 local lang = {
-
+	SI_BINDING_NAME_DISPLAY_LONGTXT = "Get long character info",

+	SI_RPF_LONGTXT_NONE = "|cff8888You see nothing special on <<1>>.|r",
+	SI_RPF_LONGTXT_INTRO = "|c88ffffOn <<1>> you notice something...|r\n|c88ffff<<2>>|r",
+	SI_RPF_EXPLANATION1 = "To have a flag set for others in your guild to be shown, you need to enclose the text in '##RP: ##', for example, '##RP: God amongst mortals ##'. For retrieving purposes, the list in the settings below denote the order of your guilds which are scanned for the other player's flag.",
+	SI_RPF_EXPLANATION2 = "Understood tags are: '##RP:', '##RP_<YourCharacterName>:', '##TXT:' and '##TXT_<YourCharacterName>' where '<YourCharacterName>' is the name of the character you want to set the specific flag for.",
+	SI_RPF_EXPLANATION3 = "Texts in 'RP' replace the character's title, texts in 'TXT' are for retrieval when someone uses the keypress for looking at the long description.",
+	SI_RPF_SETTINGS = "Settings",
+	SI_RPF_SETTINGS_TT = "Manage general settings",
+	SI_RPF_GUILDORDER = "Guild Ordering",
+	SI_RPF_GUILDORDER_TT = "Determine the ordering of the guilds which are scanned for the focused target's flag",
+	SI_RPF_MOVEUP = "Move entry up",
+	SI_RPF_MOVEDN = "Move entry down",
 }

 for stringId, stringValue in pairs(lang) do