1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <com/sun/star/container/XNameAccess.hpp>
25 #include <com/sun/star/i18n/DirectionProperty.hpp>
26 #include <com/sun/star/i18n/ScriptType.hpp>
27 #include <com/sun/star/uno/Sequence.hxx>
28 #include <com/sun/star/uno/Any.h>
30 #include <i18nlangtag/lang.h>
31 #include <i18nlangtag/mslangid.hxx>
32 #include <i18nlangtag/languagetag.hxx>
33 #include <i18nlangtag/languagetagicu.hxx>
35 #include <i18nutil/unicode.hxx>
37 #include <sal/log.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/settings.hxx>
40 #include <svtools/svtresid.hxx>
41 #include <svtools/langtab.hxx>
42 #include <unotools/syslocale.hxx>
43 #include <unotools/charclass.hxx>
44 #include <tools/resary.hxx>
45 #include <officecfg/VCL.hxx>
46 #include <langtab.hrc>
48 using namespace ::com::sun::star
;
52 class SvtLanguageTableImpl
55 std::vector
<std::pair
<OUString
, LanguageType
>> m_aStrings
;
56 void AddItem(const OUString
& rLanguage
, const LanguageType eType
)
58 m_aStrings
.emplace_back(rLanguage
, eType
);
63 SvtLanguageTableImpl();
65 bool HasType( const LanguageType eType
) const;
66 OUString
GetString( const LanguageType eType
) const;
67 LanguageType
GetType( std::u16string_view rStr
) const;
68 sal_uInt32
GetEntryCount() const;
69 LanguageType
GetTypeAtIndex( sal_uInt32 nIndex
) const;
70 LanguageType
GetValue(sal_uInt32 nIndex
) const
72 return (nIndex
< m_aStrings
.size()) ? m_aStrings
[nIndex
].second
: LANGUAGE_DONTKNOW
;
74 sal_uInt32
FindIndex(LanguageType nValue
) const
76 const size_t nItems
= m_aStrings
.size();
77 for (size_t i
= 0; i
< nItems
; ++i
)
79 if (m_aStrings
[i
].second
== nValue
)
82 return RESARRAY_INDEX_NOTFOUND
;
84 void AddEntry( const OUString
& rString
, const LanguageType eType
);
87 SvtLanguageTableImpl
& theLanguageTable()
89 static SvtLanguageTableImpl aTable
;
94 OUString
ApplyLreOrRleEmbedding( const OUString
&rText
)
96 const sal_Int32 nLen
= rText
.getLength();
100 constexpr sal_Unicode cLRE_Embedding
= 0x202A; // the start char of an LRE embedding
101 constexpr sal_Unicode cRLE_Embedding
= 0x202B; // the start char of an RLE embedding
102 constexpr sal_Unicode cPopDirectionalFormat
= 0x202C; // the unicode PDF (POP_DIRECTIONAL_FORMAT) char that terminates an LRE/RLE embedding
104 // check if there are already embedding characters at the strings start
105 // if so change nothing
106 const sal_Unicode cChar
= rText
[0];
107 if (cChar
== cLRE_Embedding
|| cChar
== cRLE_Embedding
)
110 // since we only call the function getCharacterDirection
111 // it does not matter which locale the CharClass is for.
112 // Thus we can readily make use of SvtSysLocale::GetCharClass()
113 // which should come at no cost...
114 SvtSysLocale aSysLocale
;
115 const CharClass
&rCharClass
= aSysLocale
.GetCharClass();
117 // we should look for the first non-neutral LTR or RTL character
118 // and use that to determine the embedding of the whole text...
119 // Thus we can avoid to check every character of the text.
121 bool bIsRtlText
= false;
122 for (sal_Int32 i
= 0; i
< nLen
&& !bFound
; ++i
)
124 i18n::DirectionProperty nDirection
= rCharClass
.getCharacterDirection( rText
, i
);
127 case i18n::DirectionProperty_LEFT_TO_RIGHT
:
128 case i18n::DirectionProperty_LEFT_TO_RIGHT_EMBEDDING
:
129 case i18n::DirectionProperty_LEFT_TO_RIGHT_OVERRIDE
:
130 case i18n::DirectionProperty_EUROPEAN_NUMBER
:
131 case i18n::DirectionProperty_ARABIC_NUMBER
: // yes! arabic numbers are written from left to right
138 case i18n::DirectionProperty_RIGHT_TO_LEFT
:
139 case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC
:
140 case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING
:
141 case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE
:
150 // nothing to be done, character is considered to be neutral we need to look further ...
155 sal_Unicode cStart
= cLRE_Embedding
; // default is to use LRE embedding characters
157 cStart
= cRLE_Embedding
; // then use RLE embedding
159 // add embedding start and end chars to the text if the direction could be determined
160 OUString
aRes( rText
);
163 aRes
= OUStringChar(cStart
) + aRes
164 + OUStringChar(cPopDirectionalFormat
);
170 static OUString
lcl_getDescription( const LanguageTag
& rTag
)
172 OUString
aStr( LanguageTagIcu::getDisplayName( rTag
, Application::GetSettings().GetUILanguageTag()));
173 if (aStr
.isEmpty() || aStr
== rTag
.getBcp47())
175 // Place in curly brackets, so all on-the-fly tags without display name
176 // are grouped together at the top of a listbox (but behind the
177 // "[None]" entry), and not sprinkled all over, which alphabetically
178 // might make sense in an English UI only anyway. Also a visual
179 // indicator that it is a programmatical name, IMHO.
180 return OUString::Concat("{") + aStr
+ "}";
184 // The ICU display name might be identical to a predefined name or even
185 // to another tag's ICU name; clarify that this is a generated name and
186 // append the language tag in curly brackets to distinguish.
187 return aStr
+ " {" + rTag
.getBcp47() + "}";
191 SvtLanguageTableImpl::SvtLanguageTableImpl()
193 for (size_t i
= 0; i
< SAL_N_ELEMENTS(STR_ARR_SVT_LANGUAGE_TABLE
); ++i
)
195 m_aStrings
.emplace_back(SvtResId(STR_ARR_SVT_LANGUAGE_TABLE
[i
].first
), STR_ARR_SVT_LANGUAGE_TABLE
[i
].second
);
198 auto xNA
= officecfg::VCL::ExtraLanguages::get();
199 const uno::Sequence
<OUString
> rElementNames
= xNA
->getElementNames();
200 for (const OUString
& rBcp47
: rElementNames
)
204 uno::Reference
<container::XNameAccess
> xNB
;
205 xNA
->getByName(rBcp47
) >>= xNB
;
206 bool bSuccess
= (xNB
->getByName(u
"Name"_ustr
) >>= aName
) &&
207 (xNB
->getByName(u
"ScriptType"_ustr
) >>= nType
);
210 LanguageTag
aLang(rBcp47
);
211 LanguageType nLangType
= aLang
.getLanguageType();
212 if (nType
<= sal_Int32(LanguageTag::ScriptType::RTL
) && nType
> sal_Int32(LanguageTag::ScriptType::UNKNOWN
))
213 aLang
.setScriptType(LanguageTag::ScriptType(nType
));
214 sal_uInt32 nPos
= FindIndex(nLangType
);
215 if (nPos
== RESARRAY_INDEX_NOTFOUND
)
216 AddEntry( (aName
.isEmpty() ? lcl_getDescription(aLang
) : aName
), nLangType
);
221 bool SvtLanguageTableImpl::HasType( const LanguageType eType
) const
223 LanguageType eLang
= MsLangId::getReplacementForObsoleteLanguage( eType
);
224 sal_uInt32 nPos
= FindIndex(eLang
);
226 return RESARRAY_INDEX_NOTFOUND
!= nPos
&& nPos
< GetEntryCount();
229 bool SvtLanguageTable::HasLanguageType( const LanguageType eType
)
231 return theLanguageTable().HasType( eType
);
234 OUString
SvtLanguageTableImpl::GetString( const LanguageType eType
) const
236 const LanguageType nLang
= MsLangId::getReplacementForObsoleteLanguage( eType
);
237 const sal_uInt32 nPos
= (eType
== LANGUAGE_PROCESS_OR_USER_DEFAULT
?
238 FindIndex(LANGUAGE_SYSTEM
) : FindIndex( nLang
));
240 if ( RESARRAY_INDEX_NOTFOUND
!= nPos
&& nPos
< GetEntryCount() )
241 return m_aStrings
[nPos
].first
;
243 // Obtain from ICU, or a geeky but usable-in-a-pinch lang-tag.
244 OUString
sLangTag( lcl_getDescription( LanguageTag(nLang
)));
245 SAL_WARN("svtools.misc", "Language: 0x"
247 << " with unknown name, so returning generated: "
250 // And add it to the table, so it is available in all subsequent language boxes.
251 const_cast<SvtLanguageTableImpl
*>(this)->AddEntry( sLangTag
, nLang
);
256 OUString
SvtLanguageTable::GetLanguageString( const LanguageType eType
)
258 return theLanguageTable().GetString( eType
);
261 LanguageType
SvtLanguageTableImpl::GetType( std::u16string_view rStr
) const
263 LanguageType eType
= LANGUAGE_DONTKNOW
;
264 sal_uInt32 nCount
= GetEntryCount();
266 for ( sal_uInt32 i
= 0; i
< nCount
; ++i
)
268 if (m_aStrings
[i
].first
== rStr
)
277 LanguageType
SvtLanguageTable::GetLanguageType( std::u16string_view rStr
)
279 return theLanguageTable().GetType( rStr
);
282 sal_uInt32
SvtLanguageTableImpl::GetEntryCount() const
284 return m_aStrings
.size();
287 sal_uInt32
SvtLanguageTable::GetLanguageEntryCount()
289 return theLanguageTable().GetEntryCount();
293 LanguageType
SvtLanguageTableImpl::GetTypeAtIndex( sal_uInt32 nIndex
) const
295 LanguageType nType
= LANGUAGE_DONTKNOW
;
296 if (nIndex
< GetEntryCount())
297 nType
= GetValue(nIndex
);
301 LanguageType
SvtLanguageTable::GetLanguageTypeAtIndex( sal_uInt32 nIndex
)
303 return theLanguageTable().GetTypeAtIndex( nIndex
);
306 void SvtLanguageTableImpl::AddEntry( const OUString
& rString
, const LanguageType eType
)
308 if (LanguageTag::isOnTheFlyID(eType
)
309 && LanguageTag::getOnTheFlyScriptType(eType
) == LanguageTag::ScriptType::UNKNOWN
)
311 // Classify the script type to distribute the entry into the proper
312 // language list later.
313 LanguageTag
aLanguageTag(eType
);
314 const sal_Int16 nScriptClass
= unicode::getScriptClassFromLanguageTag( aLanguageTag
);
315 LanguageTag::ScriptType eScriptType
;
316 switch (nScriptClass
)
319 eScriptType
= LanguageTag::ScriptType::WESTERN
;
320 assert(!"unexpected ScriptType");
322 case css::i18n::ScriptType::WEAK
:
323 case css::i18n::ScriptType::LATIN
:
324 eScriptType
= LanguageTag::ScriptType::WESTERN
;
326 case css::i18n::ScriptType::ASIAN
:
327 eScriptType
= LanguageTag::ScriptType::CJK
;
329 case css::i18n::ScriptType::COMPLEX
:
330 /* TODO: determine if it would be LanguageTag::ScriptType::RTL
331 * instead; could that be done by
332 * getScriptClassFromLanguageTag() as well by asking Unicode
334 eScriptType
= LanguageTag::ScriptType::CTL
;
337 aLanguageTag
.setScriptType( eScriptType
);
339 AddItem( rString
, eType
);
342 void SvtLanguageTable::AddLanguageTag( const LanguageTag
& rLanguageTag
)
344 theLanguageTable().AddEntry( lcl_getDescription(rLanguageTag
), rLanguageTag
.getLanguageType());
347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */