mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 10:00:33 +00:00
Implement LFUN_SPELLING_REMOVE (patch from switt)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35055 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
f5e2d78792
commit
e4f2484cb5
@ -84,6 +84,8 @@ The following new LyX functions have been introduced:
|
||||
|
||||
- LFUN_SPELLING_IGNORE ("spelling-ignore").
|
||||
|
||||
- LFUN_SPELLING_REMOVE ("spelling-remove").
|
||||
|
||||
- LFUN_PREVIEW_INSERT ("preview-insert").
|
||||
|
||||
- LFUN_FORWARD_SEARCH ("forward-search").
|
||||
|
@ -29,6 +29,9 @@ struct AppleSpellChecker::Private
|
||||
|
||||
~Private();
|
||||
|
||||
SpellChecker::Result toResult(SpellCheckResult status);
|
||||
string toString(SpellCheckResult status);
|
||||
|
||||
/// the speller
|
||||
AppleSpeller speller;
|
||||
};
|
||||
@ -58,11 +61,26 @@ AppleSpellChecker::~AppleSpellChecker()
|
||||
}
|
||||
|
||||
|
||||
SpellChecker::Result AppleSpellChecker::Private::toResult(SpellCheckResult status)
|
||||
{
|
||||
return status == SPELL_CHECK_FAILED ? UNKNOWN_WORD :
|
||||
status == SPELL_CHECK_LEARNED ? LEARNED_WORD : WORD_OK ;
|
||||
}
|
||||
|
||||
|
||||
string AppleSpellChecker::Private::toString(SpellCheckResult status)
|
||||
{
|
||||
return status == SPELL_CHECK_FAILED ? "FAILED" :
|
||||
status == SPELL_CHECK_LEARNED ? "LEARNED" : "OK";
|
||||
}
|
||||
|
||||
|
||||
SpellChecker::Result AppleSpellChecker::check(WordLangTuple const & word)
|
||||
{
|
||||
string const word_str = to_utf8(word.word());
|
||||
int const word_ok = checkAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
|
||||
return (word_ok) ? OK : UNKNOWN_WORD;
|
||||
SpellCheckResult result = checkAppleSpeller(d->speller, word_str.c_str(), word.lang()->code().c_str());
|
||||
LYXERR(Debug::GUI, "spellCheck: \"" << word.word() << "\" = " << d->toString(result)) ;
|
||||
return d->toResult(result);
|
||||
}
|
||||
|
||||
|
||||
@ -71,6 +89,16 @@ void AppleSpellChecker::insert(WordLangTuple const & word)
|
||||
{
|
||||
string const word_str = to_utf8(word.word());
|
||||
learnAppleSpeller(d->speller, word_str.c_str());
|
||||
LYXERR(Debug::GUI, "learn word: \"" << word.word() << "\"") ;
|
||||
}
|
||||
|
||||
|
||||
// remove from personal dictionary
|
||||
void AppleSpellChecker::remove(WordLangTuple const & word)
|
||||
{
|
||||
string const word_str = to_utf8(word.word());
|
||||
unlearnAppleSpeller(d->speller, word_str.c_str());
|
||||
LYXERR(Debug::GUI, "unlearn word: \"" << word.word() << "\"") ;
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
enum Result check(WordLangTuple const &);
|
||||
void suggest(WordLangTuple const &, docstring_list &);
|
||||
void insert(WordLangTuple const &);
|
||||
void remove(WordLangTuple const &);
|
||||
void accept(WordLangTuple const &);
|
||||
bool hasDictionary(Language const * lang) const;
|
||||
docstring const error();
|
||||
|
@ -266,17 +266,17 @@ SpellChecker::Result AspellChecker::check(WordLangTuple const & word)
|
||||
d->speller(word.lang()->code(), word.lang()->variety());
|
||||
|
||||
if (!m)
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
if (word.word().empty())
|
||||
// MSVC compiled Aspell doesn't like it.
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
string const word_str = to_utf8(word.word());
|
||||
int const word_ok = aspell_speller_check(m, word_str.c_str(), -1);
|
||||
LASSERT(word_ok != -1, /**/);
|
||||
|
||||
return (word_ok) ? OK : UNKNOWN_WORD;
|
||||
return (word_ok) ? WORD_OK : UNKNOWN_WORD;
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
enum Result check(WordLangTuple const &);
|
||||
void suggest(WordLangTuple const &, docstring_list &);
|
||||
void insert(WordLangTuple const &);
|
||||
void remove(WordLangTuple const &) {};
|
||||
void accept(WordLangTuple const &);
|
||||
bool hasDictionary(Language const * lang) const;
|
||||
docstring const error();
|
||||
|
@ -3995,7 +3995,8 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to,
|
||||
if (from == end)
|
||||
break;
|
||||
to = from;
|
||||
if (from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions)) {
|
||||
SpellChecker::Result res = from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions);
|
||||
if (SpellChecker::misspelled(res)) {
|
||||
word_lang = wl;
|
||||
break;
|
||||
}
|
||||
|
@ -113,12 +113,12 @@ SpellChecker::Result EnchantChecker::check(WordLangTuple const & word)
|
||||
enchant::Dict * m = d->speller(word.lang()->code());
|
||||
|
||||
if (!m)
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
string utf8word = to_utf8(word.word());
|
||||
|
||||
if (m->check(utf8word))
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
return UNKNOWN_WORD;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
enum Result check(WordLangTuple const &);
|
||||
void suggest(WordLangTuple const &, docstring_list &);
|
||||
void insert(WordLangTuple const &);
|
||||
void remove(WordLangTuple const &) {};
|
||||
void accept(WordLangTuple const &);
|
||||
bool hasDictionary(Language const * lang) const;
|
||||
docstring const error();
|
||||
|
@ -444,6 +444,7 @@ enum FuncCode
|
||||
LFUN_SPELLING_ADD, // spitz 20100118
|
||||
LFUN_SPELLING_IGNORE, // spitz 20100118
|
||||
// 345
|
||||
LFUN_SPELLING_REMOVE, // switt 20100728
|
||||
LFUN_PREVIEW_INSERT, // vfr, 20100328
|
||||
LFUN_FORWARD_SEARCH,
|
||||
LFUN_INSET_COPY_AS, // vfr, 20100419
|
||||
|
@ -207,18 +207,18 @@ HunspellChecker::~HunspellChecker()
|
||||
SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
|
||||
{
|
||||
if (d->isIgnored(wl))
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
Hunspell * h = d->speller(wl.lang()->code());
|
||||
if (!h)
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
int info;
|
||||
|
||||
string const encoding = h->get_dic_encoding();
|
||||
string const word_to_check = to_iconv_encoding(wl.word(), encoding);
|
||||
|
||||
if (h->spell(word_to_check.c_str(), &info))
|
||||
return OK;
|
||||
return WORD_OK;
|
||||
|
||||
if (info & SPELL_COMPOUND) {
|
||||
// FIXME: What to do with that?
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
enum Result check(WordLangTuple const &);
|
||||
void suggest(WordLangTuple const &, docstring_list &);
|
||||
void insert(WordLangTuple const &);
|
||||
void remove(WordLangTuple const &) {};
|
||||
void accept(WordLangTuple const &);
|
||||
bool hasDictionary(Language const * lang) const;
|
||||
docstring const error();
|
||||
|
@ -1015,6 +1015,17 @@ void LyXAction::init()
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_SPELLING_IGNORE, "spelling-ignore", ReadOnly, Edit },
|
||||
/*!
|
||||
* \var lyx::FuncCode lyx::LFUN_SPELLING_REMOVE
|
||||
* \li Action: Remove the word under the cursor from the respective
|
||||
* spell checker dictionary.
|
||||
* \li Syntax: spelling-remove [<STRING>] [<LANG>]
|
||||
* \li Params: <WORD>: word to remove
|
||||
<LANG>: language name (see file languages)
|
||||
* \li Origin: SWitt, 28 July 2010
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_SPELLING_REMOVE, "spelling-remove", ReadOnly, Edit },
|
||||
/*!
|
||||
* \var lyx::FuncCode lyx::LFUN_THESAURUS_ENTRY
|
||||
* \li Action: Look up thesaurus entries with respect to the word under the cursor.
|
||||
|
@ -3169,19 +3169,19 @@ void Paragraph::updateWords()
|
||||
}
|
||||
|
||||
|
||||
bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
|
||||
SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
|
||||
docstring_list & suggestions, bool do_suggestion) const
|
||||
{
|
||||
SpellChecker * speller = theSpellChecker();
|
||||
if (!speller)
|
||||
return false;
|
||||
return SpellChecker::WORD_OK;
|
||||
|
||||
if (!d->layout_->spellcheck || !inInset().allowSpellCheck())
|
||||
return false;
|
||||
return SpellChecker::WORD_OK;
|
||||
|
||||
locateWord(from, to, WHOLE_WORD);
|
||||
if (from == to || from >= pos_type(d->text_.size()))
|
||||
return false;
|
||||
return SpellChecker::WORD_OK;
|
||||
|
||||
docstring word = asString(from, to, AS_STR_INSETS);
|
||||
// Ignore words with digits
|
||||
@ -3200,29 +3200,19 @@ bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
|
||||
}
|
||||
wl = WordLangTuple(word, lang);
|
||||
SpellChecker::Result res = ignored ?
|
||||
SpellChecker::OK : speller->check(wl);
|
||||
#if 0
|
||||
// FIXME: the code below makes aspell abort if a word in an unknown
|
||||
// language is checked.
|
||||
// Just ignore any error that the spellchecker reports.
|
||||
// FIXME: we should through out an exception and catch it in the GUI to
|
||||
// display the error.
|
||||
if (!speller->error().empty())
|
||||
return false;
|
||||
#endif
|
||||
SpellChecker::WORD_OK : speller->check(wl);
|
||||
|
||||
bool const misspelled = res != SpellChecker::OK
|
||||
&& res != SpellChecker::IGNORED_WORD;
|
||||
bool const misspelled_ = SpellChecker::misspelled(res) ;
|
||||
|
||||
if (lyxrc.spellcheck_continuously)
|
||||
d->fontlist_.setMisspelled(from, to, misspelled);
|
||||
d->fontlist_.setMisspelled(from, to, misspelled_);
|
||||
|
||||
if (misspelled && do_suggestion)
|
||||
if (misspelled_ && do_suggestion)
|
||||
speller->suggest(wl, suggestions);
|
||||
else
|
||||
suggestions.clear();
|
||||
|
||||
return misspelled;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -3232,7 +3222,8 @@ bool Paragraph::isMisspelled(pos_type pos) const
|
||||
pos_type to = pos;
|
||||
WordLangTuple wl;
|
||||
docstring_list suggestions;
|
||||
return spellCheck(from, to, wl, suggestions, false);
|
||||
SpellChecker::Result res = spellCheck(from, to, wl, suggestions, false);
|
||||
return SpellChecker::misspelled(res) ;
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#define PARAGRAPH_H
|
||||
|
||||
#include "FontEnums.h"
|
||||
#include "SpellChecker.h"
|
||||
|
||||
#include "insets/InsetCode.h"
|
||||
|
||||
@ -422,8 +423,8 @@ public:
|
||||
|
||||
/// Spellcheck word at position \p from and fill in found misspelled word
|
||||
/// and \p suggestions if \p do_suggestion is true.
|
||||
/// \return true if pointed word is misspelled.
|
||||
bool spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
|
||||
/// \return result from spell checker, SpellChecker::UNKNOWN_WORD when misspelled.
|
||||
SpellChecker::Result spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
|
||||
docstring_list & suggestions, bool do_suggestion = true) const;
|
||||
|
||||
/// Spellcheck word at position \p pos.
|
||||
|
@ -32,19 +32,27 @@ public:
|
||||
/// the result from checking a single word
|
||||
enum Result {
|
||||
/// word is correct
|
||||
OK = 1,
|
||||
WORD_OK = 1,
|
||||
/// root of given word was found
|
||||
ROOT,
|
||||
ROOT_FOUND,
|
||||
/// word found through compound formation
|
||||
COMPOUND_WORD,
|
||||
/// word not found
|
||||
UNKNOWN_WORD,
|
||||
/// number of other ignored "word"
|
||||
IGNORED_WORD
|
||||
IGNORED_WORD,
|
||||
/// number of personal dictionary "word"
|
||||
LEARNED_WORD
|
||||
};
|
||||
|
||||
virtual ~SpellChecker() {}
|
||||
|
||||
/// does the spell check failed
|
||||
static bool misspelled(Result res) {
|
||||
return res != SpellChecker::WORD_OK
|
||||
&& res != SpellChecker::IGNORED_WORD
|
||||
&& res != SpellChecker::LEARNED_WORD; }
|
||||
|
||||
/// check the given word of the given lang code and return the result
|
||||
virtual enum Result check(WordLangTuple const &) = 0;
|
||||
|
||||
@ -54,6 +62,9 @@ public:
|
||||
/// insert the given word into the personal dictionary
|
||||
virtual void insert(WordLangTuple const &) = 0;
|
||||
|
||||
/// remove the given word from the personal dictionary
|
||||
virtual void remove(WordLangTuple const &) = 0;
|
||||
|
||||
/// accept the given word temporarily
|
||||
virtual void accept(WordLangTuple const &) = 0;
|
||||
|
||||
|
@ -2044,6 +2044,25 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
||||
break;
|
||||
}
|
||||
|
||||
case LFUN_SPELLING_REMOVE: {
|
||||
docstring word = from_utf8(cmd.getArg(0));
|
||||
Language * lang;
|
||||
if (word.empty()) {
|
||||
word = cur.selectionAsString(false);
|
||||
// FIXME
|
||||
if (word.size() > 100 || word.empty()) {
|
||||
// Get word or selection
|
||||
selectWordWhenUnderCursor(cur, WHOLE_WORD);
|
||||
word = cur.selectionAsString(false);
|
||||
}
|
||||
lang = const_cast<Language *>(cur.getFont().language());
|
||||
} else
|
||||
lang = const_cast<Language *>(languages.getLanguage(cmd.getArg(1)));
|
||||
WordLangTuple wl(word, lang);
|
||||
theSpellChecker()->remove(wl);
|
||||
break;
|
||||
}
|
||||
|
||||
case LFUN_PARAGRAPH_PARAMS_APPLY: {
|
||||
// Given data, an encoding of the ParagraphParameters
|
||||
// generated in the Paragraph dialog, this function sets
|
||||
@ -2591,6 +2610,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
|
||||
case LFUN_SPELLING_ADD:
|
||||
case LFUN_SPELLING_IGNORE:
|
||||
case LFUN_SPELLING_REMOVE:
|
||||
enable = theSpellChecker();
|
||||
break;
|
||||
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "Paragraph.h"
|
||||
#include "ParIterator.h"
|
||||
#include "Session.h"
|
||||
#include "SpellChecker.h"
|
||||
#include "TextClass.h"
|
||||
#include "TocBackend.h"
|
||||
#include "Toolbars.h"
|
||||
@ -730,15 +731,17 @@ void MenuDefinition::expandGraphicsGroups(BufferView const * bv)
|
||||
|
||||
void MenuDefinition::expandSpellingSuggestions(BufferView const * bv)
|
||||
{
|
||||
if (!bv || !lyxrc.spellcheck_continuously)
|
||||
if (!bv)
|
||||
return;
|
||||
WordLangTuple wl;
|
||||
docstring_list suggestions;
|
||||
pos_type from = bv->cursor().pos();
|
||||
pos_type to = from;
|
||||
Paragraph const & par = bv->cursor().paragraph();
|
||||
if (!par.spellCheck(from, to, wl, suggestions))
|
||||
return;
|
||||
SpellChecker::Result res = par.spellCheck(from, to, wl, suggestions);
|
||||
switch (res) {
|
||||
case SpellChecker::UNKNOWN_WORD:
|
||||
if (lyxrc.spellcheck_continuously) {
|
||||
LYXERR(Debug::GUI, "Misspelled Word! Suggested Words = ");
|
||||
size_t i = 0;
|
||||
MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions"));
|
||||
@ -762,7 +765,21 @@ void MenuDefinition::expandSpellingSuggestions(BufferView const * bv)
|
||||
FuncRequest(LFUN_SPELLING_ADD, arg)));
|
||||
add(MenuItem(MenuItem::Command, qt_("Ignore all|I"),
|
||||
FuncRequest(LFUN_SPELLING_IGNORE, arg)));
|
||||
|
||||
}
|
||||
break;
|
||||
case SpellChecker::LEARNED_WORD: {
|
||||
LYXERR(Debug::GUI, "Learned Word.");
|
||||
docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
|
||||
add(MenuItem(MenuItem::Command, qt_("Remove from personal dictionary|r"),
|
||||
FuncRequest(LFUN_SPELLING_REMOVE, arg)));
|
||||
}
|
||||
break;
|
||||
case SpellChecker::WORD_OK:
|
||||
case SpellChecker::COMPOUND_WORD:
|
||||
case SpellChecker::ROOT_FOUND:
|
||||
case SpellChecker::IGNORED_WORD:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct sortLanguageByName {
|
||||
|
@ -16,16 +16,24 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum SpellCheckResult {
|
||||
SPELL_CHECK_FAILED,
|
||||
SPELL_CHECK_OK,
|
||||
SPELL_CHECK_IGNORED,
|
||||
SPELL_CHECK_LEARNED
|
||||
} SpellCheckResult ;
|
||||
|
||||
typedef struct AppleSpellerRec * AppleSpeller ;
|
||||
|
||||
AppleSpeller newAppleSpeller(void);
|
||||
void freeAppleSpeller(AppleSpeller speller);
|
||||
|
||||
int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
|
||||
SpellCheckResult checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
|
||||
void ignoreAppleSpeller(AppleSpeller speller, const char * word);
|
||||
size_t makeSuggestionAppleSpeller(AppleSpeller speller, const char * word, const char * lang);
|
||||
const char * getSuggestionAppleSpeller(AppleSpeller speller, size_t pos);
|
||||
void learnAppleSpeller(AppleSpeller speller, const char * word);
|
||||
void unlearnAppleSpeller(AppleSpeller speller, const char * word);
|
||||
int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -68,16 +68,16 @@ static NSString * toString(const char * word)
|
||||
}
|
||||
|
||||
|
||||
int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
|
||||
SpellCheckResult checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
|
||||
{
|
||||
if (!speller->checker || !lang || !word)
|
||||
return 0;
|
||||
return SPELL_CHECK_FAILED;
|
||||
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString * word_ = toString(word);
|
||||
NSString * lang_ = toString(lang);
|
||||
|
||||
NSRange result = [speller->checker
|
||||
NSRange match = [speller->checker
|
||||
checkSpellingOfString:word_
|
||||
startingAt:0
|
||||
language:lang_
|
||||
@ -85,11 +85,17 @@ int checkAppleSpeller(AppleSpeller speller, const char * word, const char * lang
|
||||
inSpellDocumentWithTag:speller->doctag
|
||||
wordCount:NULL];
|
||||
|
||||
SpellCheckResult result = match.length == 0 ? SPELL_CHECK_OK : SPELL_CHECK_FAILED;
|
||||
if (result == SPELL_CHECK_OK && [NSSpellChecker instancesRespondToSelector:@selector(hasLearnedWord:)]) {
|
||||
if ([speller->checker hasLearnedWord:word_])
|
||||
result = SPELL_CHECK_LEARNED;
|
||||
}
|
||||
|
||||
[word_ release];
|
||||
[lang_ release];
|
||||
[pool release];
|
||||
|
||||
return (result.length ? 0 : 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -172,7 +178,7 @@ void learnAppleSpeller(AppleSpeller speller, const char * word)
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString * word_ = toString(word);
|
||||
|
||||
if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord)])
|
||||
if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord:)])
|
||||
[speller->checker learnWord:word_];
|
||||
|
||||
[word_ release];
|
||||
@ -181,13 +187,29 @@ void learnAppleSpeller(AppleSpeller speller, const char * word)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void unlearnAppleSpeller(AppleSpeller speller, const char * word)
|
||||
{
|
||||
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString * word_ = toString(word);
|
||||
|
||||
if ([NSSpellChecker instancesRespondToSelector:@selector(unlearnWord:)])
|
||||
[speller->checker unlearnWord:word_];
|
||||
|
||||
[word_ release];
|
||||
[pool release];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang)
|
||||
{
|
||||
BOOL result = NO;
|
||||
#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString * lang_ = toString(lang);
|
||||
if ([NSSpellChecker instancesRespondToSelector:@selector(availableLanguages)]) {
|
||||
if ([NSSpellChecker instancesRespondToSelector:@selector(availableLanguages:)]) {
|
||||
NSArray * languages = [speller->checker availableLanguages];
|
||||
|
||||
for (NSString *element in languages) {
|
||||
|
Loading…
Reference in New Issue
Block a user